#include <../../nrnconf.h> #include "nrnmpi.h" #include "bbsconf.h" #include #include #include #include "oc2iv.h" #include "bbs.h" #include "bbslocal.h" #if defined(HAVE_PVM3_H) || defined(NRNMPI) #include "bbsdirect.h" #include "bbsrcli.h" #endif #if defined(HAVE_TMS) && !NRNMPI #include #include #include static struct tms tmsbuf, tms_start_; static clock_t starttime; #endif extern "C" { extern int nrn_global_argc; extern char** nrn_global_argv; }; boolean BBSImpl::is_master_ = false; boolean BBSImpl::started_ = false; boolean BBSImpl::done_ = false; #undef debug #define debug BBSImpl::debug_ int BBSImpl::debug_ = 0; boolean BBSImpl::use_pvm_; int BBSImpl::mytid_; static int etaskcnt; static double total_exec_time; static double worker_take_time; BBS::BBS() { init(-1); } BBS::BBS(int n) { init(n); } #ifdef HAVE_PVM3_H void BBS::init(int n) { //printf("BBS::init\n"); //fflush(stdout); if (!BBSImpl::started_) { if (nrnmpi_numprocs < 2) { // master first time call nrnmpi_numprocs = 1; // the minimum if (n == 0) { // demand local BBSImpl::use_pvm_ = false; }else{ BBSDirect::check_pvm(); } BBSImpl::is_master_ = true; }else{ BBSImpl::use_pvm_ = true; } } if (BBSImpl::use_pvm_ == false) { //printf("new BBSLocal\n"); //fflush(stdout); impl_ = new BBSLocal(); }else{ if (BBSImpl::is_master_) { //printf("new BBSDirect\n"); //fflush(stdout); impl_ = new BBSDirect(); }else{ //printf("new BBSClient\n"); //fflush(stdout); impl_ = new BBSClient(); } } } #else //!HAVE_PVM3_H #if NRNMPI void BBS::init(int) { if (nrnmpi_use == 0) { BBSImpl::is_master_ = true; BBSImpl::use_pvm_ = false; impl_ = new BBSLocal(); return; } if (!BBSImpl::started_) { BBSImpl::use_pvm_ = false; BBSImpl::is_master_ = (nrnmpi_myid == 0) ? true : false; //printf("%d BBS::init is_master=%d\n", nrnmpi_myid, BBSImpl::is_master_); } // Just as with PVM which stored buffers on the bulletin board // so we have the following files to store MPI_PACKED buffers // on the bulletin board. It would be possible to factor out // the pvm stuff and we may do that later but for now we // start with copies of the four files that worked for PVM // and convert to the nrnmpi functions implemented in // ../nrnmpi // The four files are // bbsclimpi.cpp - mpi remote client BBSClient from bbsrcli.cpp // bbsdirectmpi.cpp - mpi master client BBSDirect from bbsdirect.cpp // bbssrvmpi.cpp - mpi server portion to remote client from master bbs // BBSDirectServer derived from bbssrv.cpp // bbslsrvmpi.cpp - mpi master bbs portion of BBSDirectServer // derived from bbslsrv2.cpp // We reuse the .h files of these. if (BBSImpl::is_master_) { impl_ = new BBSDirect(); }else{ impl_ = new BBSClient(); } } #else // !NRNMPI void BBS::init(int) { if (!BBSImpl::started_) { BBSImpl::use_pvm_ = false; BBSImpl::is_master_ = true; } impl_ = new BBSLocal(); } #endif // !NRNMPI #endif // !HAVE_PVM3_H BBSImpl::BBSImpl() { wait_time_ = 0.; send_time_ = 0.; integ_time_ = 0.; working_id_ = 0; n_ = 0; } BBS::~BBS() { delete impl_; } BBSImpl::~BBSImpl() { } boolean BBS::is_master() { return BBSImpl::is_master_; } int BBS::nhost() { return nrnmpi_numprocs; } int BBS::myid() { return nrnmpi_myid; } boolean BBSImpl::is_master() { return is_master_; } double BBS::time() { return impl_->time(); } double BBSImpl::time() { #if NRNMPI return nrnmpi_wtime(); #else #ifdef HAVE_TMS return double(times(&tmsbuf))/100.; #else return 0.; #endif #endif } double BBS::wait_time() { return impl_->wait_time_; } double BBS::integ_time() { return impl_->integ_time_; } double BBS::send_time() { return impl_->send_time_; } void BBS::add_wait_time(double st) { impl_->wait_time_ += impl_->time() - st; } void BBS::perror(const char* s) { impl_->perror(s); } void BBSImpl::perror(const char*) { } int BBS::upkint() { int i = impl_->upkint(); if (debug) { printf("upkint %d\n", i); } return i; } double BBS::upkdouble() { double d = impl_->upkdouble(); if (debug) { printf("upkdouble %g\n", d); } return d; } void BBS::upkvec(int n, double* px) { impl_->upkvec(n, px); if (debug) { printf("upkvec %d\n", n); } } char* BBS::upkstr() { char* s = impl_->upkstr(); if (debug) { printf("upkstr |%s|\n", s); } return s; } void BBS::pkbegin() { if (debug) { printf("pkbegin\n"); } impl_->pkbegin(); } void BBS::pkint(int i) { if (debug) { printf("pkint %d\n", i); } impl_->pkint(i); } void BBS::pkdouble(double x) { if (debug) { printf("pkdouble %g\n", x); } impl_->pkdouble(x); } void BBS::pkvec(int n, double* px) { if (debug) { printf("pkdouble %d\n", n); } impl_->pkvec(n, px); } void BBS::pkstr(const char* s) { if (debug) { printf("pkstr |%s|\n", s); } impl_->pkstr(s); } #if 0 // for now all todo messages are of the three item form // tid // gid // id // "stmt" // the latter should set hoc_ac_ if the return value is important // right now every arg must be literal, we'll handle variables later. // eg the following should work. // n = 1000 x = 0 for i=1,n { x += i } hoc_ac_ = x // although it may makes sense to send the result directly to the // tid for now we just put it back onto the mailbox in the form // message: "result tid gid" with two items, i.e. id, hoc_ac_ #endif #if 0 // the todo management has been considerably modified to support // a priority queue style for the order in which tasks are executed. // Now the server manages the task id. Given a task id, it knows the // parent task id. // The working_id is always non-trivial on worker machines, and is // only 0 on the master when dealing with a submission at the top level // post_todo(working_id_) message is the statement, here the working_id is // the parent of the future stmt task. // take_todo: message is the statement. return is the id of this task // post_result(id) message is return value. // the result will be retrieved relative to the submitting working_id_ // look_take_result(working_id_) message is the return value. the return // value of this call is the id of the task that computed the return. #endif // BBSImpl::execute_helper() in ocbbs.c void BBSImpl::execute(int id) { // assumes a "_todo" message in receive buffer ++etaskcnt; double st, et; int userid; char* s; int i; int save_id = working_id_; working_id_ = id; st = time(); if (debug_) { printf("execute begin %g: working_id_=%d\n",st, working_id_); } userid = upkint(); hoc_ac_ = double(id); execute_helper(); //builds and execute hoc statement et = time() - st; total_exec_time += et; if (debug) { printf("execute end elapsed %g: working_id_=%d hoc_ac_=%g\n", et, working_id_, hoc_ac_); } pkbegin(); pkint(userid); pkdouble(hoc_ac_); post_result(working_id_); working_id_ = save_id; } int BBS::submit(int userid) { return impl_->submit(userid); } int BBSImpl::submit(int userid) { // userid was the first item packed ++n_; if (debug) { printf("submit n_= %d for working_id=%d userid=%d\n",n_, working_id_, userid); } if (userid < 0) { save_args(-userid); }else{ post_todo(working_id_); } return userid; } void BBS::context() { impl_->context(); } void BBSImpl::context() { printf("can't execute BBS::context on a worker\n"); exit(1); } boolean BBS::working(int& id, double& x, int& userid) { return impl_->working(id, x, userid); } boolean BBSImpl::working(int& id, double& x, int& userid) { int cnt=0; double t; if (n_ <= 0) { if (debug) { printf("working n_=%d: return false\n", n_); } return false; } if(debug) { t = time(); } for (;;) { ++cnt; if ((id = look_take_result(working_id_)) != 0) { userid = upkint(); x = upkdouble(); --n_; if (debug) { printf("working n_=%d: after %d try elapsed %g sec got result for %d id=%d x=%g\n", n_, cnt, time()-t, working_id_, id, x); } if (userid < 0) { userid = -userid; return_args(userid); } return true; }else if ( (id = look_take_todo()) != 0) { if (debug) { printf("working: no result for %d but did get _todo id=%d\n", working_id_, id); } execute(id); } } }; void BBS::worker() { impl_->worker(); } void BBSImpl::worker() { // forever request and execute commands double st, et; int id; if (!is_master()) { for (;;) { st = time(); id = take_todo(); et = time() - st; worker_take_time += et; execute(id); } } } void BBS::post(const char* key) { if (debug) { printf("post: |%s|\n", key); } impl_->post(key); } boolean BBS::look_take(const char* key) { boolean b = impl_->look_take(key); if (debug) { printf("look_take |%s| return %d\n", key, b); } return b; } boolean BBS::look(const char* key) { boolean b = impl_->look(key); if (debug) { printf("look |%s| return %d\n", key, b); } return b; } void BBS::take(const char* key) { // blocking double t; if (debug) { t = time(); printf("begin take |%s| at %g\n", key, t); } impl_->take(key); if (debug) { printf("end take |%s| elapsed %g from %g\n", key, time()-t, t); } } void BBS::done() { impl_->done(); } void BBSImpl::done() { if (done_) { return; } done_ = true; #ifdef HAVE_TMS clock_t elapsed = times(&tmsbuf) - starttime; printf("%d tasks in %g seconds. %g seconds waiting for tasks\n", etaskcnt, total_exec_time, worker_take_time); printf("user=%g sys=%g elapsed=%g %g%%\n", (double)(tmsbuf.tms_utime - tms_start_.tms_utime)/100, (double)(tmsbuf.tms_stime - tms_start_.tms_stime)/100, (double)(elapsed)/100, 100.*(double)(tmsbuf.tms_utime - tms_start_.tms_utime)/(double)elapsed ); #endif } void BBSImpl::start() { if (started_) { return; } started_ = 1; #ifdef HAVE_TMS starttime = times(&tms_start_); #endif }