9b8e99e5dbc9a5dfa69b90f668932c7a51a0ed93
[charm.git] / src / libs / ck-libs / idxl / idxl.C
1 /**
2 IDXL--Index List communication library.
3
4 This is a low-level bare-bones communication library.
5 The basic primitive is an "Index List", a list of user
6 array entries to send and receive.  This basic communication
7 primitive is enough to represent (for example) FEM shared
8 nodes or an FEM ghost layer; the ghost cells in a stencil-based
9 CFD computation, etc.
10
11 Orion Sky Lawlor, olawlor@acm.org, 1/7/2003
12 */
13 #include "idxl.h"
14 #include "charm-api.h"
15
16 void IDXL_Abort(const char *callingRoutine,const char *msg,int m0,int m1,int m2)
17 {
18         char msg1[1024], msg2[1024];
19         sprintf(msg1,msg,m0,m1,m2);
20         sprintf(msg2,"Fatal error in IDXL routine %s:\n%s",callingRoutine,msg1);
21         CkAbort(msg2);
22 }
23
24 CDECL void pupIDXL_Chunk(pup_er cp) {
25         PUP::er &p=*(PUP::er *)cp;
26         IDXL_Chunk *c=(IDXL_Chunk *)TCHARM_Get_global(IDXL_globalID);
27         if (c==NULL) {
28                 c=new IDXL_Chunk((CkMigrateMessage *)0);
29                 TCHARM_Set_global(IDXL_globalID,c,pupIDXL_Chunk);
30         }
31         c->pup(p);
32         if (p.isDeleting()) {
33                 delete c;
34                 /// Zero out our global entry now that we're gone--
35                 ///   this prevents use-after-delete bugs from creeping in.
36                 TCHARM_Set_global(IDXL_globalID,0,pupIDXL_Chunk);
37         }
38 }
39 IDXL_Chunk *IDXL_Chunk::get(const char *callingRoutine) {
40         IDXL_Chunk *c=getNULL();
41         if(!c) IDXL_Abort(callingRoutine,"IDXL is not initialized");
42         return c;
43 }
44
45 CDECL void
46 IDXL_Init(int mpi_comm) {
47         if (!TCHARM_Get_global(IDXL_globalID)) {
48                 IDXL_Chunk *c=new IDXL_Chunk(mpi_comm);
49                 TCHARM_Set_global(IDXL_globalID,c,pupIDXL_Chunk);
50         }
51 }
52 FORTRAN_AS_C(IDXL_INIT,IDXL_Init,idxl_init,  (int *comm), (*comm))
53
54 IDXL_Chunk::IDXL_Chunk(int mpi_comm_) 
55         :mpi_comm(mpi_comm_), currentComm(0)
56 {
57         init();
58 }
59 IDXL_Chunk::IDXL_Chunk(CkMigrateMessage *m) :currentComm(0)
60 {
61         init();
62 }
63 void IDXL_Chunk::init(void) {
64         
65 }
66
67 void IDXL_Chunk::pup(PUP::er &p) {
68         p|mpi_comm;
69
70         p|layouts;
71         if (currentComm && !currentComm->isComplete()) CkAbort("Cannot migrate with ongoing IDXL communication");
72         
73         //Pack the dynamic IDXLs (static IDXLs must re-add themselves)
74         int i, nDynamic=0;
75         if (!p.isUnpacking()) //Count the number of non-NULL idxls:
76                 for (i=0;i<dynamic_idxls.size();i++) 
77                         if (dynamic_idxls[i]) nDynamic++;
78         p|nDynamic;
79         if (p.isUnpacking()) {
80                 for (int d=0;d<nDynamic;d++) //Loop over non-NULL IDXLs
81                 {
82                         p|i;
83                         dynamic_idxls[i]=new IDXL;
84                         p|*dynamic_idxls[i];
85                 }
86         } else /* packing */ {
87                 for (i=0;i<dynamic_idxls.size();i++) //Loop over non-NULL IDXLs
88                 if (dynamic_idxls[i]) {
89                         p|i;
90                         p|*dynamic_idxls[i];
91                 }
92         }
93 }
94
95 IDXL_Chunk::~IDXL_Chunk() {
96         // we do not delete static_idxls
97         for (int i=0;i<dynamic_idxls.size();i++) 
98                 if (dynamic_idxls[i]) delete dynamic_idxls[i];
99         delete currentComm;
100 }
101
102 /****** IDXL List ******/
103 /// Dynamically create a new empty IDXL:
104 IDXL_t IDXL_Chunk::addDynamic(void) 
105 { //Pick the next free dynamic index:
106         IDXL *ret=new IDXL;
107         ret->allocateDual(); //FIXME: add way to allocate single, too
108         return IDXL_DYNAMIC_IDXL_T+storeToFreeIndex(dynamic_idxls,ret);
109 }
110 /// Register this statically-allocated IDXL at this existing index
111 IDXL_t IDXL_Chunk::addStatic(IDXL *idx,IDXL_t at) {
112         if (at!=-1) 
113         { //User provided a (previously returned) index-- try that first
114                 if (at<IDXL_STATIC_IDXL_T || at>=IDXL_LAST_IDXL_T)
115                         CkAbort("Provided bad fixed address to IDXL_Chunk::add!");
116                 int lat=at-IDXL_STATIC_IDXL_T;
117                 while (static_idxls.size()<=lat) static_idxls.push_back(NULL);
118                 if (static_idxls[lat]==NULL)
119                 { /* that slot is free-- re-use the fixed address */
120                         static_idxls[lat]=idx;
121                         return at;
122                 }
123                 else /* idxls[lat]!=NULL, somebody's already there-- fall through */
124                         at=-1;
125         }
126         if (at==-1) { //Pick the next free static index
127                 return IDXL_STATIC_IDXL_T+storeToFreeIndex(static_idxls,idx);
128         }
129         return -1; //<- for whining compilers
130 }
131
132 /// Check this IDXL for validity
133 void IDXL_Chunk::check(IDXL_t at,const char *callingRoutine) const {
134         if (at<IDXL_DYNAMIC_IDXL_T || at>=IDXL_LAST_IDXL_T)
135                         IDXL_Abort(callingRoutine,"Invalid IDXL_t %d",at);
136 }
137 IDXL &IDXL_Chunk::lookup(IDXL_t at,const char *callingRoutine) {
138         IDXL *ret=0;
139         if (at>=IDXL_DYNAMIC_IDXL_T && at<IDXL_DYNAMIC_IDXL_T+dynamic_idxls.size())
140                 ret=dynamic_idxls[at-IDXL_DYNAMIC_IDXL_T];
141         else if (at>=IDXL_STATIC_IDXL_T && at<IDXL_STATIC_IDXL_T+static_idxls.size())
142                 ret=static_idxls[at-IDXL_STATIC_IDXL_T];
143         if (ret==NULL) 
144                 IDXL_Abort(callingRoutine,"Trying to look up invalid IDXL_t %d",at);
145         return *ret;
146 }
147 const IDXL &IDXL_Chunk::lookup(IDXL_t at,const char *callingRoutine) const {
148         IDXL_Chunk *dthis=(IDXL_Chunk *)this; // Call non-const version
149         return dthis->lookup(at,callingRoutine);
150 }
151 void IDXL_Chunk::destroy(IDXL_t at,const char *callingRoutine) {
152         IDXL **ret=NULL;
153         if (at>=IDXL_DYNAMIC_IDXL_T && at<IDXL_DYNAMIC_IDXL_T+dynamic_idxls.size())
154                 ret=&dynamic_idxls[at-IDXL_DYNAMIC_IDXL_T];
155         else if (at>=IDXL_STATIC_IDXL_T && at<IDXL_STATIC_IDXL_T+static_idxls.size())
156                 ret=&static_idxls[at-IDXL_STATIC_IDXL_T];
157         if (ret==NULL)
158                 IDXL_Abort(callingRoutine,"Trying to destroy invalid IDXL_t %d",at);
159         if (*ret==NULL)
160                 IDXL_Abort(callingRoutine,"Trying to destroy already deleted IDXL_t %d",at);
161         if (at<IDXL_STATIC_IDXL_T)
162                 delete *ret; /* only destroy dynamically allocated IDXLs */
163         *ret=NULL;
164 }
165
166 /****** User Datatype list ******/
167
168 /// Get the currently active layout list.
169 IDXL_Layout_List &IDXL_Layout_List::get(void) 
170 {
171         return IDXL_Chunk::get("IDXL_Layouts::get")->layouts; 
172 }
173
174 /**** Messaging logic ***/
175
176 IDXL_Comm_t IDXL_Chunk::addComm(int tag,int context)
177 {
178         if (currentComm && !currentComm->isComplete()) CkAbort("Cannot start two IDXL_Comms at once");
179         if (context==0) context=mpi_comm;
180         if (currentComm==0) 
181                 currentComm=new IDXL_Comm(tag,context);
182         else 
183                 currentComm->reset(tag,context);
184         return 27; //FIXME: there's only one outstanding comm!
185 }
186 IDXL_Comm *IDXL_Chunk::lookupComm(IDXL_Comm_t uc,const char *callingRoutine)
187 {
188         if (uc!=27) CkAbort("Invalid idxl_comm id");
189         return currentComm;
190 }
191
192 IDXL_Comm::IDXL_Comm(int tag_,int context) {
193         reset(tag_,context);
194 }
195 void IDXL_Comm::reset(int tag_,int context) {
196         tag=tag_;
197         if (context==0) comm=MPI_COMM_WORLD;
198         else comm=(MPI_Comm)context; /* silly: not all MPI's use "int" for MPI_Comm */
199         sto.resize(0);
200         nMsgs=0;
201         /* don't delete the msg array, because we want to avoid
202            reallocating its message buffers whenever possible. */
203         msgReq.resize(0);
204         msgSts.resize(0);
205         
206         isPost=false;
207         isDone=false;
208 }
209 IDXL_Comm::~IDXL_Comm() {
210         for (int i=0;i<msg.size();i++)
211                 delete msg[i];
212 }
213
214
215 // prepare to write this field to the message:
216 void IDXL_Comm::send(const IDXL_Side *idx,const IDXL_Layout *dtype,const void *src)
217 {
218         if (isPost) CkAbort("Cannot call IDXL_Comm_send after IDXL_Comm_flush!");
219         sto.push_back(sto_t(idx,dtype,(void *)src,send_t)); 
220 }
221 void IDXL_Comm::recv(const IDXL_Side *idx,const IDXL_Layout *dtype,void *dest)
222
223         if (isPost) CkAbort("Cannot call IDXL_Comm_recv after IDXL_Comm_flush!");
224         sto.push_back(sto_t(idx,dtype,dest,recv_t)); 
225 }
226 void IDXL_Comm::sum(const IDXL_Side *idx,const IDXL_Layout *dtype,void *srcdest)
227
228         if (isPost) CkAbort("Cannot call IDXL_Comm_sum after IDXL_Comm_flush!");
229         sto.push_back(sto_t(idx,dtype,srcdest,sum_t)); 
230 }
231
232 void IDXL_Comm::post(void) {
233         if (isPost) CkAbort("Cannot post the same IDXL_Comm_t more than once");
234         isPost=true;
235         
236         //Post all our sends and receives:
237         nMsgs=0;
238         for (int s=0;s<sto.size();s++) {
239                 const IDXL_Side *idx=sto[s].idx;
240                 const IDXL_Layout *dtype=sto[s].dtype;
241                 for (int ll=0;ll<idx->size();ll++) {
242                         const IDXL_List &l=idx->getLocalList(ll);
243                         
244                         // Create message struct
245                         ++nMsgs;
246                         if (nMsgs>msg.size()) {
247                                 msg.resize(nMsgs);
248                                 msg[nMsgs-1]=new msg_t;
249                         }
250                         msg_t *m=msg[nMsgs-1];
251                         m->sto=&sto[s];
252                         m->ll=ll;
253                         
254                         // Allocate storage for message data
255                         int len=l.size()*dtype->compressedBytes();
256                         m->allocate(len);
257                         
258                         // Copy data and post MPI request
259                         MPI_Request req;
260                         switch (sto[s].op) {
261                         case send_t:
262                                 sto[s].dtype->gather(l.size(),l.getVec(),sto[s].data,m->getBuf());
263                                 MPI_Isend(m->getBuf(),len,MPI_BYTE,l.getDest(),tag,comm,&req);
264                                 break;
265                         case recv_t:case sum_t:
266                                 MPI_Irecv(m->getBuf(),len,MPI_BYTE,l.getDest(),tag,comm,&req);
267                                 break;
268                         };
269                         msgReq.push_back(req);
270                 }
271         }
272 }
273
274 void IDXL_Comm::wait(void) {
275         if (!isPosted()) post();
276         CkAssert(msg.size()>=nMsgs);
277         CkAssert(msgReq.size()==nMsgs);
278         if (nMsgs == 0) { isDone=true; return; }
279         msgSts.resize(nMsgs);
280         MPI_Waitall(nMsgs,&msgReq[0],&msgSts[0]);
281         //Process all received messages:
282         for (int im=0;im<nMsgs;im++) {
283                 msg_t *m=msg[im];
284                 sto_t *s=m->sto;
285                 const IDXL_List &l=s->idx->getLocalList(m->ll);
286                 switch (s->op) {
287                 case send_t: /* nothing else to do */
288                         break;
289                 case recv_t:
290                         s->dtype->scatter(l.size(),l.getVec(),m->getBuf(),s->data);
291                         break;
292                 case sum_t:
293                         s->dtype->scatteradd(l.size(),l.getVec(),m->getBuf(),s->data);
294                         break;
295                 };
296         }
297         isDone=true;
298 }
299
300 void IDXL_Chunk::waitComm(IDXL_Comm *comm)
301 {
302         comm->wait();
303 }
304