NEURON
bbs.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include "nrnmpi.h"
3 #include "bbsconf.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <InterViews/resource.h>
7 #include "oc2iv.h"
8 #include "bbs.h"
9 #include "bbslocal.h"
10 #if defined(NRNMPI)
11 #include "bbsdirect.h"
12 #include "bbsrcli.h"
13 #endif
14 
15 #if defined(HAVE_TMS) && !NRNMPI
16 #include <time.h>
17 #include <sys/times.h>
18 #include <limits.h>
19 static struct tms tmsbuf, tms_start_;
20 static clock_t starttime;
21 #endif
22 
23 extern int nrn_global_argc;
24 extern char** nrn_global_argv;
25 
26 bool BBSImpl::is_master_ = false;
27 bool BBSImpl::started_ = false;
28 bool BBSImpl::done_ = false;
29 bool BBSImpl::master_works_ = true;
30 
31 #undef debug
32 #define debug BBSImpl::debug_
33 
34 int BBSImpl::debug_ = 0;
35 int BBSImpl::mytid_;
36 
37 static int etaskcnt;
38 static double total_exec_time;
39 static double worker_take_time;
40 
42  init(-1);
43 }
44 
45 BBS::BBS(int n) {
46  init(n);
47 }
48 
49 #if NRNMPI
50 void BBS::init(int) {
51  if (nrnmpi_use == 0) {
52  BBSImpl::is_master_ = true;
53  impl_ = new BBSLocal();
54  return;
55  }
56  if (!BBSImpl::started_) {
57  BBSImpl::is_master_ = (nrnmpi_myid_bbs == 0) ? true : false;
59  // printf("%d BBS::init is_master=%d\n", nrnmpi_myid_bbs, BBSImpl::is_master_);
60  }
61  // Just as with PVM which stored buffers on the bulletin board
62  // so we have the following files to store MPI_PACKED buffers
63  // on the bulletin board. It would be possible to factor out
64  // the pvm stuff and we may do that later but for now we
65  // start with copies of the four files that worked for PVM
66  // and convert to the nrnmpi functions implemented in
67  // ../nrnmpi
68  // The four files are
69  // bbsclimpi.cpp - mpi remote client BBSClient from bbsrcli.cpp
70  // bbsdirectmpi.cpp - mpi master client BBSDirect from bbsdirect.cpp
71  // bbssrvmpi.cpp - mpi server portion to remote client from master bbs
72  // BBSDirectServer derived from bbssrv.cpp
73  // bbslsrvmpi.cpp - mpi master bbs portion of BBSDirectServer
74  // derived from bbslsrv2.cpp
75  // We reuse the .h files of these.
76  if (BBSImpl::is_master_) {
77  impl_ = new BBSDirect();
78  } else {
79  impl_ = new BBSClient();
80  }
81 }
82 #else // !NRNMPI
83 void BBS::init(int) {
84  if (!BBSImpl::started_) {
85  BBSImpl::is_master_ = true;
87  }
88  impl_ = new BBSLocal();
89 }
90 #endif // !NRNMPI
91 
94  wait_time_ = 0.;
95  send_time_ = 0.;
96  integ_time_ = 0.;
97  working_id_ = 0;
98  n_ = 0;
99  pickle_ret_ = 0;
100  pickle_ret_size_ = 0;
101 }
102 
104  delete impl_;
105 }
106 
108  if (pickle_ret_) {
109  delete[] pickle_ret_;
110  }
111 }
112 
114  return BBSImpl::is_master_;
115 }
116 
117 int BBS::nhost() {
118  return nrnmpi_numprocs;
119 }
120 
121 int BBS::myid() {
122  return nrnmpi_myid;
123 }
124 
126  return is_master_;
127 }
128 
129 double BBS::time() {
130  return impl_->time();
131 }
132 
133 double BBSImpl::time() {
134 #if NRNMPI
135  return nrnmpi_wtime();
136 #else
137 #ifdef HAVE_TMS
138  return double(times(&tmsbuf)) / 100.;
139 #else
140  return 0.;
141 #endif
142 #endif
143 }
144 
145 double BBS::wait_time() {
146  return impl_->wait_time_;
147 }
148 double BBS::integ_time() {
149  return impl_->integ_time_;
150 }
151 double BBS::send_time() {
152  return impl_->send_time_;
153 }
154 void BBS::add_wait_time(double st) {
155  impl_->wait_time_ += impl_->time() - st;
156 }
157 
158 void BBS::perror(const char* s) {
159  impl_->perror(s);
160 }
161 
162 void BBSImpl::perror(const char*) {}
163 
164 int BBS::upkint() {
165  int i = impl_->upkint();
166  if (debug) {
167  printf("upkint %d\n", i);
168  }
169  return i;
170 }
171 
172 double BBS::upkdouble() {
173  double d = impl_->upkdouble();
174  if (debug) {
175  printf("upkdouble %g\n", d);
176  }
177  return d;
178 }
179 
180 void BBS::upkvec(int n, double* px) {
181  impl_->upkvec(n, px);
182  if (debug) {
183  printf("upkvec %d\n", n);
184  }
185 }
186 
187 char* BBS::upkstr() {
188  char* s = impl_->upkstr();
189  if (debug) {
190  printf("upkstr |%s|\n", s);
191  }
192  return s;
193 }
194 
195 char* BBS::upkpickle(size_t* n) {
196  char* s = impl_->upkpickle(n);
197  if (debug) {
198  printf("upkpickle %lu |%s|\n", *n, s);
199  }
200  return s;
201 }
202 
203 void BBS::pkbegin() {
204  if (debug) {
205  printf("pkbegin\n");
206  }
207  impl_->pkbegin();
208 }
209 
210 void BBS::pkint(int i) {
211  if (debug) {
212  printf("pkint %d\n", i);
213  }
214  impl_->pkint(i);
215 }
216 
217 void BBS::pkdouble(double x) {
218  if (debug) {
219  printf("pkdouble %g\n", x);
220  }
221  impl_->pkdouble(x);
222 }
223 
224 void BBS::pkvec(int n, double* px) {
225  if (debug) {
226  printf("pkdouble %d\n", n);
227  }
228  impl_->pkvec(n, px);
229 }
230 
231 void BBS::pkstr(const char* s) {
232  if (debug) {
233  printf("pkstr |%s|\n", s);
234  }
235  impl_->pkstr(s);
236 }
237 
238 void BBS::pkpickle(const char* s, size_t n) {
239  if (debug) {
240  printf("pkpickle %lu |%s|\n", n, s);
241  }
242  impl_->pkpickle(s, n);
243 }
244 
245 #if 0
246 // for now all todo messages are of the three item form
247 // tid
248 // gid
249 // id
250 // "stmt"
251 // the latter should set hoc_ac_ if the return value is important
252 // right now every arg must be literal, we'll handle variables later.
253 // eg the following should work.
254 // n = 1000 x = 0 for i=1,n { x += i } hoc_ac_ = x
255 // although it may makes sense to send the result directly to the
256 // tid for now we just put it back onto the mailbox in the form
257 // message: "result tid gid" with two items, i.e. id, hoc_ac_
258 // Modified 11/30/09. To allow a return of a (pickled) PythonObject
259 // for the case when the execution is for a Python Callable the return
260 // message result now has three items, i.e. id, rtype, hoc_ac or pickled
261 // PyObject. rtype = 0 means hoc_ac, rtype = 1 means pickled PyObject.
262 // execute_helper returns the pickle string or (char*)0.
263 #endif
264 #if 0
265 // the todo management has been considerably modified to support
266 // a priority queue style for the order in which tasks are executed.
267 // Now the server manages the task id. Given a task id, it knows the
268 // parent task id.
269 // The working_id is always non-trivial on worker machines, and is
270 // only 0 on the master when dealing with a submission at the top level
271 // post_todo(working_id_) message is the statement, here the working_id is
272 // the parent of the future stmt task.
273 // take_todo: message is the statement. return is the id of this task
274 // post_result(id) message is return value.
275 // the result will be retrieved relative to the submitting working_id_
276 // look_take_result(working_id_) message is the return value. the return
277 // value of this call is the id of the task that computed the return.
278 #endif
279 
280 // BBSImpl::execute_helper() in ocbbs.cpp
281 
282 void BBSImpl::execute(int id) { // assumes a "_todo" message in receive buffer
283  ++etaskcnt;
284  double st, et;
285  int userid;
286  char* rs;
287  char* s;
288  size_t n;
289  int i;
290  int save_id = working_id_;
291  int save_n = n_;
292  working_id_ = id;
293  n_ = 0;
294  st = time();
295  if (debug_) {
296  printf("execute begin %g: working_id_=%d\n", st, working_id_);
297  }
298  userid = upkint();
299  int wid = upkint();
300  hoc_ac_ = double(id);
301  rs = execute_helper(&n, id); // builds and execute hoc statement
302  et = time() - st;
303  total_exec_time += et;
304  if (debug) {
305  printf("execute end elapsed %g: working_id_=%d hoc_ac_=%g\n", et, working_id_, hoc_ac_);
306  }
307  pkbegin();
308  pkint(userid);
309  pkint(wid);
310  pkint(rs ? 1 : 0);
311  if (!rs) {
312  pkdouble(hoc_ac_);
313  } else {
314  pkpickle(rs, n);
315  delete[] rs;
316  }
317  working_id_ = save_id;
318  n_ = save_n;
319  post_result(id);
320 }
321 
322 int BBS::submit(int userid) {
323  return impl_->submit(userid);
324 }
325 
327  // userid was the first item packed
328  ++n_;
329  if (debug) {
330  printf("submit n_= %d for working_id=%d userid=%d\n", n_, working_id_, userid);
331  }
332  if (userid < 0) {
333  save_args(userid);
334  } else {
336  }
337  return userid;
338 }
339 
340 void BBS::context() {
341  impl_->context();
342 }
343 
345  printf("can't execute BBS::context on a worker\n");
346  exit(1);
347 }
348 
349 bool BBS::working(int& id, double& x, int& userid) {
350  return impl_->working(id, x, userid);
351 }
352 
353 void BBS::master_works(int flag) {
354  if (impl_->is_master() && nrnmpi_numprocs_bbs > 1) {
355  impl_->master_works_ = flag ? true : false;
356  }
357 }
358 
360  assert(0);
361  return 0;
362 }
363 
364 bool BBSImpl::working(int& id, double& x, int& userid) {
365  int cnt = 0;
366  int rtype;
367  double t;
368  if (n_ <= 0) {
369  if (debug) {
370  printf("working n_=%d: return false\n", n_);
371  }
372  return false;
373  }
374  if (debug) {
375  t = time();
376  }
377  for (;;) {
378  ++cnt;
379  if (master_works_) {
381  } else {
383  }
384  if (id != 0) {
385  userid = upkint();
386  int wid = upkint();
387  rtype = upkint();
388  if (rtype == 0) {
389  x = upkdouble();
390  } else {
391  assert(rtype == 1);
392  x = 0.0;
393  if (pickle_ret_) {
394  delete[] pickle_ret_;
395  }
397  }
398  --n_;
399  if (debug) {
400  printf("working n_=%d: after %d try elapsed %g sec got result for %d id=%d x=%g\n",
401  n_,
402  cnt,
403  time() - t,
404  working_id_,
405  id,
406  x);
407  }
408  if (userid < 0) {
410  }
411  return true;
412  } else if ((id = look_take_todo()) != 0) {
413  if (debug) {
414  printf("working: no result for %d but did get _todo id=%d\n", working_id_, id);
415  }
416  execute(id);
417  }
418  }
419 };
420 
421 void BBS::worker() {
423  impl_->worker();
424 }
425 
427  // forever request and execute commands
428  double st, et;
429  int id;
430  if (!is_master()) {
431  if (nrnmpi_myid_bbs == -1) { // wait for message from
432  for (;;) { // the proper nrnmpi_myid == 0
434  }
435  }
436  for (;;) {
437  st = time();
438  id = take_todo();
439  et = time() - st;
440  worker_take_time += et;
441  execute(id);
442  }
443  }
444 }
445 
446 void BBS::post(const char* key) {
447  if (debug) {
448  printf("post: |%s|\n", key);
449  }
450  impl_->post(key);
451 }
452 
453 bool BBS::look_take(const char* key) {
454  bool b = impl_->look_take(key);
455  if (debug) {
456  printf("look_take |%s| return %d\n", key, b);
457  }
458  return b;
459 }
460 
461 bool BBS::look(const char* key) {
462  bool b = impl_->look(key);
463  if (debug) {
464  printf("look |%s| return %d\n", key, b);
465  }
466  return b;
467 }
468 
469 void BBS::take(const char* key) { // blocking
470  double t;
471  if (debug) {
472  t = time();
473  printf("begin take |%s| at %g\n", key, t);
474  }
475  impl_->take(key);
476  if (debug) {
477  printf("end take |%s| elapsed %g from %g\n", key, time() - t, t);
478  }
479 }
480 
481 void BBS::done() {
482  if (impl_->runworker_called_) {
483  impl_->done();
484  }
485 }
486 
488  if (done_) {
489  return;
490  }
491  done_ = true;
492 #ifdef HAVE_TMS
493  clock_t elapsed = times(&tmsbuf) - starttime;
494  printf("%d tasks in %g seconds. %g seconds waiting for tasks\n",
495  etaskcnt,
498  printf("user=%g sys=%g elapsed=%g %g%%\n",
499  (double) (tmsbuf.tms_utime - tms_start_.tms_utime) / 100,
500  (double) (tmsbuf.tms_stime - tms_start_.tms_stime) / 100,
501  (double) (elapsed) / 100,
502  100. * (double) (tmsbuf.tms_utime - tms_start_.tms_utime) / (double) elapsed);
503 #endif
504 }
505 
507  if (started_) {
508  return;
509  }
510  started_ = 1;
511 #ifdef HAVE_TMS
512  starttime = times(&tms_start_);
513 #endif
514 }
bool look_take(const char *)
Definition: bbs.cpp:453
int myid()
Definition: bbs.cpp:121
virtual ~BBS()
Definition: bbs.cpp:103
void master_works(int flag)
Definition: bbs.cpp:353
double send_time()
Definition: bbs.cpp:151
int nhost()
Definition: bbs.cpp:117
void pkstr(const char *)
Definition: bbs.cpp:231
void upkvec(int n, double *px)
Definition: bbs.cpp:180
void pkdouble(double)
Definition: bbs.cpp:217
void pkint(int)
Definition: bbs.cpp:210
void pkvec(int n, double *px)
Definition: bbs.cpp:224
void done()
Definition: bbs.cpp:481
double integ_time()
Definition: bbs.cpp:148
int upkint()
Definition: bbs.cpp:164
bool working(int &id, double &x, int &userid)
Definition: bbs.cpp:349
bool is_master()
Definition: bbs.cpp:113
void init(int)
Definition: bbs.cpp:83
void pkbegin()
Definition: bbs.cpp:203
void perror(const char *)
Definition: bbs.cpp:158
void post(const char *)
Definition: bbs.cpp:446
void worker()
Definition: bbs.cpp:421
double wait_time()
Definition: bbs.cpp:145
void add_wait_time(double)
Definition: bbs.cpp:154
bool look(const char *)
Definition: bbs.cpp:461
void pkpickle(const char *, size_t size)
Definition: bbs.cpp:238
int submit(int userid)
Definition: bbs.cpp:322
double time()
Definition: bbs.cpp:129
BBSImpl * impl_
Definition: bbs.h:74
char * upkstr()
Definition: bbs.cpp:187
void take(const char *)
Definition: bbs.cpp:469
BBS()
Definition: bbs.cpp:41
char * upkpickle(size_t *size)
Definition: bbs.cpp:195
double upkdouble()
Definition: bbs.cpp:172
void context()
Definition: bbs.cpp:340
virtual int upkint()=0
int working_id_
Definition: bbsimpl.h:54
virtual bool look_take(const char *)=0
static bool is_master_
Definition: bbsimpl.h:60
virtual void return_args(int userid)
Definition: ocbbs.cpp:1385
double send_time_
Definition: bbsimpl.h:57
static int mytid_
Definition: bbsimpl.h:62
BBSImpl()
Definition: bbs.cpp:92
virtual void post_result(int id)=0
char * execute_helper(size_t *, int id, bool exec=true)
Definition: ocbbs.cpp:1215
static int debug_
Definition: bbsimpl.h:63
virtual void save_args(int userid)=0
int n_
Definition: bbsimpl.h:54
virtual int take_todo()=0
double integ_time_
Definition: bbsimpl.h:56
virtual int submit(int userid)
Definition: bbs.cpp:326
virtual void worker()
Definition: bbs.cpp:426
static bool master_works_
Definition: bbsimpl.h:64
virtual void pkbegin()=0
virtual void pkstr(const char *)=0
virtual void execute(int id)
Definition: bbs.cpp:282
virtual void upkvec(int, double *)=0
static bool started_
Definition: bbsimpl.h:61
int runworker_called_
Definition: bbsimpl.h:53
virtual int look_take_result(int pid)=0
virtual char * upkstr()=0
double wait_time_
Definition: bbsimpl.h:55
virtual void perror(const char *)
Definition: bbs.cpp:162
virtual void pkvec(int, double *)=0
virtual ~BBSImpl()
Definition: bbs.cpp:107
virtual void pkpickle(const char *, size_t)=0
virtual void pkint(int)=0
void subworld_worker_execute()
Definition: subworld.cpp:3
virtual bool is_master()
Definition: bbs.cpp:125
char * pickle_ret_
Definition: bbsimpl.h:58
virtual double time()
Definition: bbs.cpp:133
static bool done_
Definition: bbsimpl.h:61
virtual int look_take_todo()=0
virtual void post_todo(int parentid)=0
virtual bool working(int &id, double &x, int &userid)
Definition: bbs.cpp:364
virtual void post(const char *)=0
virtual void take(const char *)=0
virtual double upkdouble()=0
size_t pickle_ret_size_
Definition: bbsimpl.h:59
virtual bool look(const char *)=0
virtual char * upkpickle(size_t *)=0
virtual void start()
Definition: bbs.cpp:506
virtual void pkdouble(double)=0
virtual void done()
Definition: bbs.cpp:487
virtual void context()
Definition: bbs.cpp:344
virtual int master_take_result(int pid)
Definition: bbs.cpp:359
double t
Definition: cvodeobj.cpp:59
double hoc_ac_
Definition: hoc_init.cpp:397
#define assert(ex)
Definition: hocassrt.h:32
#define id
Definition: md1redef.h:33
#define i
Definition: md1redef.h:12
static double nrnmpi_wtime()
Definition: multisplit.cpp:55
static int nrnmpi_use
Definition: multisplit.cpp:48
#define printf
Definition: mwprefix.h:26
int const size_t const size_t n
Definition: nrngsl.h:11
int nrnmpi_myid
int nrnmpi_numprocs
int nrnmpi_numprocs_bbs
int nrnmpi_myid_bbs
#define debug
Definition: bbs.cpp:32
int nrn_global_argc
Definition: hoc.cpp:29
static int etaskcnt
Definition: bbs.cpp:37
char ** nrn_global_argv
Definition: hoc.cpp:30
static double total_exec_time
Definition: bbs.cpp:38
static double worker_take_time
Definition: bbs.cpp:39
static double userid(void *v)
Definition: ocbbs.cpp:227
#define cnt
Definition: spt2queue.cpp:19
#define key
Definition: spt2queue.cpp:20