NEURON
multicore.cpp
Go to the documentation of this file.
1 /* included by treeset.cpp */
2 /*#include <../../nrnconf.h>*/
3 /*#include <multicore.h>*/
4 #include <nrnpthread.h>
5 #include <nrnmpi.h>
6 
7 
8 /*
9 Now that threads have taken over the actual_v, v_node, etc, it might
10 be a good time to regularize the method of freeing, allocating, and
11 updating those arrays. To recapitulate the history, Node used to
12 be the structure that held direct values for v, area, d, rhs, etc.
13 That continued to hold for the cray vectorization project which
14 introduced v_node, v_parent, memb_list. Cache efficiency introduced
15 actual_v, actual_area, actual_d, etc and the Node started pointing
16 into those arrays. Additional nodes after allocation required updating
17 pointers to v and area since those arrays were freed and reallocated.
18 Now, the threads hold all these arrays and we want to update them
19 properly under the circumstances of changing topology, changing
20 number of threads, and changing distribution of cells on threads.
21 Note there are no longer global versions of any of these arrays.
22 We do not want to update merely due to a change in area. Recently
23 we have dealt with diam, area, ri on a section basis. We generally
24 desire an update just before a simulation when the efficient
25 structures are necessary. This is reasonably well handled by the
26 v_structure_change flag which historically freed and reallocated
27 v_node and v_parent and, just before this comment,
28 ended up setting the NrnThread tml. This makes most of the old
29 memb_list vestigial and we now got rid of it except for
30 the artificial cells (and it is possibly not really necessary there).
31 Switching between sparse and tree matrix just cause freeing and
32 reallocation of actual_rhs.
33 
34 If we can get the freeing, reallocation, and pointer update correct
35 for _actual_v, I am guessing everything else can be dragged along with
36 it. We have two major cases, call to pc.nthread and change in
37 model structure. We want to use Node* as much as possible and defer
38 the handling of v_structure_change as long as possible.
39 */
40 
41 #define CACHELINE_ALLOC(name, type, size) \
42  name = (type*) nrn_cacheline_alloc((void**) &name, size * sizeof(type))
43 #define CACHELINE_CALLOC(name, type, size) \
44  name = (type*) nrn_cacheline_calloc((void**) &name, size, sizeof(type))
45 
49 
50 static int busywait_;
51 static int busywait_main_;
52 extern void nrn_thread_error(const char*);
53 extern void nrn_threads_free();
54 extern void nrn_old_thread_save();
55 extern double nrn_timeus();
56 
58 void nrn_mk_table_check();
59 static int table_check_cnt_;
61 static int allow_busywait_;
62 
63 /* linux specfic for performance testing */
64 /* eventually will be removed */
65 #define BENCHMARKING 0
66 #if BENCHMARKING
67 /* for rdtscll() */
68 #include <asm/msr.h>
69 #define BENCHDECLARE unsigned long t1;
70 #define BENCHBEGIN(arg) \
71  if (t_[arg] < t1_[arg] + BSIZE) { \
72  rdtscl(t1); \
73  *(t_[arg]++) = t1; \
74  }
75 #define BENCHADD(arg) BENCHBEGIN(arg)
76 #define WAIT wait_for_workers_timeit
77 #define CPU_MHZ 3192
78 #define BSIZE 200000
79 #define BS 10
80 static unsigned long bcnt_, bcnt1_;
81 static unsigned long t1_[BS][BSIZE], *t_[BS];
82 #else
83 #define BENCHDECLARE /**/
84 #define BENCHBEGIN(arg) /**/
85 #define BENCHADD(arg) /**/
86 #define WAIT wait_for_workers
87 #define BS 0
88 #endif
89 
90 static void* nulljob(NrnThread* nt) {
91  return nullptr;
92 }
93 
95 #if USE_PTHREAD
96 
97 #include <pthread.h>
98 #include <sched.h> /* for sched_setaffinity */
99 
100 /* abort if using threads and a call to malloc is unprotected */
101 #define use_malloc_hook 0
102 #if use_malloc_hook
103 #include <malloc.h>
104 
105 static int nrn_malloc_protected_;
106 static void my_init_hook();
107 static void* (*old_malloc_hook)(size_t, const void*);
108 static void* (*old_memalign_hook)(size_t, size_t, const void*);
109 static void* (*old_realloc_hook)(void*, size_t, const void*);
110 static void (*old_free_hook)(void*, const void*);
111 static void* my_malloc_hook(size_t, const void*);
112 static void* my_memalign_hook(size_t, size_t, const void*);
113 static void* my_realloc_hook(void*, size_t, const void*);
114 static void my_free_hook(void*, const void*);
115 void (*__malloc_initialize_hook)(void) = my_init_hook;
116 
117 static void* my_malloc_hook(size_t size, const void* caller) {
118  void* result;
119  if (nrn_inthread_ && !nrn_malloc_protected_) {
120  abort();
121  }
122  __malloc_hook = old_malloc_hook;
123  __memalign_hook = old_memalign_hook;
124  __realloc_hook = old_realloc_hook;
125  __free_hook = old_free_hook;
126  result = malloc(size);
127  old_malloc_hook = __malloc_hook;
128  old_memalign_hook = __memalign_hook;
129  old_realloc_hook = __realloc_hook;
130  old_free_hook = __free_hook;
131  __malloc_hook = my_malloc_hook;
132  __memalign_hook = my_memalign_hook;
133  __realloc_hook = my_realloc_hook;
134  __free_hook = my_free_hook;
135  return result;
136 }
137 static void* my_memalign_hook(size_t alignment, size_t size, const void* caller) {
138  void* result;
139  if (nrn_inthread_ && !nrn_malloc_protected_) {
140  abort();
141  }
142  __malloc_hook = old_malloc_hook;
143  __memalign_hook = old_memalign_hook;
144  __realloc_hook = old_realloc_hook;
145  __free_hook = old_free_hook;
146  result = memalign(alignment, size);
147  old_malloc_hook = __malloc_hook;
148  old_memalign_hook = __memalign_hook;
149  old_realloc_hook = __realloc_hook;
150  old_free_hook = __free_hook;
151  __malloc_hook = my_malloc_hook;
152  __memalign_hook = my_memalign_hook;
153  __realloc_hook = my_realloc_hook;
154  __free_hook = my_free_hook;
155  return result;
156 }
157 static void* my_realloc_hook(void* ptr, size_t size, const void* caller) {
158  void* result;
159  if (nrn_inthread_ && !nrn_malloc_protected_) {
160  abort();
161  }
162  __malloc_hook = old_malloc_hook;
163  __memalign_hook = old_memalign_hook;
164  __realloc_hook = old_realloc_hook;
165  __free_hook = old_free_hook;
166  result = realloc(ptr, size);
167  old_malloc_hook = __malloc_hook;
168  old_memalign_hook = __memalign_hook;
169  old_realloc_hook = __realloc_hook;
170  old_free_hook = __free_hook;
171  __malloc_hook = my_malloc_hook;
172  __memalign_hook = my_memalign_hook;
173  __realloc_hook = my_realloc_hook;
174  __free_hook = my_free_hook;
175  return result;
176 }
177 static void my_free_hook(void* ptr, const void* caller) {
178  if (nrn_inthread_ && !nrn_malloc_protected_) {
179  abort();
180  }
181  __malloc_hook = old_malloc_hook;
182  __memalign_hook = old_memalign_hook;
183  __realloc_hook = old_realloc_hook;
184  __free_hook = old_free_hook;
185  free(ptr);
186  old_malloc_hook = __malloc_hook;
187  old_memalign_hook = __memalign_hook;
188  old_realloc_hook = __realloc_hook;
189  old_free_hook = __free_hook;
190  __malloc_hook = my_malloc_hook;
191  __memalign_hook = my_memalign_hook;
192  __realloc_hook = my_realloc_hook;
193  __free_hook = my_free_hook;
194 }
195 static void my_init_hook() {
196  static int installed = 0;
197  if (installed) {
198  return;
199  }
200  installed = 1;
201  old_malloc_hook = __malloc_hook;
202  __malloc_hook = my_malloc_hook;
203  old_memalign_hook = __memalign_hook;
204  __memalign_hook = my_memalign_hook;
205  old_realloc_hook = __realloc_hook;
206  __realloc_hook = my_realloc_hook;
207  old_free_hook = __free_hook;
208  __free_hook = my_free_hook;
209 }
210 #endif
211 
212 static int interpreter_locked;
213 static pthread_mutex_t interpreter_lock_;
214 static pthread_mutex_t* _interpreter_lock;
215 
216 static pthread_mutex_t nmodlmutex_;
217 pthread_mutex_t* _nmodlmutex;
218 
219 static pthread_mutex_t nrn_malloc_mutex_;
220 static pthread_mutex_t* _nrn_malloc_mutex;
221 
222 extern "C" void nrn_malloc_lock() {
223  if (_nrn_malloc_mutex) {
224  pthread_mutex_lock(_nrn_malloc_mutex);
225 #if use_malloc_hook
226  nrn_malloc_protected_ = 1;
227 #endif
228  }
229 }
230 
231 extern "C" void nrn_malloc_unlock() {
232  if (_nrn_malloc_mutex) {
233 #if use_malloc_hook
234  nrn_malloc_protected_ = 0;
235 #endif
236  pthread_mutex_unlock(_nrn_malloc_mutex);
237  }
238 }
239 
240 /* when PERMANENT is 0, we avoid false warnings with helgrind, but a bit slower */
241 /* when 0, create/join instead of wait on condition. */
242 #ifndef PERMANENT
243 #define PERMANENT 1
244 #endif
245 
246 typedef volatile struct {
247  int flag;
248  int thread_id;
249  /* for nrn_solve etc.*/
250  void* (*job)(NrnThread*);
251 } slave_conf_t;
252 
253 static pthread_cond_t* cond;
254 static pthread_mutex_t* mut;
255 static pthread_t* slave_threads;
256 static slave_conf_t* wc;
257 
258 static void wait_for_workers() {
259  int i;
260  for (i = 1; i < nrn_nthread; ++i) {
261 #if PERMANENT
262  if (busywait_main_) {
263  while (wc[i].flag != 0) {
264  ;
265  }
266  } else {
267  pthread_mutex_lock(mut + i);
268  while (wc[i].flag != 0) {
269  pthread_cond_wait(cond + i, mut + i);
270  }
271  pthread_mutex_unlock(mut + i);
272  }
273 #else
274  pthread_join(slave_threads[i], nullptr);
275 #endif
276  }
277 }
278 
279 static void wait_for_workers_timeit() {
281  BENCHBEGIN(BS - 2)
282  wait_for_workers();
283  BENCHADD(BS - 1)
284 }
285 
286 static void send_job_to_slave(int i, void* (*job)(NrnThread*) ) {
287 #if PERMANENT
288  pthread_mutex_lock(mut + i);
289  wc[i].job = job;
290  wc[i].flag = 1;
291  pthread_cond_signal(cond + i);
292  pthread_mutex_unlock(mut + i);
293 #else
294  pthread_create(slave_threads + i, nullptr, (void* (*) (void*) ) job, (void*) (nrn_threads + i));
295 #endif
296 }
297 
298 void setaffinity(int i) {
299  int mask;
300  return;
301 #if 0
302  cpu_set_t mask;
303  CPU_ZERO(&mask);
304  CPU_SET(i, &mask);
305  mask = (1 << i);
306  sched_setaffinity(0, 4, &mask);
307 #endif
308 }
309 
310 static void* slave_main(void* arg) {
311  slave_conf_t* my_wc = (slave_conf_t*) arg;
312  pthread_mutex_t* my_mut = mut + my_wc->thread_id;
313  pthread_cond_t* my_cond = cond + my_wc->thread_id;
315 #if BENCHMARKING
316  unsigned long* t_[BS];
317  int a1, a2;
318  a1 = my_wc->thread_id;
319  a2 = my_wc->thread_id + nrn_nthread;
320  t_[a1] = t1_[a1];
321  t_[a2] = t1_[a2];
322 #endif
323  setaffinity(my_wc->thread_id);
324 
325  for (;;) {
326  if (busywait_) {
327  while (my_wc->flag == 0) {
328  ;
329  }
330  if (my_wc->flag == 1) {
331  BENCHBEGIN(a1)
332  (*my_wc->job)(nrn_threads + my_wc->thread_id);
333  BENCHADD(a2)
334  } else {
335  return nullptr;
336  }
337  my_wc->flag = 0;
338  pthread_cond_signal(my_cond);
339  } else {
340  pthread_mutex_lock(my_mut);
341  while (my_wc->flag == 0) {
342  pthread_cond_wait(my_cond, my_mut);
343  }
344  pthread_mutex_unlock(my_mut);
345  pthread_mutex_lock(my_mut);
346  if (my_wc->flag == 1) {
347  pthread_mutex_unlock(my_mut);
348  BENCHBEGIN(a1)
349  (*my_wc->job)(nrn_threads + my_wc->thread_id);
350  BENCHADD(a2)
351  } else {
352  pthread_mutex_unlock(my_mut);
353  return nullptr;
354  }
355  pthread_mutex_lock(my_mut);
356  my_wc->flag = 0;
357  pthread_cond_signal(my_cond);
358  pthread_mutex_unlock(my_mut);
359  }
360  }
361  return nullptr;
362 }
363 
364 static void threads_create_pthread() {
365 #if NRNMPI
366  if (nrn_nthread > 1 && nrnmpi_numprocs > 1 && nrn_cannot_use_threads_and_mpi == 1) {
367  if (nrnmpi_myid == 0) {
368  printf("This MPI is not threadsafe so pthreads are disabled.\n");
369  }
371  return;
372  }
373 #endif
374  setaffinity(nrnmpi_myid);
375  if (nrn_nthread > 1) {
376  int i;
377 #if PERMANENT
378  CACHELINE_ALLOC(wc, slave_conf_t, nrn_nthread);
379  slave_threads = (pthread_t*) emalloc(sizeof(pthread_t) * nrn_nthread);
380  cond = (pthread_cond_t*) emalloc(sizeof(pthread_cond_t) * nrn_nthread);
381  mut = (pthread_mutex_t*) emalloc(sizeof(pthread_mutex_t) * nrn_nthread);
382  for (i = 1; i < nrn_nthread; ++i) {
383  wc[i].flag = 0;
384  wc[i].thread_id = i;
385  pthread_cond_init(cond + i, nullptr);
386  pthread_mutex_init(mut + i, nullptr);
387  pthread_create(slave_threads + i, nullptr, slave_main, (void*) (wc + i));
388  }
389 #else
390  slave_threads = (pthread_t*) emalloc(sizeof(pthread_t) * nrn_nthread);
391 #endif /* PERMANENT */
392  if (!_interpreter_lock) {
393  interpreter_locked = 0;
394  _interpreter_lock = &interpreter_lock_;
395  pthread_mutex_init(_interpreter_lock, nullptr);
396  }
397  if (!_nmodlmutex) {
398  _nmodlmutex = &nmodlmutex_;
399  pthread_mutex_init(_nmodlmutex, nullptr);
400  }
401  if (!_nrn_malloc_mutex) {
402  _nrn_malloc_mutex = &nrn_malloc_mutex_;
403  pthread_mutex_init(_nrn_malloc_mutex, nullptr);
404  }
406  } else {
408  }
409 }
410 
411 static void threads_free_pthread() {
412  int i;
413  if (slave_threads) {
414 #if PERMANENT
415  wait_for_workers();
416  for (i = 1; i < nrn_nthread; ++i) {
417  pthread_mutex_lock(mut + i);
418  wc[i].flag = -1;
419  pthread_cond_signal(cond + i);
420  pthread_mutex_unlock(mut + i);
421  pthread_join(slave_threads[i], nullptr);
422  pthread_cond_destroy(cond + i);
423  pthread_mutex_destroy(mut + i);
424  }
425  free((char*) slave_threads);
426  free((char*) cond);
427  free((char*) mut);
428  free((char*) wc);
429  slave_threads = (pthread_t*) 0;
430  cond = (pthread_cond_t*) 0;
431  mut = (pthread_mutex_t*) 0;
432  wc = (slave_conf_t*) 0;
433 #else
434  free((char*) slave_threads);
435  slave_threads = (pthread_t*) 0;
436 #endif /*PERMANENT*/
437  }
438  if (_interpreter_lock) {
439  pthread_mutex_destroy(_interpreter_lock);
440  _interpreter_lock = (pthread_mutex_t*) 0;
441  interpreter_locked = 0;
442  }
443  if (_nmodlmutex) {
444  pthread_mutex_destroy(_nmodlmutex);
445  _nmodlmutex = (pthread_mutex_t*) 0;
446  }
447  if (_nrn_malloc_mutex) {
448  pthread_mutex_destroy(_nrn_malloc_mutex);
449  _nrn_malloc_mutex = (pthread_mutex_t*) 0;
450  }
452 }
453 
454 #else /* USE_PTHREAD */
455 
456 extern "C" void nrn_malloc_lock() {}
457 extern "C" void nrn_malloc_unlock() {}
458 
459 static void threads_create_pthread() {
461 }
462 static void threads_free_pthread() {
464 }
465 #endif /* !USE_PTHREAD */
466 
467 void nrn_thread_error(const char* s) {
468  if (nrn_nthread != 1) {
469  hoc_execerror(s, (char*) 0);
470  }
471 }
472 
474 #if BENCHMARKING
475  FILE* f;
476  long i, j, n;
477  char buf[50];
478  sprintf(buf, "bench.%d.dat", nrnmpi_myid);
479  f = fopen(buf, "w");
480 #if 1
481  n = (t_[0] - t1_[0]);
482  for (i = 1; i < nrn_nthread; ++i) {
483  t_[i] = t1_[i] + n;
484  t_[i + nrn_nthread] = t1_[i + nrn_nthread] + n;
485  }
486 #endif
487  n = 0;
488  for (i = 0; i < BS; ++i) {
489  n += t_[i] - t1_[i];
490  }
491  fprintf(f, "%ld\n", n);
492  n = 0;
493  for (j = 0; j < BS; ++j) {
494  n = t_[j] - t1_[j];
495  for (i = 0; i < n; ++i) {
496  fprintf(f, "%ld %d\n", t1_[j][i], j * nrnmpi_numprocs + nrnmpi_myid);
497  }
498  }
499  fclose(f);
500 #endif /*BENCHMARKING*/
501 }
502 
503 
504 void nrn_threads_create(int n, int parallel) {
505  int i, j;
506  NrnThread* nt;
507  if (nrn_nthread != n) {
508  /*printf("sizeof(NrnThread)=%d sizeof(Memb_list)=%d\n", sizeof(NrnThread),
509  * sizeof(Memb_list));*/
512  for (i = 0; i < nrn_nthread; ++i) {
513  nt = nrn_threads + i;
514  if (nt->userpart) {
515  hoc_obj_unref(nt->userpart);
516  }
517  }
518  free((char*) nrn_threads);
519 #if BENCHMARKING
520 #endif
521  nrn_threads = (NrnThread*) 0;
522  nrn_nthread = n;
523  if (n > 0) {
525 #if BENCHMARKING
526  for (i = 0; i < BS; ++i) {
527  t_[i] = t1_[i];
528  }
529 #endif
530  for (i = 0; i < n; ++i) {
531  nt = nrn_threads + i;
532  nt->_t = 0.;
533  nt->_dt = -1e9;
534  nt->id = i;
535  nt->_stop_stepping = 0;
536  nt->tml = (NrnThreadMembList*) 0;
537  nt->_ml_list = NULL;
538  nt->roots = (hoc_List*) 0;
539  nt->userpart = 0;
540  nt->ncell = 0;
541  nt->end = 0;
542  for (j = 0; j < BEFORE_AFTER_SIZE; ++j) {
543  nt->tbl[j] = (NrnThreadBAList*) 0;
544  }
545  nt->_actual_rhs = 0;
546  nt->_actual_d = 0;
547  nt->_actual_a = 0;
548  nt->_actual_b = 0;
549  nt->_actual_v = 0;
550  nt->_actual_area = 0;
551  nt->_v_parent_index = 0;
552  nt->_v_node = 0;
553  nt->_v_parent = 0;
554  nt->_ecell_memb_list = 0;
555  nt->_ecell_child_cnt = 0;
556  nt->_ecell_children = NULL;
557  nt->_sp13mat = 0;
558  nt->_ctime = 0.0;
559  nt->_vcv = 0;
560  nt->_nrn_fast_imem = 0;
561  }
562  }
563  v_structure_change = 1;
564  diam_changed = 1;
565  }
566  if (nrn_thread_parallel_ != parallel) {
568  if (parallel) {
570  }
571  }
572  /*printf("nrn_threads_create %d %d\n", nrn_nthread, nrn_thread_parallel_);*/
573 }
574 
575 /*
576 Avoid invalidating pointers to i_membrane_ unless the number of compartments
577 in a thread has changed.
578 */
579 static int fast_imem_nthread_ = 0;
580 static int* fast_imem_size_ = NULL;
582 
583 static void fast_imem_free() {
584  int i;
585  for (i = 0; i < nrn_nthread; ++i) {
587  }
588  for (i = 0; i < fast_imem_nthread_; ++i) {
589  if (fast_imem_size_[i] > 0) {
590  free(fast_imem_[i]._nrn_sav_rhs);
591  free(fast_imem_[i]._nrn_sav_d);
592  }
593  }
594  if (fast_imem_nthread_) {
595  free(fast_imem_size_);
596  free(fast_imem_);
597  fast_imem_nthread_ = 0;
599  fast_imem_ = NULL;
600  }
601 }
602 
603 static void fast_imem_alloc() {
604  int i;
606  fast_imem_free();
608  fast_imem_size_ = static_cast<int*>(ecalloc(nrn_nthread, sizeof(int)));
610  }
611  for (i = 0; i < nrn_nthread; ++i) {
612  NrnThread* nt = nrn_threads + i;
613  int n = nt->end;
614  _nrn_Fast_Imem* fi = fast_imem_ + i;
615  if (n != fast_imem_size_[i]) {
616  if (fast_imem_size_[i] > 0) {
617  free(fi->_nrn_sav_rhs);
618  free(fi->_nrn_sav_d);
619  }
620  if (n > 0) {
621  CACHELINE_CALLOC(fi->_nrn_sav_rhs, double, n);
622  CACHELINE_CALLOC(fi->_nrn_sav_d, double, n);
623  }
624  fast_imem_size_[i] = n;
625  }
626  }
627 }
628 
630  if (nrn_use_fast_imem) {
631  int i;
632  fast_imem_alloc();
633  for (i = 0; i < nrn_nthread; ++i) {
635  }
636  } else {
637  fast_imem_free();
638  }
639 }
640 
642  int it, i;
643  for (it = 0; it < nrn_nthread; ++it) {
644  NrnThread* nt = nrn_threads + it;
645  NrnThreadMembList *tml, *tml2;
646  for (tml = nt->tml; tml; tml = tml2) {
647  Memb_list* ml = tml->ml;
648  tml2 = tml->next;
649  free((char*) ml->nodelist);
650  free((char*) ml->nodeindices);
651  if (memb_func[tml->index].hoc_mech) {
652  free((char*) ml->prop);
653  } else {
654  free((char*) ml->data);
655  free((char*) ml->pdata);
656  }
657  if (ml->_thread) {
658  if (memb_func[tml->index].thread_cleanup_) {
659  (*memb_func[tml->index].thread_cleanup_)(ml->_thread);
660  }
661  free((char*) ml->_thread);
662  }
663  free((char*) ml);
664  free((char*) tml);
665  }
666  if (nt->_ml_list) {
667  free((char*) nt->_ml_list);
668  nt->_ml_list = NULL;
669  }
670  for (i = 0; i < BEFORE_AFTER_SIZE; ++i) {
671  NrnThreadBAList *tbl, *tbl2;
672  for (tbl = nt->tbl[i]; tbl; tbl = tbl2) {
673  tbl2 = tbl->next;
674  free((char*) tbl);
675  }
676  nt->tbl[i] = (NrnThreadBAList*) 0;
677  }
678  nt->tml = (NrnThreadMembList*) 0;
679  if (nt->userpart == 0 && nt->roots) {
680  hoc_l_freelist(&nt->roots);
681  nt->ncell = 0;
682  }
683  if (nt->_actual_rhs) {
684  free((char*) nt->_actual_rhs);
685  nt->_actual_rhs = 0;
686  }
687  if (nt->_actual_d) {
688  free((char*) nt->_actual_d);
689  nt->_actual_d = 0;
690  }
691  if (nt->_actual_a) {
692  free((char*) nt->_actual_a);
693  nt->_actual_a = 0;
694  }
695  if (nt->_actual_b) {
696  free((char*) nt->_actual_b);
697  nt->_actual_b = 0;
698  }
699  if (nt->_v_parent_index) {
700  free((char*) nt->_v_parent_index);
701  nt->_v_parent_index = 0;
702  }
703  if (nt->_v_node) {
704  free((char*) nt->_v_node);
705  nt->_v_node = 0;
706  }
707  if (nt->_v_parent) {
708  free((char*) nt->_v_parent);
709  nt->_v_parent = 0;
710  }
711  nt->_ecell_memb_list = 0;
712  if (nt->_ecell_children) {
713  nt->_ecell_child_cnt = 0;
714  free(nt->_ecell_children);
715  nt->_ecell_children = NULL;
716  }
717  if (nt->_sp13mat) {
718  spDestroy(nt->_sp13mat);
719  nt->_sp13mat = 0;
720  }
721  nt->_nrn_fast_imem = NULL;
722  /* following freed by nrn_recalc_node_ptrs */
724  nt->_actual_v = 0;
725  nt->_actual_area = 0;
726  nt->end = 0;
727  nt->ncell = 0;
728  nt->_vcv = 0;
729  }
730 }
731 
732 /* be careful to make the tml list in proper memb_order. This is important */
733 /* for correct finitialize where mechanisms that write concentrations must be */
734 /* after ions and before mechanisms that read concentrations. */
735 
736 static void thread_memblist_setup(NrnThread* _nt, int* mlcnt, void** vmap) {
737  int i, ii;
738  Node* nd;
739  Prop* p;
740  NrnThreadMembList *tml, **ptml;
741  Memb_list** mlmap = (Memb_list**) vmap;
742  BAMech** bamap = (BAMech**) vmap;
743 #if 0
744 printf("thread_memblist_setup %lx v_node_count=%d ncell=%d end=%d\n", (long)nth, v_node_count, nth->ncell, nth->end);
745 #endif
746  for (i = 0; i < n_memb_func; ++i) {
747  mlcnt[i] = 0;
748  }
749 
750  /* count */
751  for (i = 0; i < _nt->end; ++i) {
752  nd = _nt->_v_node[i];
753  for (p = nd->prop; p; p = p->next) {
754  if (memb_func[p->type].current || memb_func[p->type].state ||
755  memb_func[p->type].initialize) {
756  ++mlcnt[p->type];
757  }
758  }
759  }
760  /* allocate */
761  ptml = &_nt->tml;
762  for (ii = 0; ii < n_memb_func; ++ii) {
763  i = memb_order_[ii];
764  if (mlcnt[i]) {
765  if (_nt->id > 0 && memb_func[i].vectorized == 0) {
766  hoc_execerror(memb_func[i].sym->name, "is not thread safe");
767  }
768  /*printf("thread_memblist_setup %lx type=%d cnt=%d\n", (long)nth, i, mlcnt[i]);*/
770  tml->index = i;
771  tml->next = (NrnThreadMembList*) 0;
772  *ptml = tml;
773  ptml = &tml->next;
774  CACHELINE_ALLOC(tml->ml, Memb_list, 1);
775  if (i == EXTRACELL) {
776  _nt->_ecell_memb_list = tml->ml;
777  }
778  mlmap[i] = tml->ml;
779  CACHELINE_ALLOC(tml->ml->nodelist, Node*, mlcnt[i]);
780  CACHELINE_ALLOC(tml->ml->nodeindices, int, mlcnt[i]);
781  if (memb_func[i].hoc_mech) {
782  tml->ml->prop = (Prop**) emalloc(mlcnt[i] * sizeof(Prop*));
783  } else {
784  CACHELINE_ALLOC(tml->ml->data, double*, mlcnt[i]);
785  CACHELINE_ALLOC(tml->ml->pdata, Datum*, mlcnt[i]);
786  }
787  tml->ml->_thread = (Datum*) 0;
788  if (memb_func[i].thread_size_) {
789  tml->ml->_thread = (Datum*) ecalloc(memb_func[i].thread_size_, sizeof(Datum));
790  if (memb_func[tml->index].thread_mem_init_) {
791  (*memb_func[tml->index].thread_mem_init_)(tml->ml->_thread);
792  }
793  }
794  tml->ml->nodecount = 0; /* counted again below */
795  }
796  }
798  for (tml = _nt->tml; tml; tml = tml->next) {
799  _nt->_ml_list[tml->index] = tml->ml;
800  }
801 
802  /* fill */
803  for (i = 0; i < _nt->end; ++i) {
804  nd = _nt->_v_node[i];
805  for (p = nd->prop; p; p = p->next) {
806  if (memb_func[p->type].current || memb_func[p->type].state ||
807  memb_func[p->type].initialize) {
808  Memb_list* ml = mlmap[p->type];
809  ml->nodelist[ml->nodecount] = nd;
810  ml->nodeindices[ml->nodecount] = nd->v_node_index;
811  if (memb_func[p->type].hoc_mech) {
812  ml->prop[ml->nodecount] = p;
813  } else {
814  ml->data[ml->nodecount] = p->param;
815  ml->pdata[ml->nodecount] = p->dparam;
816  }
817  ++ml->nodecount;
818  }
819  }
820  }
821  /* count and store any Node* with the property
822  nd->extnode == NULL && nd->pnd != NULL && nd->pnd->extcell != NULL
823  */
824  if (_nt->_ecell_memb_list) {
825  Node* pnd;
826  int cnt = 0;
827  for (i = 0; i < _nt->end; ++i) {
828  nd = _nt->_v_node[i];
829  pnd = _nt->_v_parent[i];
830  if (nd->extnode == NULL && pnd && pnd->extnode) {
831  ++cnt;
832  }
833  }
834  if (cnt) {
835  Node** p;
837  _nt->_ecell_child_cnt = cnt;
838  p = _nt->_ecell_children;
839  cnt = 0;
840  for (i = 0; i < _nt->end; ++i) {
841  nd = _nt->_v_node[i];
842  pnd = _nt->_v_parent[i];
843  if (nd->extnode == NULL && pnd && pnd->extnode) {
844  p[cnt++] = nd;
845  }
846  }
847  }
848  }
849 #if 0
850  for (i=0; i < n_memb_func; ++i) {
851  if (mlcnt[i]) {assert(mlcnt[i] = mlmap[i]->nodecount);}
852  }
853  for (tml = _nt->tml; tml; tml = tml->next) {
854  assert(mlcnt[tml->index] == tml->ml->nodecount);
855  }
856 #endif
857  /* fill the ba lists */
858  /* need map from ml type to BA type. Reuse vmap */
859  for (i = 0; i < BEFORE_AFTER_SIZE; ++i) {
860  BAMech* bam;
861  NrnThreadBAList *tbl, **ptbl;
862  for (ii = 0; ii < n_memb_func; ++ii) {
863  bamap[ii] = (BAMech*) 0;
864  }
865  for (bam = bamech_[i]; bam; bam = bam->next) {
866  // Save first before-after block only. In case of multiple
867  // before-after blocks with the same mech type, we will get
868  // subsequent ones using linked list below.
869  if (!bamap[bam->type]) {
870  bamap[bam->type] = bam;
871  }
872  }
873  // necessary to keep in order wrt multiple BAMech with same mech type
874  ptbl = _nt->tbl + i;
875  for (tml = _nt->tml; tml; tml = tml->next) {
876  if (bamap[tml->index]) {
877  int mtype = tml->index;
878  Memb_list* ml = tml->ml;
879  for (bam = bamap[mtype]; bam && bam->type == mtype; bam = bam->next) {
880  tbl = (NrnThreadBAList*) emalloc(sizeof(NrnThreadBAList));
881  *ptbl = tbl;
882  tbl->next = (NrnThreadBAList*) 0;
883  tbl->bam = bam;
884  tbl->ml = ml;
885  ptbl = &(tbl->next);
886  }
887  }
888  }
889  }
890  /* fill in the Point_process._vnt value. */
891  /* artificial cells done in v_setup_vectors() */
892  for (tml = _nt->tml; tml; tml = tml->next)
893  if (memb_func[tml->index].is_point) {
894  for (i = 0; i < tml->ml->nodecount; ++i) {
895  Point_process* pnt = (Point_process*) tml->ml->pdata[i][1]._pvoid;
896  pnt->_vnt = (void*) _nt;
897  }
898  }
899 }
900 
902  int it, *mlcnt;
903  void** vmap;
904  mlcnt = (int*) emalloc(n_memb_func * sizeof(int));
905  vmap = (void**) emalloc(n_memb_func * sizeof(void*));
906  for (it = 0; it < nrn_nthread; ++it) {
907  thread_memblist_setup(nrn_threads + it, mlcnt, vmap);
908  }
910  free((char*) vmap);
911  free((char*) mlcnt);
914  (*nrn_mk_transfer_thread_data_)();
915  }
916 }
917 
918 /* secorder needs to correspond to cells in NrnThread with roots */
919 /* at the beginning of each thread region */
920 /* this differs from original secorder where all roots are at the beginning */
921 /* in passing, also set start and end indices. */
922 
923 static void reorder_secorder() {
924  NrnThread* _nt;
925  Section *sec, *ch;
926  Node* nd;
927  hoc_Item* qsec;
928  hoc_List* sl;
929  int order, isec, i, j, inode;
930  /* count and allocate */
931  // ForAllSections(sec)
932  ITERATE(qsec, section_list) {
933  Section* sec = hocSEC(qsec);
934  sec->order = -1;
935  }
936  order = 0;
937  FOR_THREADS(_nt) {
938  /* roots of this thread */
939  sl = _nt->roots;
940  inode = 0;
941  ITERATE(qsec, sl) {
942  sec = hocSEC(qsec);
943  assert(sec->order == -1);
944  secorder[order] = sec;
945  sec->order = order;
946  ++order;
947  nd = sec->parentnode;
948  nd->_nt = _nt;
949  inode += 1;
950  }
951  /* all children of what is already in secorder */
952  for (isec = order - _nt->ncell; isec < order; ++isec) {
953  sec = secorder[isec];
954  /* to make it easy to fill in PreSyn.nt_*/
955  sec->prop->dparam[9]._pvoid = (void*) _nt;
956  for (j = 0; j < sec->nnode; ++j) {
957  nd = sec->pnode[j];
958  nd->_nt = _nt;
959  inode += 1;
960  }
961  for (ch = sec->child; ch; ch = ch->sibling) {
962  assert(ch->order == -1);
963  secorder[order] = ch;
964  ch->order = order;
965  ++order;
966  }
967  }
968  _nt->end = inode;
969  CACHELINE_CALLOC(_nt->_actual_rhs, double, inode);
970  CACHELINE_CALLOC(_nt->_actual_d, double, inode);
971  CACHELINE_CALLOC(_nt->_actual_a, double, inode);
972  CACHELINE_CALLOC(_nt->_actual_b, double, inode);
973  CACHELINE_CALLOC(_nt->_v_node, Node*, inode);
974  CACHELINE_CALLOC(_nt->_v_parent, Node*, inode);
975  CACHELINE_CALLOC(_nt->_v_parent_index, int, inode);
976  }
977  /* do it again and fill _v_node and _v_parent */
978  /* index each cell section in relative order. Do offset later */
979  // ForAllSections(sec)
980  ITERATE(qsec, section_list) {
981  Section* sec = hocSEC(qsec);
982  sec->order = -1;
983  }
984  order = 0;
985  FOR_THREADS(_nt) {
986  /* roots of this thread */
987  sl = _nt->roots;
988  inode = 0;
989  ITERATE(qsec, sl) {
990  sec = hocSEC(qsec);
991  assert(sec->order == -1);
992  secorder[order] = sec;
993  sec->order = order;
994  ++order;
995  nd = sec->parentnode;
996  nd->_nt = _nt;
997  _nt->_v_node[inode] = nd;
998  _nt->_v_parent[inode] = (Node*) 0;
999  _nt->_v_node[inode]->v_node_index = inode;
1000  inode += 1;
1001  }
1002  /* all children of what is already in secorder */
1003  for (isec = order - _nt->ncell; isec < order; ++isec) {
1004  sec = secorder[isec];
1005  /* to make it easy to fill in PreSyn.nt_*/
1006  sec->prop->dparam[9]._pvoid = (void*) _nt;
1007  for (j = 0; j < sec->nnode; ++j) {
1008  nd = sec->pnode[j];
1009  nd->_nt = _nt;
1010  _nt->_v_node[inode] = nd;
1011  if (j) {
1012  _nt->_v_parent[inode] = sec->pnode[j - 1];
1013  } else {
1014  _nt->_v_parent[inode] = sec->parentnode;
1015  }
1016  _nt->_v_node[inode]->v_node_index = inode;
1017  inode += 1;
1018  }
1019  for (ch = sec->child; ch; ch = ch->sibling) {
1020  assert(ch->order == -1);
1021  secorder[order] = ch;
1022  ch->order = order;
1023  ++order;
1024  }
1025  }
1026  _nt->end = inode;
1027  }
1029  /*assert(inode == v_node_count);*/
1030  /* not missing any */
1031  // ForAllSections(sec)
1032  ITERATE(qsec, section_list) {
1033  Section* sec = hocSEC(qsec);
1034  assert(sec->order != -1);
1035  }
1036 
1037  /* here is where multisplit reorders the nodes. Afterwards
1038  in either case, we can then point to v, d, rhs in proper
1039  node order
1040  */
1041  FOR_THREADS(_nt) for (inode = 0; inode < _nt->end; ++inode) {
1042  _nt->_v_node[inode]->_classical_parent = _nt->_v_parent[inode];
1043  }
1044  if (nrn_multisplit_setup_) {
1045  /* classical order abandoned */
1046  (*nrn_multisplit_setup_)();
1047  }
1048  /* make the Nodes point to the proper d, rhs */
1049  FOR_THREADS(_nt) {
1050  for (j = 0; j < _nt->end; ++j) {
1051  Node* nd = _nt->_v_node[j];
1052  nd->_d = _nt->_actual_d + j;
1053  nd->_rhs = _nt->_actual_rhs + j;
1054  }
1055  }
1056  /* because the d,rhs changed, if multisplit is used we need to update
1057  the reduced tree gather/scatter pointers
1058  */
1059  if (nrn_multisplit_setup_) {
1061  }
1062 }
1063 
1064 
1066  int i, id, index;
1067  int* ix;
1068  if (table_check_) {
1069  free((void*) table_check_);
1070  table_check_ = (Datum*) 0;
1071  }
1072  ix = (int*) emalloc(n_memb_func * sizeof(int));
1073  for (i = 0; i < n_memb_func; ++i) {
1074  ix[i] = -1;
1075  }
1076  table_check_cnt_ = 0;
1077  for (id = 0; id < nrn_nthread; ++id) {
1078  NrnThread* nt = nrn_threads + id;
1079  NrnThreadMembList* tml;
1080  for (tml = nt->tml; tml; tml = tml->next) {
1081  index = tml->index;
1082  if (memb_func[index].thread_table_check_ && ix[index] == -1) {
1083  ix[index] = id;
1084  table_check_cnt_ += 2;
1085  }
1086  }
1087  }
1088  if (table_check_cnt_) {
1090  }
1091  i = 0;
1092  for (id = 0; id < nrn_nthread; ++id) {
1093  NrnThread* nt = nrn_threads + id;
1094  NrnThreadMembList* tml;
1095  for (tml = nt->tml; tml; tml = tml->next) {
1096  index = tml->index;
1097  if (memb_func[index].thread_table_check_ && ix[index] == id) {
1098  table_check_[i++].i = id;
1099  table_check_[i++]._pvoid = (void*) tml;
1100  }
1101  }
1102  }
1103  free((void*) ix);
1104 }
1105 
1107  int i;
1108  for (i = 0; i < table_check_cnt_; i += 2) {
1110  NrnThreadMembList* tml = (NrnThreadMembList*) table_check_[i + 1]._pvoid;
1111  Memb_list* ml = tml->ml;
1113  ml->data[0], ml->pdata[0], ml->_thread, nt, tml->index);
1114  }
1115 }
1116 
1117 /* if it is possible for more than one thread to get into the
1118  interpreter, lock it. */
1120 #if USE_PTHREAD
1121  if (nrn_inthread_) {
1122  pthread_mutex_lock(_interpreter_lock);
1123  interpreter_locked = 1;
1124  }
1125 #endif
1126 }
1128 #if USE_PTHREAD
1129  if (interpreter_locked) {
1130  interpreter_locked = 0;
1131  pthread_mutex_unlock(_interpreter_lock);
1132  }
1133 #endif
1134 }
1135 
1136 void nrn_multithread_job(void* (*job)(NrnThread*) ) {
1137  int i;
1138 #if USE_PTHREAD
1139  BENCHDECLARE
1140  if (nrn_thread_parallel_) {
1141  nrn_inthread_ = 1;
1142  for (i = 1; i < nrn_nthread; ++i) {
1143  send_job_to_slave(i, job);
1144  }
1145  BENCHBEGIN(0)
1146  (*job)(nrn_threads);
1148  WAIT();
1149  nrn_inthread_ = 0;
1150  } else { /* sequential */
1151 #else
1152  {
1153 #endif
1154  for (i = 1; i < nrn_nthread; ++i) {
1155  BENCHBEGIN(i)
1156  (*job)(nrn_threads + i);
1158  }
1159  BENCHBEGIN(0)
1160  (*job)(nrn_threads);
1162  }
1163 }
1164 
1165 
1166 void nrn_onethread_job(int i, void* (*job)(NrnThread*) ) {
1167  BENCHDECLARE
1168  assert(i >= 0 && i < nrn_nthread);
1169 #if USE_PTHREAD
1170  if (nrn_thread_parallel_) {
1171  if (i > 0) {
1172  send_job_to_slave(i, job);
1173  WAIT();
1174  } else {
1175  BENCHBEGIN(0)
1176  (*job)(nrn_threads);
1178  }
1179  } else {
1180 #else
1181  {
1182 #endif
1183  (*job)(nrn_threads + i);
1184  }
1185 }
1186 
1188 #if USE_PTHREAD
1189  if (nrn_thread_parallel_) {
1190  wait_for_workers();
1191  }
1192 #endif
1193 }
1194 
1196  NrnThread* nt;
1197  assert(it >= 0 && it < nrn_nthread);
1198  nt = nrn_threads + it;
1199  if (nt->userpart == nullptr && nt->roots) {
1200  hoc_l_freelist(&nt->roots);
1201  }
1202  if (sl) {
1203  hoc_obj_ref(sl);
1204  }
1205  if (nt->userpart) {
1206  hoc_obj_unref(nt->userpart);
1207  nt->userpart = nullptr;
1208  nt->roots = (hoc_List*) 0;
1209  }
1210  if (sl) {
1211  nt->userpart = sl; /* already reffed above */
1212  nt->roots = (hoc_List*) sl->u.this_pointer;
1213  }
1214  v_structure_change = 1;
1215 }
1216 
1218  int i, it, b, n;
1219  hoc_Item* qsec;
1220  hoc_List* sl;
1221  char buf[256];
1222  Section* sec;
1223  NrnThread* nt;
1224  /* all one or all the other*/
1225  b = (nrn_threads[0].userpart != nullptr);
1226  for (it = 1; it < nrn_nthread; ++it) {
1227  if ((nrn_threads[it].userpart != nullptr) != b) {
1228  hoc_execerror("some threads have a user defined partition", "and some do not");
1229  }
1230  }
1231  if (!b) {
1232  return 0;
1233  }
1234 
1235  /* discard partition if any section mentioned has been deleted. The
1236  model has changed */
1237  FOR_THREADS(nt) {
1238  sl = nt->roots;
1239  ITERATE(qsec, sl) {
1240  sec = hocSEC(qsec);
1241  if (!sec->prop) {
1242  for (i = 0; i < nrn_nthread; ++i) {
1243  nrn_thread_partition(i, nullptr);
1244  }
1245  return 0;
1246  }
1247  }
1248  }
1249 
1250  // ForAllSections(sec)
1251  ITERATE(qsec, section_list) {
1252  Section* sec = hocSEC(qsec);
1253  sec->volatile_mark = 0;
1254  }
1255  /* fill in ncell and verify consistency */
1256  n = 0;
1257  for (it = 0; it < nrn_nthread; ++it) {
1258  nt = nrn_threads + it;
1259  sl = nt->roots;
1260  nt->ncell = 0;
1261  ITERATE(qsec, sl) {
1262  sec = hocSEC(qsec);
1263  ++nt->ncell;
1264  ++n;
1265  if (sec->parentsec) {
1266  sprintf(buf, "in thread partition %d is not a root section", it);
1268  }
1269  if (sec->volatile_mark) {
1270  sprintf(buf, "appeared again in partition %d", it);
1272  }
1273  sec->volatile_mark = 1;
1274  }
1275  }
1276  if (n != nrn_global_ncell) {
1277  sprintf(buf,
1278  "The total number of cells, %d, is different than the number of user partition "
1279  "cells, %d\n",
1281  n);
1282  hoc_execerror(buf, (char*) 0);
1283  }
1284  return 1;
1285 }
1286 
1287 void nrn_use_busywait(int b) {
1288 #if USE_PTHREAD
1290  if (b == 0 && busywait_main_ == 1) {
1291  busywait_ = 0;
1293  busywait_main_ = 0;
1294  } else if (b == 1 && busywait_main_ == 0) {
1295  busywait_main_ = 1;
1296  wait_for_workers();
1297  busywait_ = 1;
1299  }
1300  } else {
1301  if (busywait_main_ == 1) {
1302  busywait_ = 0;
1304  busywait_main_ = 0;
1305  }
1306  }
1307 #endif
1308 }
1309 
1311  int old = allow_busywait_;
1312  allow_busywait_ = b;
1313  return old;
1314 }
1315 
1316 #if USE_PTHREAD
1317 static long waste_;
1318 static void* waste(void* v) {
1319  size_t i, j, n;
1320  n = (size_t) v;
1321  j = 0;
1322  for (i = 0; i < n; ++i) {
1323  j += i;
1324  }
1325  /* hoping it is not optimized away */
1326  waste_ = j;
1327  return nullptr;
1328 }
1329 
1330 #define _nt_ 32
1331 static double trial(int ip) {
1332  int i;
1333  double t;
1334  pthread_t* th;
1335  th = (pthread_t*) ecalloc(ip, sizeof(pthread_t));
1336  t = nrn_timeus();
1337  for (i = 0; i < ip; ++i) {
1338  pthread_create(th + i, nullptr, waste, (void*) 100000000);
1339  }
1340  for (i = 0; i < ip; ++i) {
1341  pthread_join(th[i], nullptr);
1342  }
1343  t = nrn_timeus() - t;
1344  free((char*) th);
1345  return t;
1346 }
1347 #endif
1348 
1350 #if USE_PTHREAD
1351  int i, ip;
1352  double t1, t2;
1353  printf("nthread walltime (count to 1e8 on each thread)\n");
1354  t1 = trial(1);
1355  printf("%4d\t %g\n", 1, t1);
1356  for (ip = 2; ip <= _nt_; ip *= 2) {
1357  t2 = trial(ip);
1358  printf("%4d\t %g\n", ip, t2);
1359  if (t2 > 1.3 * t1) {
1360  return ip / 2;
1361  }
1362  }
1363  return _nt_;
1364 #else
1365  return 1;
1366 #endif
1367 }
Section ** secorder
Definition: solve.cpp:77
int section_count
Definition: solve.cpp:76
const char * secname(Section *sec)
Definition: cabcode.cpp:1776
short index
Definition: cabvars.h:10
Memb_func * memb_func
Definition: init.cpp:123
static Node * pnd
Definition: clamp.cpp:33
#define spDestroy
Definition: cspredef.h:9
int diam_changed
Definition: cabcode.cpp:23
void(* nrn_multisplit_setup_)()
Definition: treeset.cpp:46
static double order(void *v)
Definition: cvodeobj.cpp:239
double t
Definition: cvodeobj.cpp:59
int v_structure_change
Definition: cvodestb.cpp:99
sprintf(buf, " if (secondorder) {\n" " int _i;\n" " for (_i = 0; _i < %d; ++_i) {\n" " _p[_slist%d[_i]] += dt*_p[_dlist%d[_i]];\n" " }}\n", numeqn, listnum, listnum)
int nrn_use_fast_imem
Definition: fadvance.cpp:167
void hoc_execerror(const char *, const char *)
Definition: hoc.cpp:754
char buf[512]
Definition: init.cpp:13
void nrn_hoc_unlock()
Definition: multicore.cpp:1127
void nrn_hoc_lock()
Definition: multicore.cpp:1119
void hoc_obj_ref(Object *obj)
Definition: hoc_oop.cpp:1810
void hoc_obj_unref(Object *obj)
Definition: hoc_oop.cpp:1828
#define assert(ex)
Definition: hocassrt.h:32
void * ecalloc(size_t n, size_t size)
Definition: symbol.cpp:215
#define hocSEC(q)
Definition: hoclist.h:66
void hoc_l_freelist(hoc_List **)
void
#define v
Definition: md1redef.h:4
#define sec
Definition: md1redef.h:13
#define nodecount
Definition: md1redef.h:30
#define id
Definition: md1redef.h:33
#define i
Definition: md1redef.h:12
#define BEFORE_AFTER_SIZE
Definition: membfunc.h:78
#define ITERATE(itm, lst)
Definition: model.h:25
char * emalloc(unsigned n)
Definition: list.cpp:166
static int allow_busywait_
Definition: multicore.cpp:61
void nrn_thread_error(const char *)
Definition: multicore.cpp:467
void nrn_onethread_job(int i, void *(*job)(NrnThread *))
Definition: multicore.cpp:1166
static int table_check_cnt_
Definition: multicore.cpp:59
void nrn_threads_create(int n, int parallel)
Definition: multicore.cpp:504
static int fast_imem_nthread_
Definition: multicore.cpp:579
#define BENCHDECLARE
Definition: multicore.cpp:83
static void fast_imem_free()
Definition: multicore.cpp:583
int nrn_allow_busywait(int b)
Definition: multicore.cpp:1310
#define BENCHADD(arg)
Definition: multicore.cpp:85
static void thread_memblist_setup(NrnThread *_nt, int *mlcnt, void **vmap)
Definition: multicore.cpp:736
#define CACHELINE_ALLOC(name, type, size)
Definition: multicore.cpp:41
void nrn_malloc_unlock()
Definition: multicore.cpp:457
void nrn_multithread_job(void *(*job)(NrnThread *))
Definition: multicore.cpp:1136
void nrn_threads_free()
Definition: multicore.cpp:641
int nrn_nthread
Definition: multicore.cpp:46
void nrn_use_busywait(int b)
Definition: multicore.cpp:1287
#define BS
Definition: multicore.cpp:87
static int busywait_main_
Definition: multicore.cpp:51
NrnThread * nrn_threads
Definition: multicore.cpp:47
#define BENCHBEGIN(arg)
Definition: multicore.cpp:84
static void threads_create_pthread()
Definition: multicore.cpp:459
int nrn_inthread_
Definition: multicore.cpp:94
int nrn_user_partition()
Definition: multicore.cpp:1217
static int * fast_imem_size_
Definition: multicore.cpp:580
int nrn_how_many_processors()
Definition: multicore.cpp:1349
static void nrn_thread_memblist_setup()
Definition: multicore.cpp:901
static void reorder_secorder()
Definition: multicore.cpp:923
void nrn_thread_stat()
Definition: multicore.cpp:473
static Datum * table_check_
Definition: multicore.cpp:60
void nrn_thread_partition(int it, Object *sl)
Definition: multicore.cpp:1195
#define CACHELINE_CALLOC(name, type, size)
Definition: multicore.cpp:43
void nrn_mk_table_check()
Definition: multicore.cpp:1065
void nrn_thread_table_check()
Definition: multicore.cpp:1106
static void * nulljob(NrnThread *nt)
Definition: multicore.cpp:90
void nrn_fast_imem_alloc()
Definition: multicore.cpp:629
static _nrn_Fast_Imem * fast_imem_
Definition: multicore.cpp:581
void nrn_old_thread_save()
Definition: treeset.cpp:2159
void nrn_malloc_lock()
Definition: multicore.cpp:456
static void fast_imem_alloc()
Definition: multicore.cpp:603
void nrn_wait_for_threads()
Definition: multicore.cpp:1187
static int busywait_
Definition: multicore.cpp:50
static void threads_free_pthread()
Definition: multicore.cpp:462
static int nrn_thread_parallel_
Definition: multicore.cpp:57
#define WAIT
Definition: multicore.cpp:86
double nrn_timeus()
Definition: ftime.cpp:61
void(* nrn_mk_transfer_thread_data_)()
Definition: multicore.cpp:48
#define FOR_THREADS(nt)
Definition: multicore.h:104
void nrn_multisplit_ptr_update()
#define printf
Definition: mwprefix.h:26
#define fprintf
Definition: mwprefix.h:30
int const size_t const size_t n
Definition: nrngsl.h:11
size_t p
size_t j
int nrnmpi_myid
int nrnmpi_numprocs
hoc_List * section_list
Definition: init.cpp:102
int nrn_global_ncell
Definition: init.cpp:103
BAMech ** bamech_
Definition: init.cpp:129
short * memb_order_
Definition: init.cpp:125
int n_memb_func
Definition: init.cpp:440
#define arg
Definition: redef.h:28
sl
Definition: seclist.cpp:181
static double cond(void *v)
Definition: singlech.cpp:337
FILE * fopen()
#define cnt
Definition: spt2queue.cpp:19
#define NULL
Definition: sptree.h:16
double * _nrn_sav_rhs
Definition: multicore.h:46
double * _nrn_sav_d
Definition: multicore.h:47
struct BAMech * next
Definition: membfunc.h:82
int type
Definition: membfunc.h:81
void * hoc_mech
Definition: membfunc.h:54
int is_point
Definition: membfunc.h:53
Pvmi current
Definition: membfunc.h:33
Pvmi state
Definition: membfunc.h:35
void(* thread_table_check_)(double *, Datum *, Datum *, NrnThread *, int)
Definition: membfunc.h:51
int vectorized
Definition: membfunc.h:47
void(* thread_mem_init_)(Datum *)
Definition: membfunc.h:49
void(* thread_cleanup_)(Datum *)
Definition: membfunc.h:50
Pvmi initialize
Definition: membfunc.h:36
int nodecount
Definition: nrnoc_ml.h:18
Node ** nodelist
Definition: nrnoc_ml.h:5
double ** data
Definition: nrnoc_ml.h:14
Datum ** pdata
Definition: nrnoc_ml.h:15
Prop ** prop
Definition: nrnoc_ml.h:16
Datum * _thread
Definition: nrnoc_ml.h:17
Definition: section.h:133
struct NrnThread * _nt
Definition: section.h:158
struct Extnode * extnode
Definition: section.h:161
struct Node * _classical_parent
Definition: section.h:157
double * _rhs
Definition: section.h:146
int v_node_index
Definition: section.h:175
struct Prop * prop
Definition: section.h:152
double * _d
Definition: section.h:145
BAMech * bam
Definition: multicore.h:41
struct NrnThreadBAList * next
Definition: multicore.h:42
Memb_list * ml
Definition: multicore.h:40
Represent main neuron object computed by single thread.
Definition: multicore.h:58
double _dt
Definition: multicore.h:60
int * _v_parent_index
Definition: multicore.h:76
NrnThreadMembList * tml
Definition: multicore.h:62
int ncell
Definition: multicore.h:64
char * _sp13mat
Definition: multicore.h:79
_nrn_Fast_Imem * _nrn_fast_imem
Definition: multicore.h:82
int id
Definition: multicore.h:66
NrnThreadBAList * tbl[BEFORE_AFTER_SIZE]
Definition: multicore.h:89
int _stop_stepping
Definition: multicore.h:67
int end
Definition: multicore.h:65
double _ctime
Definition: multicore.h:86
double * _actual_b
Definition: multicore.h:73
double * _actual_v
Definition: multicore.h:74
Memb_list ** _ml_list
Definition: multicore.h:63
int _ecell_child_cnt
Definition: multicore.h:68
double * _actual_d
Definition: multicore.h:71
Node ** _v_parent
Definition: multicore.h:78
hoc_List * roots
Definition: multicore.h:90
Node ** _ecell_children
Definition: multicore.h:81
double * _actual_a
Definition: multicore.h:72
double * _actual_rhs
Definition: multicore.h:70
Memb_list * _ecell_memb_list
Definition: multicore.h:80
void * _vcv
Definition: multicore.h:83
Object * userpart
Definition: multicore.h:91
Node ** _v_node
Definition: multicore.h:77
double _t
Definition: multicore.h:59
double * _actual_area
Definition: multicore.h:75
struct NrnThreadMembList * next
Definition: multicore.h:34
Memb_list * ml
Definition: multicore.h:35
Definition: hocdec.h:227
void * _vnt
Definition: section.h:270
Definition: section.h:214
int order
Definition: section.h:52
struct Section * sibling
Definition: section.h:46
Definition: hocdec.h:177
void * _pvoid
Definition: hocdec.h:187
int i
Definition: hocdec.h:180