NEURON
bbsmpipack.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 
5 /* do not want the redef in the dynamic load case */
6 #include <nrnmpiuse.h>
7 
8 #if NRNMPI_DYNAMICLOAD
9 #include <nrnmpi_dynam.h>
10 #endif
11 
12 #include <nrnmpi.h>
13 
14 #if NRNMPI
15 #include <string.h>
16 #include <assert.h>
17 #include <errno.h>
18 #include <mpi.h>
19 #include <nrnmpidec.h>
20 #include <nrnmpi_impl.h>
21 #include <hocdec.h>
22 
23 #define nrn_mpi_assert(arg) nrn_assert(arg == MPI_SUCCESS)
24 
25 #define nrnmpidebugleak 0
26 #define debug 0
27 
28 extern MPI_Comm nrn_bbs_comm;
29 
30 #if nrnmpidebugleak
31 static int nrnmpi_bufcnt_;
32 #endif
33 
34 /* we want to find the key easily and I am assuming that all MPI
35  implementations allow one to start unpacking from a particular position.
36  Therefore we give space for the key position at the beginning and
37  an int always takes up the same space at position 0.
38  Now I regret not forcing the key to come first at the user level.
39 */
40 
41 #define my_MPI_INT 0
42 #define my_MPI_DOUBLE 1
43 #define my_MPI_CHAR 2
44 #define my_MPI_PACKED 3
45 #define my_MPI_PICKLE 4
46 
47 static MPI_Datatype mytypes[] = {MPI_INT, MPI_DOUBLE, MPI_CHAR, MPI_PACKED, MPI_CHAR};
48 
49 static void unpack(void* buf, int count, int my_datatype, bbsmpibuf* r, const char* errmes) {
50  int type[2];
51  assert(r && r->buf);
52 #if debug
53  printf("%d unpack upkpos=%d pkposition=%d keypos=%d size=%d\n",
55  r->upkpos,
56  r->pkposition,
57  r->keypos,
58  r->size);
59 #endif
60  assert(r->upkpos >= 0 && r->size >= r->upkpos);
61  nrn_mpi_assert(MPI_Unpack(r->buf, r->size, &r->upkpos, type, 2, MPI_INT, nrn_bbs_comm));
62 #if debug
63  printf("%d unpack r=%p size=%d upkpos=%d type[0]=%d datatype=%d type[1]=%d count=%d\n",
65  r,
66  r->size,
67  r->upkpos,
68  type[0],
69  my_datatype,
70  type[1],
71  count);
72 #endif
73  if (type[0] != my_datatype || type[1] != count) {
74  printf("%d unpack size=%d upkpos=%d type[0]=%d datatype=%d type[1]=%d count=%d\n",
76  r->size,
77  r->upkpos,
78  type[0],
79  my_datatype,
80  type[1],
81  count);
82  }
83  assert(type[0] == my_datatype);
84  assert(type[1] == count);
85  nrn_mpi_assert(
86  MPI_Unpack(r->buf, r->size, &r->upkpos, buf, count, mytypes[my_datatype], nrn_bbs_comm));
87 }
88 
89 void nrnmpi_upkbegin(bbsmpibuf* r) {
90  int type;
91  int p;
92 #if debug
93  printf("%d nrnmpi_upkbegin %p (preunpack upkpos=%d keypos=%d)\n",
95  r,
96  r->upkpos,
97  r->keypos);
98 #endif
99  assert(r && r->buf && r->size > 0);
100  if (nrnmpi_myid_bbs == -1) {
101  hoc_execerror("subworld process with nhost > 0 cannot use", "the bulletin board");
102  }
103  r->upkpos = 0;
104  nrn_mpi_assert(MPI_Unpack(r->buf, r->size, &r->upkpos, &p, 1, MPI_INT, nrn_bbs_comm));
105  if (p > r->size) {
106  printf("\n %d nrnmpi_upkbegin keypos=%d size=%d\n", nrnmpi_myid_bbs, p, r->size);
107  }
108  assert(p <= r->size);
109  nrn_mpi_assert(MPI_Unpack(r->buf, r->size, &p, &type, 1, MPI_INT, nrn_bbs_comm));
110 #if debug
111  printf("%d nrnmpi_upkbegin type=%d keypos=%d\n", nrnmpi_myid_bbs, type, p);
112 #endif
113  assert(type == 0);
114  r->keypos = p;
115 }
116 
117 char* nrnmpi_getkey(bbsmpibuf* r) {
118  char* s;
119  int type;
120  type = r->upkpos;
121  r->upkpos = r->keypos;
122 #if debug
123  printf("%d nrnmpi_getkey %p keypos=%d\n", nrnmpi_myid_bbs, r, r->keypos);
124 #endif
125  s = nrnmpi_upkstr(r);
126  assert(r->pkposition == 0 || r->pkposition == r->upkpos);
127  r->pkposition = r->upkpos;
128  r->upkpos = type;
129 #if debug
130  printf("getkey return %s\n", s);
131 #endif
132  return s;
133 }
134 
135 int nrnmpi_getid(bbsmpibuf* r) {
136  int i, type;
137  type = r->upkpos;
138  r->upkpos = r->keypos;
139 #if debug
140  printf("%d nrnmpi_getid %p keypos=%d\n", nrnmpi_myid_bbs, r, r->keypos);
141 #endif
142  i = nrnmpi_upkint(r);
143  r->upkpos = type;
144 #if debug
145  printf("getid return %d\n", i);
146 #endif
147  return i;
148 }
149 
150 int nrnmpi_upkint(bbsmpibuf* r) {
151  int i;
152  unpack(&i, 1, my_MPI_INT, r, "upkint");
153  return i;
154 }
155 
156 double nrnmpi_upkdouble(bbsmpibuf* r) {
157  double x;
158  unpack(&x, 1, my_MPI_DOUBLE, r, "upkdouble");
159  return x;
160 }
161 
162 void nrnmpi_upkvec(int n, double* x, bbsmpibuf* r) {
163  unpack(x, n, my_MPI_DOUBLE, r, "upkvec");
164 }
165 
166 #if NRNMPI_DYNAMICLOAD
167 /* for some unknown reason mpiexec -n 2 python3 test0.py gives
168 load_nrnmpi: /home/hines/neuron/nrndynam/x86_64/lib/libnrnmpi.so: undefined symbol: cxx_char_alloc
169 So fill this in explicitly in nrnmpi_dynam.cpp
170 */
171 char* (*p_cxx_char_alloc)(int len);
172 #endif
173 
174 char* nrnmpi_upkstr(bbsmpibuf* r) {
175  int len;
176  char* s;
177  unpack(&len, 1, my_MPI_INT, r, "upkstr length");
178 #if NRNMPI_DYNAMICLOAD
179  s = (*p_cxx_char_alloc)(len + 1); /* will be delete not free */
180 #else
181  s = cxx_char_alloc(len + 1); /* will be delete not free */
182 #endif
183  unpack(s, len, my_MPI_CHAR, r, "upkstr string");
184  s[len] = '\0';
185  return s;
186 }
187 
188 char* nrnmpi_upkpickle(size_t* size, bbsmpibuf* r) {
189  int len;
190  char* s;
191  unpack(&len, 1, my_MPI_INT, r, "upkpickle length");
192  *size = len;
193 #if NRNMPI_DYNAMICLOAD
194  s = (*p_cxx_char_alloc)(len + 1); /* will be delete not free */
195 #else
196  s = cxx_char_alloc(len + 1); /* will be delete, not free */
197 #endif
198  unpack(s, len, my_MPI_PICKLE, r, "upkpickle data");
199  return s;
200 }
201 
202 static void resize(bbsmpibuf* r, int size) {
203  int newsize;
204  if (r->size < size) {
205  newsize = (size / 64) * 64 + 128;
206  r->buf = static_cast<char*>(hoc_Erealloc(r->buf, newsize));
207  hoc_malchk();
208  r->size = newsize;
209  }
210 }
211 
212 void nrnmpi_pkbegin(bbsmpibuf* r) {
213  int type;
214  if (nrnmpi_myid_bbs == -1) {
215  hoc_execerror("subworld process with nhost > 0 cannot use", "the bulletin board");
216  }
217  r->pkposition = 0;
218  type = 0;
219 #if debug
220  printf(
221  "%d nrnmpi_pkbegin %p size=%d pkposition=%d\n", nrnmpi_myid_bbs, r, r->size, r->pkposition);
222 #endif
223  nrn_mpi_assert(MPI_Pack(&type, 1, MPI_INT, r->buf, r->size, &r->pkposition, nrn_bbs_comm));
224 }
225 
226 void nrnmpi_enddata(bbsmpibuf* r) {
227  int p, type, isize;
228  p = r->pkposition;
229  type = 0;
230 #if debug
231  printf("%d nrnmpi_enddata %p size=%d pkposition=%d\n", nrnmpi_myid_bbs, r, r->size, p);
232  int oldsize = r->size;
233 #endif
234  nrn_mpi_assert(MPI_Pack_size(1, MPI_INT, nrn_bbs_comm, &isize));
235  resize(r, r->pkposition + isize);
236 #if debug
237  if (oldsize < r->pkposition + isize) {
238  printf("%d %p need %d more. end up with total of %d\n", nrnmpi_myid_bbs, r, isize, r->size);
239  }
240 #endif
241  nrn_mpi_assert(MPI_Pack(&type, 1, MPI_INT, r->buf, r->size, &r->pkposition, nrn_bbs_comm));
242 #if debug
243  printf("%d nrnmpi_enddata buf=%p size=%d pkposition=%d\n",
245  r->buf,
246  r->size,
247  r->pkposition);
248 #endif
249  nrn_mpi_assert(MPI_Pack(&p, 1, MPI_INT, r->buf, r->size, &type, nrn_bbs_comm));
250 #if debug
251  printf("%d after nrnmpi_enddata, %d was packed at beginning and 0 was packed before %d\n",
253  p,
254  r->pkposition);
255 #endif
256 }
257 
258 static void pack(void* inbuf, int incount, int my_datatype, bbsmpibuf* r, const char* e) {
259  int type[2];
260  int dsize, isize;
261 #if debug
262  int oldsize = r->size;
263  printf("%d pack %p count=%d type=%d outbuf-%p pkposition=%d %s\n",
265  r,
266  incount,
267  my_datatype,
268  r->buf,
269  r->pkposition,
270  e);
271 #endif
272  nrn_mpi_assert(MPI_Pack_size(incount, mytypes[my_datatype], nrn_bbs_comm, &dsize));
273  nrn_mpi_assert(MPI_Pack_size(2, MPI_INT, nrn_bbs_comm, &isize));
274  resize(r, r->pkposition + dsize + isize);
275 #if debug
276  if (oldsize < r->pkposition + dsize + isize) {
277  printf("%d %p need %d more. end up with total of %d\n",
279  r,
280  dsize + isize,
281  r->size);
282  }
283 #endif
284  type[0] = my_datatype;
285  type[1] = incount;
286  nrn_mpi_assert(MPI_Pack(type, 2, MPI_INT, r->buf, r->size, &r->pkposition, nrn_bbs_comm));
287  nrn_mpi_assert(MPI_Pack(
288  inbuf, incount, mytypes[my_datatype], r->buf, r->size, &r->pkposition, nrn_bbs_comm));
289 #if debug
290  printf("%d pack done pkposition=%d\n", nrnmpi_myid_bbs, r->pkposition);
291 #endif
292 }
293 
294 void nrnmpi_pkint(int i, bbsmpibuf* r) {
295  int ii;
296  ii = i;
297  pack(&ii, 1, my_MPI_INT, r, "pkint");
298 }
299 
300 void nrnmpi_pkdouble(double x, bbsmpibuf* r) {
301  double xx;
302  xx = x;
303  pack(&xx, 1, my_MPI_DOUBLE, r, "pkdouble");
304 }
305 
306 void nrnmpi_pkvec(int n, double* x, bbsmpibuf* r) {
307  pack(x, n, my_MPI_DOUBLE, r, "pkvec");
308 }
309 
310 void nrnmpi_pkstr(const char* s, bbsmpibuf* r) {
311  int len;
312  len = strlen(s);
313  pack(&len, 1, my_MPI_INT, r, "pkstr length");
314  pack((char*) s, len, my_MPI_CHAR, r, "pkstr string");
315 }
316 
317 void nrnmpi_pkpickle(const char* s, size_t size, bbsmpibuf* r) {
318  int len = size;
319  pack(&len, 1, my_MPI_INT, r, "pkpickle length");
320  pack((char*) s, len, my_MPI_PICKLE, r, "pkpickle data");
321 }
322 
323 void nrnmpi_bbssend(int dest, int tag, bbsmpibuf* r) {
324 #if debug
325  printf("%d nrnmpi_bbssend %p dest=%d tag=%d size=%d\n",
327  r,
328  dest,
329  tag,
330  (r) ? r->size : 0);
331 #endif
332 
333  /* Some MPI implementations limit tags to be less than full MPI_INT domain
334  so pack tag if > 20 in second slot (now reserved) of buffer and use 20 as the tag.
335  */
336  if (tag > 20) {
337  int save_position = r->pkposition;
338  int save_upkpos = r->upkpos;
339  nrnmpi_upkbegin(r);
340  nrnmpi_upkint(r);
341  r->pkposition = r->upkpos;
342  nrnmpi_pkint(tag, r);
343  r->pkposition = save_position;
344  r->upkpos = save_upkpos;
345  tag = 20;
346  }
347 
348  if (r) {
349  assert(r->buf && r->keypos <= r->size);
350  nrn_mpi_assert(MPI_Send(r->buf, r->size, MPI_PACKED, dest, tag, nrn_bbs_comm));
351  } else {
352  nrn_mpi_assert(MPI_Send(nullptr, 0, MPI_PACKED, dest, tag, nrn_bbs_comm));
353  }
354  errno = 0;
355 #if debug
356  printf("%d return from send\n", nrnmpi_myid_bbs);
357 #endif
358 }
359 
360 int nrnmpi_bbsrecv(int source, bbsmpibuf* r) {
361  MPI_Status status;
362  int size;
363  if (source == -1) {
364  source = MPI_ANY_SOURCE;
365  }
366 #if debug
367  printf("%d nrnmpi_bbsrecv %p\n", nrnmpi_myid_bbs, r);
368 #endif
369  nrn_mpi_assert(MPI_Probe(source, MPI_ANY_TAG, nrn_bbs_comm, &status));
370  nrn_mpi_assert(MPI_Get_count(&status, MPI_PACKED, &size));
371 #if debug
372  printf("%d nrnmpi_bbsrecv probe size=%d source=%d tag=%d\n",
374  size,
375  status.MPI_SOURCE,
376  status.MPI_TAG);
377 #endif
378  resize(r, size);
379  nrn_mpi_assert(
380  MPI_Recv(r->buf, r->size, MPI_PACKED, source, MPI_ANY_TAG, nrn_bbs_comm, &status));
381  errno = 0;
382  /* Some MPI implementations limit tags to be less than full MPI_INT domain
383  In the past we allowed TODO mesages to have tags > 20 (FIRSTID of src/parallel/bbssrv.h)
384  To fix the bug we no longer send or receive such tags and instead
385  copy the tag into the second pkint of the r->buf and send with
386  a tag of 20.
387  */
388  if (status.MPI_TAG == 20) {
389  int tag;
390  int save_upkpos = r->upkpos; /* possibly not needed */
391  nrnmpi_upkbegin(r);
392  nrnmpi_upkint(r);
393  tag = nrnmpi_upkint(r);
394  r->upkpos = save_upkpos;
395  return tag;
396  }
397  return status.MPI_TAG;
398 }
399 
400 int nrnmpi_bbssendrecv(int dest, int tag, bbsmpibuf* s, bbsmpibuf* r) {
401  int size, itag, source;
402 #if debug
403  printf("%d nrnmpi_bbssendrecv dest=%d tag=%d\n", nrnmpi_myid_bbs, dest, tag);
404 #endif
405  if (!nrnmpi_iprobe(&size, &itag, &source) || source != dest) {
406 #if debug
407  printf("%d nrnmpi_bbssendrecv nothing available so send\n", nrnmpi_myid_bbs);
408 #endif
409  nrnmpi_bbssend(dest, tag, s);
410  }
411  return nrnmpi_bbsrecv(dest, r);
412 }
413 
414 int nrnmpi_iprobe(int* size, int* tag, int* source) {
415  int flag = 0;
416  MPI_Status status;
417  nrn_mpi_assert(MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, nrn_bbs_comm, &flag, &status));
418  if (flag) {
419  if (source)
420  *source = status.MPI_SOURCE;
421  if (tag)
422  *tag = status.MPI_TAG;
423  if (size)
424  nrn_mpi_assert(MPI_Get_count(&status, MPI_PACKED, size));
425  }
426  return flag;
427 }
428 
429 void nrnmpi_probe(int* size, int* tag, int* source) {
430  MPI_Status status;
431  nrn_mpi_assert(MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, nrn_bbs_comm, &status));
432  if (source)
433  *source = status.MPI_SOURCE;
434  if (tag)
435  *tag = status.MPI_TAG;
436  if (size)
437  nrn_mpi_assert(MPI_Get_count(&status, MPI_PACKED, size));
438 }
439 
440 bbsmpibuf* nrnmpi_newbuf(int size) {
441  bbsmpibuf* buf;
442  buf = (bbsmpibuf*) hoc_Emalloc(sizeof(bbsmpibuf));
443  hoc_malchk();
444 #if debug
445  printf("%d nrnmpi_newbuf %p\n", nrnmpi_myid_bbs, buf);
446 #endif
447  buf->buf = (char*) 0;
448  if (size > 0) {
449  buf->buf = static_cast<char*>(hoc_Emalloc(size * sizeof(char)));
450  hoc_malchk();
451  }
452  buf->size = size;
453  buf->pkposition = 0;
454  buf->upkpos = 0;
455  buf->keypos = 0;
456  buf->refcount = 0;
457 #if nrnmpidebugleak
458  ++nrnmpi_bufcnt_;
459 #endif
460  return buf;
461 }
462 
463 void nrnmpi_copy(bbsmpibuf* dest, bbsmpibuf* src) {
464  int i;
465  resize(dest, src->size);
466  for (i = 0; i < src->size; ++i) {
467  dest->buf[i] = src->buf[i];
468  }
469  dest->pkposition = src->pkposition;
470  dest->upkpos = src->upkpos;
471  dest->keypos = src->keypos;
472 }
473 
474 static void nrnmpi_free(bbsmpibuf* buf) {
475 #if debug
476  printf("%d nrnmpi_free %p\n", nrnmpi_myid_bbs, buf);
477 #endif
478  if (buf->buf) {
479  free(buf->buf);
480  }
481  free(buf);
482 #if nrnmpidebugleak
483  --nrnmpi_bufcnt_;
484 #endif
485 }
486 
487 void nrnmpi_ref(bbsmpibuf* buf) {
488  assert(buf);
489  buf->refcount += 1;
490 }
491 
492 void nrnmpi_unref(bbsmpibuf* buf) {
493  if (buf) {
494  --buf->refcount;
495  if (buf->refcount <= 0) {
496  nrnmpi_free(buf);
497  }
498  }
499 }
500 
501 #if nrnmpidebugleak
502 void nrnmpi_checkbufleak() {
503  if (nrnmpi_bufcnt_ > 0) {
504  printf("%d nrnmpi_bufcnt=%d\n", nrnmpi_myid_bbs, nrnmpi_bufcnt_);
505  }
506 }
507 #endif
508 
509 #endif /*NRNMPI*/
#define i
Definition: md1redef.h:19
char buf[512]
Definition: init.cpp:13
#define assert(ex)
Definition: hocassrt.h:24
char * cxx_char_alloc(size_t sz)
Definition: ivoc.cpp:169
printf
Definition: extdef.h:5
void hoc_malchk(void)
Definition: nrnoc_aux.cpp:83
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
void * hoc_Emalloc(size_t)
Definition: nrnoc_aux.cpp:80
int ii
Definition: cellorder.cpp:631
int const size_t const size_t n
Definition: nrngsl.h:10
return status
size_t p
s
Definition: multisend.cpp:521
short type
Definition: cabvars.h:10
#define MPI_Comm
void * hoc_Erealloc(void *ptr, std::size_t size)
Definition: memory.cpp:41
int nrnmpi_myid_bbs
static double pack(void *v)
Definition: ocbbs.cpp:280
static double unpack(void *v)
Definition: ocbbs.cpp:321
static double resize(void *v)
Definition: ocptrvector.cpp:86