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 
93  runworker_called_ = 0;
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 
165 int BBS::upkint() {
166  int i = impl_->upkint();
167  if (debug) {
168  printf("upkint %d\n", i);
169  }
170  return i;
171 }
172 
173 double BBS::upkdouble() {
174  double d = impl_->upkdouble();
175  if (debug) {
176  printf("upkdouble %g\n", d);
177  }
178  return d;
179 }
180 
181 void BBS::upkvec(int n, double* px) {
182  impl_->upkvec(n, px);
183  if (debug) {
184  printf("upkvec %d\n", n);
185  }
186 }
187 
188 char* BBS::upkstr() {
189  char* s = impl_->upkstr();
190  if (debug) {
191  printf("upkstr |%s|\n", s);
192  }
193  return s;
194 }
195 
196 char* BBS::upkpickle(size_t* n) {
197  char* s = impl_->upkpickle(n);
198  if (debug) {
199  printf("upkpickle %lu |%s|\n", *n, s);
200  }
201  return s;
202 }
203 
204 void BBS::pkbegin() {
205  if (debug) {
206  printf("pkbegin\n");
207  }
208  impl_->pkbegin();
209 }
210 
211 void BBS::pkint(int i) {
212  if (debug) {
213  printf("pkint %d\n", i);
214  }
215  impl_->pkint(i);
216 }
217 
218 void BBS::pkdouble(double x) {
219  if (debug) {
220  printf("pkdouble %g\n", x);
221  }
222  impl_->pkdouble(x);
223 }
224 
225 void BBS::pkvec(int n, double* px) {
226  if (debug) {
227  printf("pkdouble %d\n", n);
228  }
229  impl_->pkvec(n, px);
230 }
231 
232 void BBS::pkstr(const char* s) {
233  if (debug) {
234  printf("pkstr |%s|\n", s);
235  }
236  impl_->pkstr(s);
237 }
238 
239 void BBS::pkpickle(const char* s, size_t n) {
240  if (debug) {
241  printf("pkpickle %lu |%s|\n", n, s);
242  }
243  impl_->pkpickle(s, n);
244 }
245 
246 #if 0
247 // for now all todo messages are of the three item form
248 // tid
249 // gid
250 // id
251 // "stmt"
252 // the latter should set hoc_ac_ if the return value is important
253 // right now every arg must be literal, we'll handle variables later.
254 // eg the following should work.
255 // n = 1000 x = 0 for i=1,n { x += i } hoc_ac_ = x
256 // although it may makes sense to send the result directly to the
257 // tid for now we just put it back onto the mailbox in the form
258 // message: "result tid gid" with two items, i.e. id, hoc_ac_
259 // Modified 11/30/09. To allow a return of a (pickled) PythonObject
260 // for the case when the execution is for a Python Callable the return
261 // message result now has three items, i.e. id, rtype, hoc_ac or pickled
262 // PyObject. rtype = 0 means hoc_ac, rtype = 1 means pickled PyObject.
263 // execute_helper returns the pickle string or (char*)0.
264 #endif
265 #if 0
266 // the todo management has been considerably modified to support
267 // a priority queue style for the order in which tasks are executed.
268 // Now the server manages the task id. Given a task id, it knows the
269 // parent task id.
270 // The working_id is always non-trivial on worker machines, and is
271 // only 0 on the master when dealing with a submission at the top level
272 // post_todo(working_id_) message is the statement, here the working_id is
273 // the parent of the future stmt task.
274 // take_todo: message is the statement. return is the id of this task
275 // post_result(id) message is return value.
276 // the result will be retrieved relative to the submitting working_id_
277 // look_take_result(working_id_) message is the return value. the return
278 // value of this call is the id of the task that computed the return.
279 #endif
280 
281 // BBSImpl::execute_helper() in ocbbs.cpp
282 
283 void BBSImpl::execute(int id) { // assumes a "_todo" message in receive buffer
284  ++etaskcnt;
285  double st, et;
286  int userid;
287  char* rs;
288  char* s;
289  size_t n;
290  int i;
291  int save_id = working_id_;
292  int save_n = n_;
293  working_id_ = id;
294  n_ = 0;
295  st = time();
296  if (debug_) {
297 printf("execute begin %g: working_id_=%d\n",st, working_id_);
298  }
299  userid = upkint();
300  int wid = upkint();
301  hoc_ac_ = double(id);
302  rs = execute_helper(&n, id); //builds and execute hoc statement
303  et = time() - st;
304  total_exec_time += et;
305  if (debug) {
306 printf("execute end elapsed %g: working_id_=%d hoc_ac_=%g\n",
307 et, working_id_, hoc_ac_);
308  }
309  pkbegin();
310  pkint(userid);
311  pkint(wid);
312  pkint(rs?1:0);
313  if (!rs) {
314  pkdouble(hoc_ac_);
315  }else{
316  pkpickle(rs, n);
317  delete [] rs;
318  }
319  working_id_ = save_id;
320  n_ = save_n;
321  post_result(id);
322 }
323 
324 int BBS::submit(int userid) {
325  return impl_->submit(userid);
326 }
327 
329  // userid was the first item packed
330  ++n_;
331  if (debug) {
332 printf("submit n_= %d for working_id=%d userid=%d\n",n_, working_id_, userid);
333  }
334  if (userid < 0) {
335  save_args(userid);
336  }else{
337  post_todo(working_id_);
338  }
339  return userid;
340 }
341 
342 void BBS::context() {
343  impl_->context();
344 }
345 
347  printf("can't execute BBS::context on a worker\n");
348  exit(1);
349 }
350 
351 bool BBS::working(int& id, double& x, int& userid) {
352  return impl_->working(id, x, userid);
353 }
354 
355 void BBS::master_works(int flag) {
356  if (impl_->is_master() && nrnmpi_numprocs_bbs > 1) {
357  impl_->master_works_ = flag ? true : false;
358  }
359 }
360 
362  assert(0);
363  return 0;
364 }
365 
366 bool BBSImpl::working(int& id, double& x, int& userid) {
367  int cnt=0;
368  int rtype;
369  double t;
370  if (n_ <= 0) {
371  if (debug) {
372  printf("working n_=%d: return false\n", n_);
373  }
374  return false;
375  }
376  if(debug) {
377  t = time();
378  }
379  for (;;) {
380  ++cnt;
381  if (master_works_) {
382  id = look_take_result(working_id_);
383  }else{
384  id = master_take_result(working_id_);
385  }
386  if (id != 0) {
387  userid = upkint();
388  int wid = upkint();
389  rtype = upkint();
390  if (rtype == 0) {
391  x = upkdouble();
392  }else{
393  assert(rtype == 1);
394  x = 0.0;
395  if (pickle_ret_) {
396  delete [] pickle_ret_;
397  }
398  pickle_ret_ = upkpickle(&pickle_ret_size_);
399  }
400  --n_;
401  if (debug) {
402 printf("working n_=%d: after %d try elapsed %g sec got result for %d id=%d x=%g\n",
403 n_, cnt, time()-t, working_id_, id, x);
404  }
405  if (userid < 0) {
406  return_args(userid);
407  }
408  return true;
409  }else if ( (id = look_take_todo()) != 0) {
410  if (debug) {
411 printf("working: no result for %d but did get _todo id=%d\n", working_id_, id);
412  }
413  execute(id);
414  }
415  }
416 };
417 
418 void BBS::worker() {
420  impl_->worker();
421 }
422 
424  // forever request and execute commands
425  double st, et;
426  int id;
427  if (!is_master()) {
428  if (nrnmpi_myid_bbs == -1) { // wait for message from
429  for(;;) { // the proper nrnmpi_myid == 0
430  subworld_worker_execute();
431  }
432  }
433  for (;;) {
434  st = time();
435  id = take_todo();
436  et = time() - st;
437  worker_take_time += et;
438  execute(id);
439  }
440  }
441 }
442 
443 void BBS::post(const char* key) {
444  if (debug) {
445  printf("post: |%s|\n", key);
446  }
447  impl_->post(key);
448 }
449 
450 bool BBS::look_take(const char* key) {
451  bool b = impl_->look_take(key);
452  if (debug) {
453  printf("look_take |%s| return %d\n", key, b);
454  }
455  return b;
456 }
457 
458 bool BBS::look(const char* key) {
459  bool b = impl_->look(key);
460  if (debug) {
461  printf("look |%s| return %d\n", key, b);
462  }
463  return b;
464 }
465 
466 void BBS::take(const char* key) { // blocking
467  double t;
468  if (debug) {
469  t = time();
470  printf("begin take |%s| at %g\n", key, t);
471  }
472  impl_->take(key);
473  if (debug) {
474  printf("end take |%s| elapsed %g from %g\n", key, time()-t, t);
475  }
476 }
477 
478 void BBS::done() {
479  if (impl_->runworker_called_) {
480  impl_->done();
481  }
482 }
483 
485  if (done_) { return; }
486  done_ = true;
487 #ifdef HAVE_TMS
488  clock_t elapsed = times(&tmsbuf) - starttime;
489  printf("%d tasks in %g seconds. %g seconds waiting for tasks\n",
491  printf("user=%g sys=%g elapsed=%g %g%%\n",
492  (double)(tmsbuf.tms_utime - tms_start_.tms_utime)/100,
493  (double)(tmsbuf.tms_stime - tms_start_.tms_stime)/100,
494  (double)(elapsed)/100,
495  100.*(double)(tmsbuf.tms_utime - tms_start_.tms_utime)/(double)elapsed
496  );
497 #endif
498 }
499 
501  if (started_) { return; }
502  started_ = 1;
503 #ifdef HAVE_TMS
504  starttime = times(&tms_start_);
505 #endif
506 }
static bool is_master_
Definition: bbsimpl.h:59
int upkint()
Definition: bbs.cpp:165
virtual int upkint()=0
static double userid(void *v)
Definition: ocbbs.cpp:225
#define assert(ex)
Definition: hocassrt.h:26
return true
Definition: savstate.cpp:357
char ** nrn_global_argv
Definition: hoc.cpp:30
void execute(Inst *p)
Definition: code.cpp:2651
BBSImpl * impl_
Definition: bbs.h:72
virtual void done()
Definition: bbs.cpp:484
virtual ~BBS()
Definition: bbs.cpp:103
double integ_time_
Definition: bbsimpl.h:55
bool working(int &id, double &x, int &userid)
Definition: bbs.cpp:351
int nrnmpi_myid_bbs
double send_time_
Definition: bbsimpl.h:56
static int mytid_
Definition: bbsimpl.h:61
virtual void start()
Definition: bbs.cpp:500
virtual int submit(int userid)
Definition: bbs.cpp:328
static bool master_works_
Definition: bbsimpl.h:63
char * upkpickle(size_t *size)
Definition: bbs.cpp:196
virtual ~BBSImpl()
Definition: bbs.cpp:107
int nrnmpi_numprocs_bbs
static int nrnmpi_use
Definition: multisplit.cpp:48
virtual void pkstr(const char *)=0
virtual void execute(int id)
Definition: bbs.cpp:283
virtual double time()
Definition: bbs.cpp:133
void pkbegin()
Definition: bbs.cpp:204
void context()
Definition: bbs.cpp:342
int submit(int userid)
Definition: bbs.cpp:324
bool is_master()
Definition: bbs.cpp:113
virtual void pkvec(int, double *)=0
virtual void post(const char *)=0
static double total_exec_time
Definition: bbs.cpp:38
void upkvec(int n, double *px)
Definition: bbs.cpp:181
virtual char * upkstr()=0
void done()
Definition: bbs.cpp:478
#define debug
Definition: bbs.cpp:32
void perror(const char *)
Definition: bbs.cpp:158
static int debug_
Definition: bbsimpl.h:62
void init(int)
Definition: bbs.cpp:83
int runworker_called_
Definition: bbsimpl.h:52
int const size_t const size_t n
Definition: nrngsl.h:12
void pkdouble(double)
Definition: bbs.cpp:218
int nrnmpi_numprocs
virtual void pkpickle(const char *, size_t)=0
_CONST char * s
Definition: system.cpp:74
void pkstr(const char *)
Definition: bbs.cpp:232
void add_wait_time(double)
Definition: bbs.cpp:154
bool look_take(const char *)
Definition: bbs.cpp:450
#define printf
Definition: mwprefix.h:26
virtual void pkbegin()=0
void take(const char *)
Definition: bbs.cpp:466
virtual void context()
Definition: bbs.cpp:346
BBSImpl()
Definition: bbs.cpp:92
#define cnt
Definition: spt2queue.cpp:19
#define key
Definition: spt2queue.cpp:20
static double worker_take_time
Definition: bbs.cpp:39
void pkpickle(const char *, size_t size)
Definition: bbs.cpp:239
virtual void perror(const char *)
Definition: bbs.cpp:162
double wait_time()
Definition: bbs.cpp:145
double upkdouble()
Definition: bbs.cpp:173
int nhost()
Definition: bbs.cpp:117
void pkint(int)
Definition: bbs.cpp:211
virtual bool look(const char *)=0
void worker()
Definition: bbs.cpp:418
double send_time()
Definition: bbs.cpp:151
double integ_time()
Definition: bbs.cpp:148
virtual void pkdouble(double)=0
virtual void take(const char *)=0
int nrnmpi_myid
virtual bool working(int &id, double &x, int &userid)
Definition: bbs.cpp:366
BBS()
Definition: bbs.cpp:41
virtual int master_take_result(int pid)
Definition: bbs.cpp:361
void pkvec(int n, double *px)
Definition: bbs.cpp:225
#define i
Definition: md1redef.h:12
#define id
Definition: md1redef.h:33
static double nrnmpi_wtime()
Definition: multisplit.cpp:55
static bool started_
Definition: bbsimpl.h:60
virtual double upkdouble()=0
virtual void pkint(int)=0
int nrn_global_argc
Definition: hoc.cpp:29
double time()
Definition: bbs.cpp:129
double hoc_ac_
Definition: hoc_init.cpp:261
virtual bool is_master()
Definition: bbs.cpp:125
int myid()
Definition: bbs.cpp:121
void master_works(int flag)
Definition: bbs.cpp:355
double t
Definition: init.cpp:123
char * upkstr()
Definition: bbs.cpp:188
virtual bool look_take(const char *)=0
static bool done_
Definition: bbsimpl.h:60
double wait_time_
Definition: bbsimpl.h:54
static int etaskcnt
Definition: bbs.cpp:37
virtual void upkvec(int, double *)=0
virtual void worker()
Definition: bbs.cpp:423
bool look(const char *)
Definition: bbs.cpp:458
void post(const char *)
Definition: bbs.cpp:443
virtual char * upkpickle(size_t *)=0