Skip to content

Instantly share code, notes, and snippets.

@VictorEijkhout
Last active August 29, 2015 14:04
Show Gist options
  • Save VictorEijkhout/5843f819de8607588d1c to your computer and use it in GitHub Desktop.
Save VictorEijkhout/5843f819de8607588d1c to your computer and use it in GitHub Desktop.
// -*- c++ -*-
#if defined(IMP_BASE_H)
#else
#define IMP_BASE_H 1
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
#include <vector>
#include "utils.h"
#define CHK(x) if (x) { \
char errtxt[200]; int len=200; \
MPI_Error_string(x,errtxt,&len); \
printf("p=%d, line=%d, err=%d, %s\n",mytid,__LINE__,x,errtxt); \
return ;}
/****
**** Basics
****/
class imp_environment {
private:
int nargs; char **the_args;
protected:
int debug;
int P;
std::vector<double> execution_times;
double flops;
int ntasks_executed;
public:
std::vector<int> domains;
imp_environment(int,char**);
int has_argument(const char*);
int iargument(const char*,int);
int nprocs();
int nprocs_on_shared_space() { return domains.size(); };
int is_first_proc(int p) { return p==0; };
int is_last_proc(int p) { return p==P-1; };
void register_execution_time(double);
void register_flops(double);
void record_task_executed();
virtual void print_stats() = 0;
#define DEBUG_STATS 1
#define DEBUG_PROGRESS 2
#define DEBUG_VECTORS 4
#define DEBUG_MESSAGES 8
int dodebug();
virtual void print(const char *fmt,...) = 0;
virtual void dprint(const char *fmt,...) = 0;
};
/****
**** Distribution
****/
class ioperator {
protected:
int by,shift,mul,div,none,mod;
public:
const char *operate_type;
ioperator() { none = 0; by = 0; shift = 0; mul = 0; div = 0; mod = 0; };
ioperator( const char *op );
index_int operate( index_int );
index_int operate( index_int, index_int );
index_int inverse_operate( index_int );
index_int inverse_operate( index_int, index_int );
int is_none_op() { return none; };
int is_shift_op() { return shift; };
int is_right_shift_op() { return shift && (by>0); };
int is_left_shift_op() { return shift && (by<0); };
int is_modulo_op() { return shift && mod; };
int is_bump_op() { return shift && !mod; };
int is_restrict_op() { return mul; };
int is_prolongate_op() { return div; };
int amount() { return by; };
};
class shift_operator : public ioperator {
public:
shift_operator( index_int n ) : ioperator() { none = 0; shift = 1; by = n; };
};
class distribution;
class indexstruct {
/* This describes the elements on a single processor.
Currently we support these cases:
- first,last>=0, size<0 : contiguous blocks;
note: `last' is the first not included element
note: we allow zero size by first==last
- first,last<0, size>=0 : floating
- first,last,size<0, len>=0 : explicit indices; again zero size allowed
Any operations are handled by an ioperator object in the parallel_indexstruct,
so these struct objects are strictly `as is'.
*/
private:
int stride_amount;
index_int first,last,size,len,*idx;
protected:
public:
indexstruct() { first=-1; last=-2; size=-1; len=-1; idx=NULL; stride_amount=1; };
indexstruct( indexstruct& other ) : indexstruct() {
first = other.first; last = other.last; size = other.size; len = other.len;
if (other.is_indexed()) {
printf("indexed of length %ld\n",len);
idx = new index_int[len];
for (index_int i=0; i<len; i++) {
idx[i] = other.idx[i];
}
}
stride_amount = other.stride_amount; };
indexstruct(index_int f,index_int l);
indexstruct(index_int f,index_int l,int stri);
indexstruct(index_int s);
indexstruct(index_int l,index_int *i);
int is_contiguous() { return last>=first; };
int is_indexed() { return len>0; };
int is_floating() { return size>=0; }
int is_null() { return !is_contiguous() && !is_indexed() && !is_floating(); };
int equals( indexstruct *other) {
return ( is_contiguous() && other->is_contiguous() &&
first==other->first && last==other->last ) ; }
index_int first_index();
index_int last_index();
index_int local_size();
int stride();
int can_service(index_int i);
void translate_by( index_int );
void translate_to( index_int );
indexstruct *subtract( indexstruct* ); // VLE can these go?
indexstruct *subtract( ioperator* );
indexstruct *subtract( ioperator*,index_int m );
indexstruct *intersect( indexstruct* );
indexstruct *intersect_cont_cont( indexstruct* );
indexstruct *intersect_cont_idx( indexstruct* );
indexstruct *intersect_idx_idx( indexstruct* );
std::vector<indexstruct*> *wrap(index_int g,int mod);
indexstruct *operate( ioperator* );
indexstruct *operate( ioperator*,distribution* );
indexstruct *operate( ioperator*,int mn,int mx );
void truncate( distribution *d );
indexstruct *relativize( indexstruct* );
void union_with( indexstruct* );
indexstruct *struct_union( indexstruct *merge );
};
class message;
class parallel_indexstruct {
/* This describes the assignment of elements to processors,
implemented as an array of `index_struct' objects,
one for each processor.
There are two creation routines
- from global size : contiguous disjoint blocks
- from local sizes : arbitrary mappings;
we only know how many elements per processor, not their structure
*/
private :
int P; index_int Nglobal;
protected:
public: // data
int orthogonal_dimension;
ioperator *operate_type;
indexstruct **processor_structures; // VLE overload [] to get the elts of this
public: // methods
parallel_indexstruct(int p);
// parallel_indexstruct( const parallel_indexstruct& other ); // VLE attempt at copy; not working yet
void create_from_global_size(index_int gsize);
void create_from_uniform_local_size(index_int lsize);
void create_from_replicated_local_size(index_int lsize);
void create_from_local_sizes(index_int*,index_int);
void create_from_explicit_indices(index_int*,index_int**);
void create_from_function( index_int(*)(int,index_int),index_int ); // from p,i
void set_orthogonal_dimension(index_int o) { orthogonal_dimension = o; };
index_int get_orthogonal_dimension() { return orthogonal_dimension; };
index_int first_index(int);
index_int last_index(int);
index_int local_size(int); // distributed size without orthogonal dimension
index_int local_allocation( int p ); // including orthogonal dimension
index_int global_size();
int nprocs() { return P; };
int is_valid_index(index_int);
int can_service(int p,index_int i);
int find_index(index_int);
int find_index(index_int,int);
std::vector<message*> *message_for_segment
(imp_environment*,int,int,indexstruct*,indexstruct*);
void print();
};
class distribution {
private:
imp_environment *environment;
protected:
public: // data
parallel_indexstruct *structure;
public: // methods
distribution(imp_environment *env);
// distribution(distribution*,ioperator *op);
distribution( const distribution& other ); // VLE attempt at copy; not working yet
indexstruct *processor_structure(int p);
index_int first_index(int p) {return structure->first_index(p);};
index_int last_index(int p) {return structure->last_index(p); };
index_int local_size(int p) {return structure->local_size(p); };
index_int global_size() {return structure->global_size(); };
index_int local_allocation(int p) {return structure->local_allocation(p); };
index_int get_orthogonal_dimension() { return structure->get_orthogonal_dimension(); };
int eq(distribution *d);
// const char *operate_string();
// ioperator *dist_operator();
int nprocs() { return structure->nprocs(); };
int shifted_processor(int p);
int is_valid_index(index_int i);
int can_service(int p,index_int i);
int find_index(index_int i) { return structure->find_index(i); };
int find_index(index_int i,int p) { return structure->find_index(i,p); };
imp_environment *get_environment() { return environment; }
std::vector<message*> *analyze_one_dependence
(int,int,ioperator*,distribution*,indexstruct*);
std::vector<message*> *analyze_one_dependence(int mytid,distribution *beta);
};
/****
**** Object
****/
class object {
private:
protected:
index_int allocated_size;
double *data;
public:
distribution *distro;
object( distribution *d ) { distro = d; data = NULL; allocated_size = -1; };
index_int first_index(int p) {return distro->first_index(p);};
index_int last_index(int p) {return distro->last_index(p); };
index_int local_size(int p) {return distro->local_size(p); };
index_int global_size() {return distro->global_size(); };
void copy_contents_from(object *inp) {
// VLE we need to check compatible distributions
printf("VLE copy contents really needs distribution check\n");
memcpy(data,inp->get_data(),allocated_size); };
double *get_data();
index_int get_orthogonal_dimension() { return distro->get_orthogonal_dimension(); };
void set_allocated_size( index_int s ) { allocated_size = s; };
index_int get_allocated_size() { return allocated_size; };
virtual distribution *get_distribution() = 0;
int nprocs() { return distro->nprocs(); };
virtual void print() = 0;
};
/****
**** Message
****/
/*
Message description:
- sender,receiver : the processors involved, one of them is "me"
- src_index : global base index of send data on the source
- tar_index : local base address of receive data in the halo
- size : number of elements to communicate
*/
class message {
private:
protected:
imp_environment* environment;
public:
int sender,receiver;
indexstruct *src_struct,*tar_struct;
// index_int src_index,tar_index, size;
message(imp_environment*);
message(imp_environment*,int self,int other,indexstruct*,indexstruct*);
message(imp_environment*,char *buf,int len); // VLE create by MPI_Unpack of a buffer, should be in mpi_base?
index_int src_index();
index_int tar_index();
index_int size();
void set_environment(imp_environment*);
void set_tar_struct( indexstruct* );
void as_string(char *buf,int *len);
imp_environment *get_environment();
};
/****
**** Task
****/
class beta_object;
class task_queue;
class task {
protected:
int done;
task_queue *surrounding_queue;
object *halo_object;
const char *name;
public: // data
object *invector,*outvector;
int step,domain,task_id;
std::vector<message*> *recv_messages;
std::vector<message*> *send_messages;
void (*localexecutefn)
(int step,int localno,object *invector,object *outvector,void*);
void *localexecutectx;
indexstruct *halo_struct; // VLE should we turn this into a distribution?
public: // methods
task() { localexecutefn = NULL; localexecutectx = NULL;
invector = NULL; outvector = NULL;
recv_messages = NULL; send_messages = NULL;
step = -1; domain = -1; task_id = -1; done = 0; name = ""; };
task(int s,int d,object *in,object *out);
void set_name(const char *nam) { name = nam; }
const char *get_name() { return name; };
message *recv_message(int i) { return (*recv_messages)[i]; }
message *send_message(int i) { return (*send_messages)[i]; }
virtual object *get_halo_object() = 0;
virtual void analyze_dependencies(object*,beta_object*) = 0;
// begone virtual void analyze_no_dependencies() = 0;
virtual std::vector<message*>*
create_receive_structure_for_task(int,distribution*,beta_object*);
std::vector<int> *get_predecessors();
virtual void execute() = 0;
virtual void execute(object*,object*) = 0;
void local_execute(object*,object*);
void local_execute(object*,object*,void*);
task_queue *get_surrounding_queue();
void set_surrounding_queue(task_queue*);
// virtual void create_receive_structure(distribution*,beta_object*) = 0;
int n_incoming_messages() { return recv_messages->size(); };
int n_outgoing_messages() { return send_messages->size(); };
void display(imp_environment*);
};
/****
**** Kernel
****/
class beta_object {
private:
int null;
protected:
std::vector<ioperator*> *operators;
int shift_based;
distribution *gamma_distro,*beta_distro;
public:
beta_object(distribution*);
int is_null();
void add_beta_oper( ioperator *op );
int is_oper_based() { return operators!=NULL && n_opers()>0; };
void set_explicit_beta_distribution( distribution *distro );
int is_shift_based() { return shift_based>0; };
int is_restrict_prolongate_based() { return shift_based==0; };
std::vector<ioperator*> *get_operators();
distribution *get_beta_distribution();
distribution *get_gamma_distribution();
int n_opers();
};
class kernel {
private:
task_queue *surrounding_queue;
protected:
const char *name;
int has_been_analyzed;
public: // data
int kernel_number;
object *in_object,*out_object;
beta_object* beta_definition;
std::vector<task*> *tasks;
void (*localexecutefn) (int step,int p,object *invector,object *outvector,void*);
void *localexecutectx;
public: // methods
kernel( object *out ) { kernel_number = -1; name = ""; has_been_analyzed = 0;
in_object = NULL; out_object = out; localexecutectx = NULL;
beta_definition = new beta_object(out->get_distribution()); };
kernel( object *in_obj,object *out_obj);
void set_kernel_number( int n ) { kernel_number = n; };
void set_name(const char *nam) { name = nam; }
const char *get_name() { return name; };
void add_beta_oper( ioperator* );
void set_explicit_beta_distribution( distribution *dist );
// void add_beta_vector( object *obj );
// void add_beta_vector( object *obj,distribution *distr ); // VLE is the distr needed?
virtual std::vector<task*> *split_to_tasks() = 0;
virtual void execute() = 0;
void set_surrounding_queue(task_queue*);
task_queue *get_surrounding_queue();
imp_environment *get_environment();
};
/****
**** Queue
****/
class task_queue {
private:
double t_analyze,t_run,t_all_runs;
protected:
imp_environment *environment;
std::vector<kernel*> all_kernels;
public: // data
int kernel_count;
std::vector<task*> tasks;
public: // routines
task_queue(imp_environment *env) { environment = env; kernel_count = 0; };
void add_kernel(kernel *k);
void analyze_dependencies();
virtual void execute() = 0;
void execute_task( int t );
task* get_task(int);
imp_environment *get_environment();
};
#endif
// -*- c++ -*-
#if defined(MPI_BASE_H)
#else
#define MPI_BASE_H 1
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
#include <mpi.h>
#include <imp_base.h>
/****
**** Basics
****/
class mpi_environment : public imp_environment {
private:
public:
MPI_Comm comm;
int mytid();
double nmsgs,nsend;
mpi_environment(int,char**);
~mpi_environment();
int iam_first_proc() { return is_first_proc( mytid() ); };
int iam_last_proc() { return is_last_proc( mytid() ); };
void print_stats();
void print(const char *fmt,...); // proc zero only
void gprint(const char *fmt,...); // everywhere
void dprint(const char *fmt,...); // debug, proc zero only
void dgprint(const char *fmt,...); // debug, everywhere
};
/****
**** Distribution
****/
class mpi_distribution : public distribution {
public:
mpi_distribution(mpi_environment *env) : distribution((imp_environment*)env) {};
mpi_distribution( const mpi_distribution& other ) : distribution(other) {};
mpi_environment *get_environment() {
return (mpi_environment*)( (distribution*)this )->get_environment(); }
index_int my_local_size();
index_int my_local_allocation();
index_int my_first_index(); // these should not be here;
index_int my_last_index(); // they will in fact throw an exception for nondisjoint
};
class mpi_disjoint_blockdistribution : public mpi_distribution {
public:
// local global
mpi_disjoint_blockdistribution(mpi_environment *env,index_int,index_int);
// ortho local global
mpi_disjoint_blockdistribution(mpi_environment *env,index_int,index_int,index_int);
};
class mpi_nondisjoint_blockdistribution : public mpi_distribution {
public:
mpi_nondisjoint_blockdistribution(mpi_environment *env,index_int lsize,index_int gsize);
mpi_nondisjoint_blockdistribution(mpi_environment *env,
index_int ortho,index_int lsize,index_int gsize);
};
class mpi_gathered_scalar : public mpi_distribution {
public:
mpi_gathered_scalar(mpi_environment *env);
mpi_gathered_scalar(mpi_environment *env,index_int s); // ortho
mpi_gathered_scalar(mpi_environment *env,index_int s,index_int n); // ortho & local
};
class mpi_replicated_scalar : public mpi_distribution {
public:
mpi_replicated_scalar(mpi_environment *env);
mpi_replicated_scalar(mpi_environment *env,index_int s);
};
class mpi_function_distribution : public mpi_distribution {
public:
mpi_function_distribution(mpi_environment *env,index_int(*)(int,index_int),index_int);
};
/****
**** Object
****/
class mpi_object : public object {
private:
protected:
int data_is_private;
public:
// VLE WTF mpi_distribution *distro;
mpi_object( mpi_distribution *d );
mpi_object( mpi_distribution *d, double *x );
~mpi_object() {
if (data_is_private)
delete data;
};
mpi_distribution *get_distribution() { return (mpi_distribution*)distro; };
void print();
};
/****
**** Message
****/
class mpi_message : public message {
private:
public:
mpi_message(mpi_environment*,int self,int other,indexstruct*,indexstruct*);
mpi_message(mpi_environment*,char *buf,int len);
MPI_Request* request_from(int other,mpi_environment *env);
void check_involved(int,int);
char *buffer();
int buffer_length() { return 2*sizeof(int)+3*sizeof(index_int); }
// void localize(mpi_distribution*);
};
/****
**** Task
****/
class mpi_task_queue;
class mpi_task : public task {
private:
protected:
MPI_Request *kernel_requests;
public:
mpi_task(int s,int d,object *in,object *out)
: task(s,d,in,out) { halo_object = NULL; };
mpi_task(int s,int d,object *out)
: task(s,d,NULL,out) { halo_object = NULL; };
void analyze_dependencies(object *in_object,beta_object* beta_definition);
// I want these create routines to return their object, but the halo is private
// and its creation is done in a kernel routine. Hm.
std::vector<message*> *create_send_structure_for_task(int,distribution*,beta_object*);
void allocate_halo_vector(distribution*,beta_object*);
mpi_object *get_halo_object() { return (mpi_object*)halo_object; };
index_int compute_halo_size(distribution*,beta_object*);
int get_nsends(imp_environment*,std::vector<message*> *recv_messages);
void execute();
void execute(object*,object*);
/* Stuff */
void print();
};
/****
**** Kernel
****/
class mpi_kernel : public kernel {
private:
protected:
mpi_distribution *distro;
public:
mpi_kernel( object *in_obj,object *out_obj );
mpi_kernel( object *out_obj );
std::vector<task*> *split_to_tasks();
void analyze_dependencies();
void allocate_halo_vector(int);
void execute();
};
/****
**** Queue
****/
class mpi_task_queue : public task_queue {
private:
public:
mpi_task_queue(mpi_environment *env);
void execute();
};
#endif
TEST_CASE( "divide distributions", "[distribution]" ) {
}
[albook:~/Current/Ilib/i-mp/code/mpi] %% cat u.cxx
TEST_CASE( "Elementary ioperator stuff","[operate][1]" ) {
ioperator *iop;
CHECK_THROWS( iop = new ioperator("no_such_thing") );
}
TEST_CASE( "Test ioperator right workings modulo","[operate][2][modulo]" ) {
ioperator *i1 = right_shift_mod; new ioperator(">>1");
CHECK( i1->is_shift_op() );
CHECK( i1->is_modulo_op() );
CHECK( i1->amount()==1 );
CHECK( i1->operate(0)==1 );
CHECK_THROWS( i1->operate(-1) );
CHECK( i1->operate(5)==6 );
CHECK( i1->operate(5,6)==0 );
}
TEST_CASE( "Test ioperator right workings","[operate][2]" ) {
ioperator *i2 = new ioperator(">=1");
CHECK( !i2->is_modulo_op() );
CHECK( i2->operate(0)==1 );
CHECK_THROWS( i2->operate(-1) );
CHECK( i2->operate(15)==16 );
CHECK( i2->operate(15,16)==15 );
}
TEST_CASE( "Test ioperator left workings modulo","[operate][3][modulo]" ) {
ioperator *i1 = left_shift_mod; new ioperator("<<1");
CHECK( i1->is_modulo_op() );
CHECK( i1->operate(1)==0 );
CHECK_THROWS( i1->operate(-1) );
// CHECK_THROWS( i1->operate(0) );
CHECK( i1->operate(6)==5 );
CHECK( i1->operate(0,6)==5 );
}
TEST_CASE( "Test ioperator left workings bump","[operate][3]" ) {
}
/*
* Unit tests for the MPI backend of IMP
* based on the CATCH framework (https://github.com/philsquared/Catch)
*/
#include <stdlib.h>
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
void unittest_mpi_setup();
int main(int argc,char **argv) {
unittest_mpi_setup();
int result = Catch::Session().run( argc, argv );
return 0;
}
/*
* Unit tests for the MPI backend of IMP
* based on the CATCH framework (https://github.com/philsquared/Catch)
*/
#include <stdlib.h>
#include <math.h>
#include "catch.hpp"
#include "mpi_base.h"
int mytid,ntids;
MPI_Comm comm;
mpi_environment *env;
ioperator *no_op, *left_shift_mod,*right_shift_mod;
void unittest_mpi_setup() {
try {
env = new mpi_environment(0,NULL); }
catch (int x) {
printf("Could not even get started\n"); throw(1);
}
comm = MPI_COMM_WORLD;
MPI_Comm_size(comm,&ntids);
MPI_Comm_rank(comm,&mytid);
no_op = new ioperator("none");
right_shift_mod = new ioperator(">>1");
left_shift_mod = new ioperator("<<1");
return;
}
#include "u.cxx"
TEST_CASE( "divide distributions", "[distribution]" ) {
}
#ifndef UTILS_H
#define UTILS_H
#define MOD(x,y) ( ( x+y ) % y )
#ifndef MAX
#define MAX(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
#endif
#ifndef MIN
#define MIN(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#endif
typedef long long int index_int; // VLE we should test somewhere.....
#define MPI_INDEX_INT MPI_LONG_LONG_INT
void psizes_from_global(index_int *sizes,int P,index_int gsize);
int hasarg_from_argcv(const char *name,int nargs,char **the_args);
int iarg_from_argcv(const char *name,int vdef,int nargs,char **the_args);
#endif
@VictorEijkhout
Copy link
Author

compile with

mpicxx -std=c++11 -I../imp -o um unittest_main.cxx us.cxx

@philsquared
Copy link

Here's the issue:

#include "u.cxx" (in us.cxx:34)

You're #including one file with test cases into another file that also has test cases. The functions that back the test cases have names that are generated by using the line number. The symbols are local to the translation unit (because they are in an anonymous namespace). But when you #include another file the line number is local to the included file - and so no longer unique (which is in line with your suspicion).

The solution is to make the two files with test cases independent - perhaps #including another file with common code (e.g. the unittest_mpi_setup())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment