NEURON
bgpdma.cpp
Go to the documentation of this file.
1 // included by netpar.cpp
2 
3 /*
4 Overall exchange strategy
5 
6 When a cell spikes, it immediately does a DCMF_Multicast of
7 (int gid, double spiketime) to all the target machines that have
8 cells that need to receive this spike by spiketime + delay
9 I'd like to cycle through a list of mconfig.nconnections so that
10 I don't have to wait for my single connection to complete the previous
11 broadcast when there is a high density of generated spikes but I need
12 to take care of my bus error issues first.
13 
14 In order to minimize the number of nrnmpi_bgp_conserve tests
15 (and potentially abandon them altogether if I can ever guarantee
16 that exchange time is less than half the computation time), I divide the
17 minimum delay integration intervals into two equal subintervals.
18 So if a spike is generated in an even subinterval, I do not have
19 to include it in the conservation check until the end of the next even
20 subinterval.
21 
22 When a spike is received (DMA interrupt) it is placed in even or odd
23 buffers (depending on whether the coded gid is positive or negative)
24 
25 At the end of a computation subinterval the even or odd buffer spikes
26 are enqueued in the priority queue after checking that the number
27 of spikes sent is equal to the number of spikes sent.
28 */
29 
30 extern "C" {
31 
32 extern IvocVect* vector_arg(int);
33 extern void vector_resize(IvocVect*, int);
34 
35 } // extern "C"
36 extern void (*nrntimeout_call)();
37 
38 
39 
40 // The initial idea behind use_phase2_ is to avoid the large overhead of
41 // initiating a send of the up to 10k list of target hosts when a cell fires.
42 // I.e. when there are a small number of cells on a processor, this causes
43 // load balance problems.
44 // Load balance shuld be better if the send is distributed to a much smaller
45 // set of targets, which, when they receive the spike, pass it on to a neighbor
46 // set.
47 // We expect that TWOPHASE will work best in combination with ENQUEUE=2
48 // which has the greatest amount of overlap between computation
49 // and communication.
50 // Note: the old implementation assumed that input PreSyn did not need
51 // a BGP_DMASend pointer so used the PreSyn.bgp.srchost_ element to
52 // help figure out the target_hosts_ list for the output PreSyn.bgp.dma_send_.
53 // Since bgp.srchost_ is used only for setup, it can be overwritten at phase2
54 // setup time with a bgp.dma_send_ so as to pass on the spike to the
55 // phase2 list of target hosts.
56 
57 #if !defined(DCMFTICK)
58 #define DCMFTICK 0
59 #define DCMFTIMEBASE 0
60 #endif
61 
62 static unsigned long long dmasend_time_;
63 static int n_xtra_cons_check_;
64 #define MAXNCONS 10
65 #if MAXNCONS
66 static int xtra_cons_hist_[MAXNCONS+1];
67 #endif
68 
69 // asm/msr.h no longer compiles on my machine.
70 // only for basic testing of logic when not on blue gene/p
71 #define USE_RDTSCL 0
72 
73 // only use if careful not to overrun the buffer during a simulation
74 #if 0 && USE_RDTSCL
75 #define TBUFSIZE (1<<15)
76 #else
77 #define TBUFSIZE 0
78 #endif
79 
80 #if TBUFSIZE
81 static unsigned long tbuf_[TBUFSIZE];
82 static int itbuf_;
83 #if USE_RDTSCL // but can have many accuracy problems on recent cpus.
84 /* for rdtscl() */
85 //#include <asm/msr.h>
86 #define rdtscl(a) a++
87 static unsigned long t__;
88 #define TBUF {rdtscl(t__); tbuf_[itbuf_++] = t__;}
89 #else
90 #define TBUF tbuf_[itbuf_++] = (unsigned long)DCMF_Timebase();
91 #endif // not USE_RDTSCLL
92 #else
93 #define TBUF /**/
94 #endif
95 
96 // ENQUEUE 0 means to BGP_ReceiveBuffer buffer -> PreSyn.send
97 // ENQUEUE 1 means to BGP_ReceiveBuffer buffer -> psbuf -> PreSyn.send
98 // ENQUEUE 2 means to BGP_ReceiveBuffer.incoming -> PrySyn.send
99 // Note that ENQUEUE 2 give more overlap between computation and exchange
100 // since the enqueuing takes place during computation except for those
101 // remaining during conservation.
102 #define ENQUEUE 2
103 
104 #if ENQUEUE == 2
105 static unsigned long enq2_find_time_;
106 static unsigned long enq2_enqueue_time_; // includes enq_find_time_
107 #endif
108 
109 #define PHASE2BUFFER_SIZE 2048 // power of 2
110 #define PHASE2BUFFER_MASK (PHASE2BUFFER_SIZE - 1)
111 struct Phase2Buffer {
113  double spiketime;
114 };
115 
116 #include <structpool.h>
117 
119 
120 #define BGP_RECEIVEBUFFER_SIZE 10000
122 public:
124  virtual ~BGP_ReceiveBuffer();
125  void init(int index);
126  void incoming(int gid, double spiketime);
127  void enqueue();
128  int index_;
129  int size_;
130  int count_;
132  int busy_;
133  int nsend_, nrecv_; // for checking conservation
134  int nsend_cell_; // cells that spiked this interval.
135  unsigned long long timebase_;
138 
139 #if ENQUEUE == 1
140  void enqueue1();
141  void enqueue2();
142 #endif
144  void phase2send();
147  int phase2_nsend_cell_, phase2_nsend_;
149 };
150 
151 static int use_phase2_;
152 #define NTARGET_HOSTS_PHASE1 ntarget_hosts_phase1_
153 
154 class BGP_DMASend {
155 public:
156  BGP_DMASend();
157  virtual ~BGP_DMASend();
158  void send(int gid, double t);
163 };
164 
166 public:
168  virtual ~BGP_DMASend_Phase2();
170 
171  void send_phase2(int gid, double t, BGP_ReceiveBuffer*);
174 };
175 
178 #if BGP_INTERVAL == 2
179 // note that if a spike is supposed to be received by bgp_receive_buffer[1]
180 // then during transmission its gid is complemented.
181 #endif
182 
184  busy_ = 0;
185  count_ = 0;
186  size_ = BGP_RECEIVEBUFFER_SIZE;
187  buffer_ = new NRNMPI_Spike*[size_];
188  pool_ = new SpkPool(BGP_RECEIVEBUFFER_SIZE);
189  psbuf_ = 0;
190 #if ENQUEUE == 1
191  psbuf_ = new PreSyn*[size_];
192 #endif
193  phase2_buffer_ = new Phase2Buffer[PHASE2BUFFER_SIZE];
194  phase2_head_ = phase2_tail_ = 0;
195 }
197  assert(busy_ == 0);
198  for (int i = 0; i < count_; ++i) {
199  pool_->hpfree(buffer_[i]);
200  }
201  delete [] buffer_;
202  delete pool_;
203  if (psbuf_) delete [] psbuf_;
204  delete [] phase2_buffer_;
205 }
207  index_ = index;
208  timebase_ = 0;
209  nsend_cell_ = nsend_ = nrecv_ = busy_ = maxcount_ = 0;
210  for (int i = 0; i < count_; ++i) {
211  pool_->hpfree(buffer_[i]);
212  }
213  count_ = 0;
214  phase2_head_ = phase2_tail_ = 0;
215  phase2_nsend_cell_ = phase2_nsend_ = 0;
216 }
217 void BGP_ReceiveBuffer::incoming(int gid, double spiketime) {
218 //printf("%d %p.incoming %g %g %d\n", nrnmpi_myid, this, t, spk->spiketime, spk->gid);
219  assert(busy_ == 0);
220  busy_ = 1;
221  if (count_ >= size_) {
222  size_ *= 2;
223  NRNMPI_Spike** newbuf = new NRNMPI_Spike*[size_];
224  for (int i = 0; i < count_; ++i) {
225  newbuf[i] = buffer_[i];
226  }
227  delete [] buffer_;
228  buffer_ = newbuf;
229  if (psbuf_) {
230  delete [] psbuf_;
231  psbuf_ = new PreSyn*[size_];
232  }
233  }
234  NRNMPI_Spike* spk = pool_->alloc();
235  spk->gid = gid;
236  spk->spiketime = spiketime;
237  buffer_[count_++] = spk;
238  if (maxcount_ < count_) { maxcount_ = count_; }
239  ++nrecv_;
240  busy_ = 0;
241 }
243 //printf("%d %p.enqueue count=%d t=%g nrecv=%d nsend=%d\n", nrnmpi_myid, this, t, count_, nrecv_, nsend_);
244  assert(busy_ == 0);
245  busy_ = 1;
246 #if 1
247  for (int i=0; i < count_; ++i) {
248  NRNMPI_Spike* spk = buffer_[i];
249 #if ENQUEUE == 2
250  unsigned long long tb = DCMFTIMEBASE;
251 #endif
252 
253  auto iter = gid2in_.find(spk->gid);
254  nrn_assert(iter != gid2in_.end());
255  PreSyn* ps = iter->second;
256  if (use_phase2_ && ps->bgp.dma_send_phase2_) {
257  // cannot do directly because busy_;
258  //ps->bgp.dma_send_phase2_->send_phase2(spk->gid, spk->spiketime, this);
259  Phase2Buffer& pb = phase2_buffer_[phase2_head_++];
260  phase2_head_ &= PHASE2BUFFER_MASK;
261  assert(phase2_head_ != phase2_tail_);
262  pb.ps = ps;
263  pb.spiketime = spk->spiketime;
264 
265  }
266 #if ENQUEUE == 2
267  enq2_find_time_ += (unsigned long)(DCMFTIMEBASE - tb);
268 #endif
269  ps->send(spk->spiketime, net_cvode_instance, nrn_threads);
270  pool_->hpfree(spk);
271 #if ENQUEUE == 2
272  enq2_enqueue_time_ += (unsigned long)(DCMFTIMEBASE - tb);
273 #endif
274  }
275 #endif
276  count_ = 0;
277 #if ENQUEUE != 2
278  nrecv_ = 0;
279  nsend_ = 0;
280  nsend_cell_ = 0;
281 #endif
282  busy_ = 0;
283  phase2send();
284 }
285 
286 #if ENQUEUE == 1
287 void BGP_ReceiveBuffer::enqueue1() {
288 //printf("%d %lx.enqueue count=%d t=%g nrecv=%d nsend=%d\n", nrnmpi_myid, (long)this, t, count_, nrecv_, nsend_);
289  assert(busy_ == 0);
290  busy_ = 1;
291  for (int i=0; i < count_; ++i) {
292  NRNMPI_Spike* spk = buffer_[i];
293  auto iter = gid2in_->find(spk->gid);
294  nrn_assert(iter != gid2in_.end()));
295  PreSyn* ps = iter->second;
296  psbuf_[i] = ps;
297  if (use_phase2_ && ps->bgp.dma_send_phase2_) {
298  // cannot do directly because busy_;
299  //ps->bgp.dma_send_phase2_->send_phase2(spk->gid, spk->spiketime, this);
300  Phase2Buffer& pb = phase2_buffer_[phase2_head_++];
301  phase2_head_ &= PHASE2BUFFER_MASK;
302  assert(phase2_head_ != phase2_tail_);
303  pb.ps = ps;
304  pb.spiketime = spk->spiketime;
305 
306  }
307  }
308  busy_ = 0;
309  phase2send();
310 }
311 
312 void BGP_ReceiveBuffer::enqueue2() {
313 //printf("%d %lx.enqueue count=%d t=%g nrecv=%d nsend=%d\n", nrnmpi_myid, (long)this, t, count_, nrecv_, nsend_);
314  assert(busy_ == 0);
315  busy_ = 1;
316  for (int i=0; i < count_; ++i) {
317  NRNMPI_Spike* spk = buffer_[i];
318  PreSyn* ps = psbuf_[i];
320  pool_->hpfree(spk);
321  }
322  count_ = 0;
323  nrecv_ = 0;
324  nsend_ = 0;
325  nsend_cell_ = 0;
326  busy_ = 0;
327 }
328 #endif // ENQUEUE == 1
329 
331  while (phase2_head_ != phase2_tail_) {
332  Phase2Buffer& pb = phase2_buffer_[phase2_tail_++];
333  phase2_tail_ &= PHASE2BUFFER_MASK;
334  pb.ps->bgp.dma_send_phase2_->send_phase2(pb.ps->gid_, pb.spiketime, this);
335  }
336 }
337 
338 // number of DCMF_Multicast_t to cycle through when not using recordreplay
339 #define NSEND 10
340 
341 static int max_ntarget_host;
342 // For one phase sending, max_multisend_targets is max_ntarget_host.
343 // For two phase sending, it is the maximum of all the
344 // ntarget_hosts_phase1 and ntarget_hosts_phase2.
346 
347 double nrn_bgp_receive_time(int type) { // and others
348  double rt = 0.;
349  switch(type) {
350  case 2: //in msend_recv
351  if (!use_bgpdma_) { return rt; }
352  for (int i = 0; i < n_bgp_interval; ++i) {
353  rt += bgp_receive_buffer[i]->timebase_ * DCMFTICK;
354  }
355  break;
356  case 3: // in BGP_DMAsend::send
357  if (!use_bgpdma_) { return rt; }
358  rt = dmasend_time_ * DCMFTICK;
359  break;
360  case 4: // number of extra conservation checks
361  rt = double(n_xtra_cons_check_);
362  // and if there is second vector arg then also return the histogram
363 #if MAXNCONS
364  if (ifarg(2) && use_bgpdma_) {
365  IvocVect* vec = vector_arg(2);
366  vector_resize(vec, MAXNCONS+1);
367  for (int i=0; i <= MAXNCONS; ++i) {
368  vector_vec(vec)[i] = double(xtra_cons_hist_[i]);
369  }
370  }
371 #endif // MAXNCONS
372 #if TBUFSIZE
373  if (ifarg(3)) {
374  IvocVect* vec = vector_arg(3);
375  vector_resize(vec, itbuf_+1);
376  for (int i=0; i <= itbuf_; ++i) {
377  vector_vec(vec)[i] = double(tbuf_[i]);
378  }
379  vector_vec(vec)[itbuf_] = DCMFTICK;
380  }
381 #endif
382  break;
383  case 8: // exchange method properties
384  // bit 0: 0 allgather, 1 multisend (MPI_ISend)
385  // bit 1: unused, legacy
386  // bit 2: n_bgp_interval, 0 means one interval, 1 means 2
387  // bit 3: number of phases, 0 means 1 phase, 1 means 2
388  // bit 4: unused (1 used to mean althash used)
389  // bit 5: 1 means enqueue separated into two parts for timeing
390  {
391  int method = use_bgpdma_ ? 1 : 0;
392  int p = method + 4*(n_bgp_interval == 2 ? 1 : 0)
393  + 8*use_phase2_
394  + 16*(0) // no hash selection, just std::unordered_map
395  + 32*ENQUEUE;
396  rt = double(p);
397  }
398  break;
399  case 12: // greatest length multisend
400  {
401  rt = double(max_multisend_targets);
402  break;
403  }
404  }
405  return rt;
406 }
407 
408 extern void nrnmpi_bgp_comm();
409 extern void nrnmpi_bgp_multisend(NRNMPI_Spike*, int, int*);
411 extern int nrnmpi_bgp_conserve(int nsend, int nrecv);
412 
413 static void bgp_dma_init() {
414  for (int i = 0; i < n_bgp_interval; ++i) {
415  bgp_receive_buffer[i]->init(i);
416  }
417  current_rbuf = 0;
418  next_rbuf = n_bgp_interval - 1;
419 #if TBUFSIZE
420  itbuf_ = 0;
421 #endif
422  dmasend_time_ = 0;
423 #if ENQUEUE == 2
425 #endif
426  n_xtra_cons_check_ = 0;
427 #if MAXNCONS
428  for (int i=0; i <= MAXNCONS; ++i) {
429  xtra_cons_hist_[i] = 0;
430  }
431 #endif // MAXNCONS
432 }
433 
434 static int bgp_advance() {
435  NRNMPI_Spike spk;
436  int i = 0;
437  while(nrnmpi_bgp_single_advance(&spk)) {
438  i += 1;
439  int j = 0;
440 #if BGP_INTERVAL == 2
441  if (spk.gid < 0) {
442  spk.gid = ~spk.gid;
443  j = 1;
444  }
445 #endif
446  bgp_receive_buffer[j]->incoming(spk.gid, spk.spiketime);
447  }
448  nrecv_ += i;
449  return i;
450 }
451 
452 #if BGPDMA
453 void nrnbgp_messager_advance() {
454 #if BGPDMA & 1
455  if (use_bgpdma_) { bgp_advance(); }
456 #endif
457 #if ENQUEUE == 2
458  bgp_receive_buffer[current_rbuf]->enqueue();
459 #endif
460 }
461 #endif
462 
464  ntarget_hosts_ = 0;
465  target_hosts_ = NULL;
466  ntarget_hosts_phase1_ = 0;
467 }
468 
470  if (target_hosts_) {
471  delete [] target_hosts_;
472  }
473 }
474 
476  ntarget_hosts_phase2_ = 0;
477  target_hosts_phase2_ = 0;
478 }
479 
481  if (target_hosts_phase2_) {
482  delete [] target_hosts_phase2_;
483  }
484 }
485 
486 // helps debugging when core dump since otherwise cannot tell where
487 // BGP_DMASend::send fails
488 #if 0
489 static void mymulticast(DCMF_Multicast_t* arg) {
490  DCMF_Multicast(arg);
491 }
492 static void myrestart(DCMF_Request_t* arg) {
493  DCMF_Restart(arg);
494 }
495 #endif
496 
497 void BGP_DMASend::send(int gid, double t) {
498  unsigned long long tb = DCMFTIMEBASE;
499  if (NTARGET_HOSTS_PHASE1) {
500  spk_.gid = gid;
501  spk_.spiketime = t;
502 #if BGP_INTERVAL == 2
503  bgp_receive_buffer[next_rbuf]->nsend_ += ntarget_hosts_;
504  bgp_receive_buffer[next_rbuf]->nsend_cell_ += 1;
505  if (next_rbuf == 1) {
506  spk_.gid = ~spk_.gid;
507  }
508 #else
509  bgp_receive_buffer[0]->nsend_ += ntarget_hosts_;
510  bgp_receive_buffer[0]->nsend_cell_ += 1;
511 #endif
512  nsend_ += 1;
513 #if BGPDMA & 1
514  if (use_bgpdma_) {
515  nrnmpi_bgp_multisend(&spk_, NTARGET_HOSTS_PHASE1, target_hosts_);
516  }
517 #endif
518  }
519  dmasend_time_ += DCMFTIMEBASE - tb;
520 }
521 
523  unsigned long long tb = DCMFTIMEBASE;
524  if (ntarget_hosts_phase2_) {
525  spk_.gid = gid;
526  spk_.spiketime = t;
527 #if BGP_INTERVAL == 2
528  if (rb->index_ == 1) {
529  spk_.gid = ~spk_.gid;
530  }
531 #endif
532  rb->phase2_nsend_cell_ += 1;
533  rb->phase2_nsend_ += ntarget_hosts_phase2_;
534 #if BGPDMA & 1
535  if (use_bgpdma_) {
536  nrnmpi_bgp_multisend(&spk_, ntarget_hosts_phase2_, target_hosts_phase2_);
537  }
538 #endif
539  }
540  dmasend_time_ += DCMFTIMEBASE - tb;
541 }
542 
544 // nrn_spike_exchange();
545  assert(nt == nrn_threads);
546  TBUF
547  double w1, w2;
548  int ncons = 0;
549  int& s = bgp_receive_buffer[current_rbuf]->nsend_;
550  int& r = bgp_receive_buffer[current_rbuf]->nrecv_;
551 #if ENQUEUE == 2 && TBUFSIZE
552  unsigned long tfind, tsend;
553 #endif
554  w1 = nrnmpi_wtime();
555 #if BGPDMA & 1
556  if (use_bgpdma_) {
557  nrnbgp_messager_advance();
558  TBUF
559 #if ENQUEUE == 2 && TBUFSIZE
560  // want the overlap with computation, not conserve
561  tfind = enq2_find_time_;
563 #endif
564 #if TBUFSIZE
565  nrnmpi_barrier();
566 #endif
567  TBUF
568  while (nrnmpi_bgp_conserve(s, r) != 0) {
569  nrnbgp_messager_advance();
570  ++ncons;
571  }
572  TBUF
573  }
574 #endif
575  w1 = nrnmpi_wtime() - w1;
576  w2 = nrnmpi_wtime();
577 #if TBUFSIZE
578  tbuf_[itbuf_++] = (unsigned long)ncons;
579  tbuf_[itbuf_++] = (unsigned long)bgp_receive_buffer[current_rbuf]->nsend_cell_;
580  tbuf_[itbuf_++] = (unsigned long)s;
581  tbuf_[itbuf_++] = (unsigned long)r;
582  tbuf_[itbuf_++] = (unsigned long)dmasend_time_;
583  if (use_phase2_) {
584  tbuf_[itbuf_++] = (unsigned long)bgp_receive_buffer[current_rbuf]->phase2_nsend_cell_;
585  tbuf_[itbuf_++] = (unsigned long)bgp_receive_buffer[current_rbuf]->phase2_nsend_;
586  }
587 #endif
588 #if (BGPMDA & 2) && MAXNCONS
589  if (ncons > MAXNCONS) { ncons = MAXNCONS; }
591 #endif // MAXNCONS
592 #if ENQUEUE == 0
593  bgp_receive_buffer[current_rbuf]->enqueue();
594 #endif
595 #if ENQUEUE == 1
596  bgp_receive_buffer[current_rbuf]->enqueue1();
597  TBUF
598  bgp_receive_buffer[current_rbuf]->enqueue2();
599 #endif
600 #if ENQUEUE == 2
601  bgp_receive_buffer[current_rbuf]->enqueue();
602  s = r = bgp_receive_buffer[current_rbuf]->nsend_cell_ = 0;
603  bgp_receive_buffer[current_rbuf]->phase2_nsend_cell_ = 0;
604  bgp_receive_buffer[current_rbuf]->phase2_nsend_ = 0;
605  enq2_find_time_ = 0;
606  enq2_enqueue_time_ = 0;
607 #if TBUFSIZE
608  tbuf_[itbuf_++] = tfind;
609  tbuf_[itbuf_++] = tsend;
610 #endif
611 #endif // ENQUEUE == 2
612  wt1_ = nrnmpi_wtime() - w2;
613  wt_ = w1;
614 #if BGP_INTERVAL == 2
615 //printf("%d reverse buffers %g\n", nrnmpi_myid, t);
616  if (n_bgp_interval == 2) {
618  next_rbuf = ((next_rbuf + 1)&1);
619  }
620 #endif
621  TBUF
622 }
623 
624 void bgp_dma_send(PreSyn* ps, double t) {
625 #if 0
626  if (nrn_use_localgid_) {
627  nrn_outputevent(ps->localgid_, t);
628  }else{
629  nrn2ncs_outputevent(ps->output_index_, t);
630  }
631 #endif
632  if (ps->bgp.dma_send_) ps->bgp.dma_send_->send(ps->output_index_, t);
633 }
634 
636  if (ps->bgp.dma_send_) {
637  if (ps->output_index_ >= 0) {
638  delete ps->bgp.dma_send_;
639  ps->bgp.dma_send_ = 0;
640  }
641  if (ps->output_index_ < 0) {
642  delete ps->bgp.dma_send_phase2_;
643  ps->bgp.dma_send_phase2_ = 0;
644  }
645  }
646 }
647 
648 static void bgpdma_cleanup() {
649  nrntimeout_call = 0;
650  for (const auto& iter: gid2out_) {
651  bgpdma_cleanup_presyn(iter.second);
652  }
653  for (const auto& iter: gid2in_) {
654  bgpdma_cleanup_presyn(iter.second);
655  }
656  if (!use_bgpdma_ && bgp_receive_buffer[1]) {
657  delete bgp_receive_buffer[0];
658  bgp_receive_buffer[0] = NULL;
659  }
660 #if BGP_INTERVAL == 2
661  if ((!use_bgpdma_ || n_bgp_interval != 2) && bgp_receive_buffer[1]) {
662  delete bgp_receive_buffer[1];
663  bgp_receive_buffer[1] = NULL;
664  }
665 #endif
666 }
667 
668 #ifndef BGPTIMEOUT
669 #define BGPTIMEOUT 0
670 #endif
671 
672 #if BGPTIMEOUT
673 static void bgptimeout() {
674  printf("%d timeout %d %d %d\n", nrnmpi_myid, current_rbuf,
675  bgp_receive_buffer[current_rbuf]->nsend_,
676  bgp_receive_buffer[current_rbuf]->nrecv_
677  );
678 }
679 #endif
680 
681 #if WORK_AROUND_RECORD_BUG
682 static void ensure_ntarget_gt_3(BGP_DMASend* bs) {
683  // work around for bug in RecordReplay
684  if (bs->ntarget_hosts_ > 3) { return; }
685  int nold = bs->ntarget_hosts_;
686  int* old = bs->target_hosts_;
687  bs->target_hosts_ = new int[4];
688  for (int i=0; i < nold; ++i) {
689  bs->target_hosts_[i] = old[i];
690  }
691  delete [] old;
692  int h = (nrnmpi_myid + 4)%nrnmpi_numprocs;
693  while (bs->ntarget_hosts_ < 4) {
694  int b = 0;
695  for (int i=0; i < bs->ntarget_hosts_; ++i) {
696  if (h == bs->target_hosts_[i]) {
697  h = (h + 4)%nrnmpi_numprocs;
698  b = 1;
699  break;
700  }
701  }
702  if (b == 0) {
703  bs->target_hosts_[bs->ntarget_hosts_++] = h;
704  h = (h + 1)%nrnmpi_numprocs;
705  }
706  }
708 }
709 #endif
710 
711 #define FASTSETUP 1
712 #if FASTSETUP
713 #include "bgpdmasetup.cpp"
714 #endif
715 
717  bgpdma_cleanup();
718  if (!use_bgpdma_) { return; }
719  //not sure this is useful for debugging when stuck in a collective.
720 #if BGPTIMEOUT
721  nrntimeout_call = bgptimeout;
722 #endif
723  nrnmpi_bgp_comm();
724  //if (nrnmpi_myid == 0) printf("bgp_dma_setup()\n");
725  // although we only care about the set of hosts that gid2out_
726  // sends spikes to (source centric). We do not want to send
727  // the entire list of gid2in (which may be 10000 times larger
728  // than gid2out) from every machine to every machine.
729  // so we accomplish the task in two phases the first of which
730  // involves allgather with a total receive buffer size of number
731  // of cells (even that is too large and we will split it up
732  // into chunks). And the second, an
733  // allreduce with receive buffer size of number of hosts.
734  max_ntarget_host = 0;
736 
737 #if FASTSETUP
738  // completely new algorithm does one and two phase.
740 #else // obsolete
741  // see 672:544c61a730ec
742 #error "FASTSETUP required"
743 #endif // obsolete (not FASTSETUP)
744 
745  if (!bgp_receive_buffer[0]) {
746  bgp_receive_buffer[0] = new BGP_ReceiveBuffer();
747  }
748 #if BGP_INTERVAL == 2
749  if (n_bgp_interval == 2 && !bgp_receive_buffer[1]) {
750  bgp_receive_buffer[1] = new BGP_ReceiveBuffer();
751  }
752 #endif
753 }
754 
755 #ifdef USENCS
756 
757 //give me data on which gids of this node send out APs
758 int ncs_bgp_sending_info( int **sendlist2build )
759 {
760  int nsrcgid = 0;
761  for (const auto& iter: gid2out_) {
762  if (iter.second->output_index_ >= 0) {
763  ++nsrcgid;
764  }
765  }
766 
767  *sendlist2build = nsrcgid ? new int[nsrcgid] : 0;
768  int i = 0;
769  for (const auto& iter: gid2out) {
770  PreSyn* ps = iter.second;
771  if (ps->output_index_ >= 0) {
772  (*sendlist2build)[i] = ps->gid_;
773  ++i;
774  }
775  }}}
776 
777  //printf( "Node %d sees first hand %d gids send\n", nrnmpi_myid, nsrcgid );
778  return nsrcgid;
779 }
780 
781 
782 //function to access the sending information of a presyn
783 int ncs_bgp_target_hosts( int gid, int** targetnodes )
784 {
785  auto iter = gid2out_->find(gid);
786  nrn_assert(iter != gid2out_.end());
787  PreSyn* ps = iter->second;
788  if( ps->bgp.dma_send_ ) {
789  (*targetnodes) = ps->bgp.dma_send_->ntarget_hosts_? new int[ps->bgp.dma_send_->ntarget_hosts_] : 0;
790  return ps->bgp.dma_send_->ntarget_hosts_;
791  }
792 
793  return 0;
794 }
795 
796 //iterate over gid2in_ just so I can see what is in there
797 int ncs_bgp_target_info( int **presyngids )
798 {
799  //(*presyngids) = 0;
800 
801  int i, nsrcgid;
802  nsrcgid = 0;
803 
804  // some target PreSyns may not have any input
805  // so initialize all to -1
806  for (const auto& iter: gid2in_) {
807  PreSyn* ps = iter.second;
808  assert(ps->output_index_ < 0);
809  if( ps->bgp.srchost_ != -1 ) { //has input
810  ++nsrcgid;
811  //printf( "Node %d: Presyn for gid %d has src %d\n", nrnmpi_myid, ps->gid_, ps->bgp.srchost_ );
812  }
813  }
814 
815  (*presyngids) = nsrcgid ? new int[nsrcgid] : 0;
816 
817  i=0;
818  for (const auto& iter: gid2in_) {
819  PreSyn* ps = iter.second;
820  assert(ps->output_index_ < 0);
821  if( ps->bgp.srchost_ != -1 ) { //has input
822  (*presyngids)[i] = ps->gid_;
823  ++i;
824  }
825  }
826 
827  return nsrcgid;
828 }
829 
830 int ncs_bgp_mindelays( int **srchost, double **delays )
831 {
832  int i, nsrcgid=0;
833 
834  for (const auto& iter: gid2in_) {
835  PreSyn* ps = iter.second;
836  assert(ps->output_index_ < 0);
837  if( ps->bgp.srchost_ != -1 ) { //has input
838  ++nsrcgid;
839  }
840  }
841 
842  (*delays) = nsrcgid ? new double[nsrcgid] : 0;
843  (*srchost) = nsrcgid ? new int[nsrcgid] : 0;
844 
845  i=0;
846  for (const auto& iter: gid2in_) {
847  PreSyn* ps = iter.second;
848  assert(ps->output_index_ < 0);
849  if( ps->bgp.srchost_ != -1 ) { //has input
850  (*delays)[i] = ps->mindelay();
851  (*srchost)[i] = ps->bgp.srchost_;
852  ++i;
853  }
854  }
855 
856  return nsrcgid;
857 }
858 
859 #endif //USENCS
860 
static unsigned long enq2_find_time_
Definition: bgpdma.cpp:105
#define TBUFSIZE
Definition: bgpdma.cpp:77
void send_phase2(int gid, double t, BGP_ReceiveBuffer *)
Definition: bgpdma.cpp:522
#define nrn_assert(ex)
Definition: nrnassrt.h:35
static BGP_ReceiveBuffer * bgp_receive_buffer[BGP_INTERVAL]
Definition: bgpdma.cpp:176
#define assert(ex)
Definition: hocassrt.h:26
#define MAXNCONS
Definition: bgpdma.cpp:64
short type
Definition: cabvars.h:10
#define PHASE2BUFFER_SIZE
Definition: bgpdma.cpp:109
void bgpdma_cleanup_presyn(PreSyn *ps)
Definition: bgpdma.cpp:635
static int max_multisend_targets
Definition: bgpdma.cpp:345
static Gid2PreSyn gid2out_
Definition: netpar.cpp:33
static unsigned long enq2_enqueue_time_
Definition: bgpdma.cpp:106
static int current_rbuf
Definition: bgpdma.cpp:177
static int max_ntarget_host
Definition: bgpdma.cpp:341
Definition: netcon.h:232
static int use_phase2_
Definition: bgpdma.cpp:151
static void bgp_dma_init()
Definition: bgpdma.cpp:413
void vector_resize(IvocVect *, int)
void
virtual void send(double sendtime, NetCvode *, NrnThread *)
Definition: netcvode.cpp:3165
size_t p
Represent main neuron object computed by single thread.
Definition: multicore.h:58
int output_index_
Definition: netcon.h:276
void bgp_dma_receive(NrnThread *nt)
Definition: bgpdma.cpp:543
void(* nrntimeout_call)()
#define TBUF
Definition: bgpdma.cpp:93
void phase2send()
Definition: bgpdma.cpp:330
#define BGP_RECEIVEBUFFER_SIZE
Definition: bgpdma.cpp:120
void nrnmpi_bgp_multisend(NRNMPI_Spike *, int, int *)
void nrnmpi_bgp_comm()
#define DCMFTIMEBASE
Definition: bgpdma.cpp:59
static void setup_presyn_dma_lists()
int ntarget_hosts_phase1_
Definition: bgpdma.cpp:162
double nrn_bgp_receive_time(int type)
Definition: bgpdma.cpp:347
static int bgp_advance()
Definition: bgpdma.cpp:434
static void bgpdma_cleanup()
Definition: bgpdma.cpp:648
void init()
Definition: init.cpp:169
void bgp_dma_send(PreSyn *ps, double t)
Definition: bgpdma.cpp:624
long
Definition: netcvode.cpp:4792
void init(int index)
Definition: bgpdma.cpp:206
virtual ~BGP_DMASend_Phase2()
Definition: bgpdma.cpp:480
double spiketime
Definition: bgpdma.cpp:113
int gid
Definition: nrnmpi.h:18
int nrnmpi_numprocs
_CONST char * s
Definition: system.cpp:74
NrnThread * nrn_threads
Definition: multicore.cpp:45
void bgp_dma_setup()
Definition: bgpdma.cpp:716
PreSyn * ps
Definition: bgpdma.cpp:112
static void nrnmpi_barrier()
#define printf
Definition: mwprefix.h:26
PreSyn ** psbuf_
Definition: bgpdma.cpp:143
double spiketime
Definition: nrnmpi.h:19
NRNMPI_Spike ** buffer_
Definition: bgpdma.cpp:136
IvocVect * vector_arg(int)
Definition: ivocvect.cpp:332
static Gid2PreSyn gid2in_
Definition: netpar.cpp:34
int ntarget_hosts_
Definition: bgpdma.cpp:159
unsigned long long timebase_
Definition: bgpdma.cpp:135
NRNMPI_Spike spk_
Definition: bgpdma.cpp:169
size_t j
int phase2_nsend_cell_
Definition: bgpdma.cpp:147
#define DCMFTICK
Definition: bgpdma.cpp:58
static int n_bgp_interval
Definition: netpar.cpp:29
int gid_
Definition: netcon.h:277
static unsigned long long dmasend_time_
Definition: bgpdma.cpp:62
int ifarg(int)
Definition: code.cpp:1562
void send(const char *url)
Definition: hel2mos.cpp:212
#define NTARGET_HOSTS_PHASE1
Definition: bgpdma.cpp:152
#define ENQUEUE
Definition: bgpdma.cpp:102
static int next_rbuf
Definition: bgpdma.cpp:177
virtual ~BGP_ReceiveBuffer()
Definition: bgpdma.cpp:196
Pool< NRNMPI_Spike > SpkPool
Definition: bgpdma.cpp:118
void incoming(int gid, double spiketime)
Definition: bgpdma.cpp:217
int nrnmpi_bgp_conserve(int nsend, int nrecv)
int nrnmpi_myid
SpkPool * pool_
Definition: bgpdma.cpp:137
int * target_hosts_
Definition: bgpdma.cpp:160
Phase2Buffer * phase2_buffer_
Definition: bgpdma.cpp:148
#define PHASE2BUFFER_MASK
Definition: bgpdma.cpp:110
#define i
Definition: md1redef.h:12
static double nrnmpi_wtime()
Definition: multisplit.cpp:55
double * vector_vec(Vect *v)
Definition: ivocvect.cpp:271
static int n_xtra_cons_check_
Definition: bgpdma.cpp:63
#define arg
Definition: redef.h:28
void send(int gid, double t)
Definition: bgpdma.cpp:497
#define BGP_INTERVAL
Definition: netpar.cpp:27
virtual ~BGP_DMASend()
Definition: bgpdma.cpp:469
double t
Definition: init.cpp:123
double mindelay()
Definition: netcvode.cpp:2967
int * target_hosts_phase2_
Definition: bgpdma.cpp:173
NRNMPI_Spike spk_
Definition: bgpdma.cpp:161
int nrnmpi_bgp_single_advance(NRNMPI_Spike *)
return NULL
Definition: cabcode.cpp:461
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:27
static int ncons
Definition: kinetic.cpp:500
int ntarget_hosts_phase2_
Definition: bgpdma.cpp:172
short index
Definition: cabvars.h:11
static int xtra_cons_hist_[MAXNCONS+1]
Definition: bgpdma.cpp:66