From 901f8ddb515e2e496a34499104ac906a0983b18d Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Tue, 7 Apr 2020 19:07:32 +0200 Subject: [PATCH 001/202] Improved the mapping_string.sh script so that now it generates two other variables FF_NUM_CORES and FF_NUM_REAL_CORES in the config.hpp file. This way, ff_numCores and ff_realNumCores (which are costly functions) can use the two new variables, if they are set, instead of accessing the /proc FS each time. --- ff/config.hpp | 31 +++++++++++++++++++++++++++++-- ff/mapping_string.sh | 30 ++++++++++++++++++++++++++---- ff/mapping_utils.hpp | 2 ++ ff/parallel_for_internals.hpp | 2 +- tests/test_combine.cpp | 8 ++++++-- tests/test_ofarm2.cpp | 2 +- 6 files changed, 65 insertions(+), 10 deletions(-) diff --git a/ff/config.hpp b/ff/config.hpp index dd8a824b..999064e2 100644 --- a/ff/config.hpp +++ b/ff/config.hpp @@ -59,15 +59,22 @@ * 'mapping_string.sh', which returns a suitable string that can be * copy-paste in the FF_MAPPING_STRING preprocessor variable. * Note that, if you wish (and trust it) the script can modify the - * config.hpp file for you. Example: + * config.hpp file for you. The script also sets the FF_NUM_CORES and + * FF_NUM_REAL_CORES variables. + * Example: * > ./mapping_string.sh - * > "0,2,1,3" + * > FF_MAPPING_STRING="0,2,1,3" + * > FF_NUM_CORES=4 + * > FF_NUM_REAL_CORES=2 * > Do you want that I change the ./config.hpp file for you? (y/N) y * > This is the new FF_MAPPING_STRING variable in the ./config.hpp file: * > #if !defined MAPPING_STRING * > #define FF_MAPPING_STRING "0,2,1,3" * > #else + * > ... * + */ +/* * NOTE: if FF_MAPPING_STRING is "" (default), FastFlow executes a linear * mapping of threads. */ @@ -76,6 +83,26 @@ #else #define FF_MAPPING_STRING MAPPING_STRING #endif +/* + * It is the number of the logical cores of the machine. + * NOTE: if FF_NUM_CORES is -1 (default), FastFlow will use ff_numCores() + * (which is a costly function). + */ +#if !defined NUM_CORES +#define FF_NUM_CORES -1 +#else +#define FF_NUM_CORES NUM_CORES +#endif +/* + * It is the number of the physical cores of the machine. + * NOTE: if FF_NUM_REAL_CORES is -1 (default), FastFlow will use + * ff_realNumCores() (which is a costly function) + */ +#if !defined NUM_REAL_CORES +#define FF_NUM_REAL_CORES -1 +#else +#define FF_NUM_REAL_CORES NUM_REAL_CORES +#endif #if defined(FF_BOUNDED_BUFFER) diff --git a/ff/mapping_string.sh b/ff/mapping_string.sh index 38f4f7d6..a5aac0e4 100755 --- a/ff/mapping_string.sh +++ b/ff/mapping_string.sh @@ -8,6 +8,9 @@ # machine that are topologically contiguous, i.e the first context of the # first core, the first context of the second core, and so on up to the # last context of the last core. +# It also can set two variables: +# FF_NUM_CORES +# FF_NUM_REAL_CORES # # Example: Given the following OS numbering for the logical cores # @@ -20,6 +23,8 @@ # # the string produced is: # "0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15". +# FF_NUM_CORES=16 +# FF_NUM_REAL_CORES=8 # topocmd=lstopo # or lstopo-no-graphics or hwloc-ls @@ -49,8 +54,9 @@ for((j=0;j<$nway;++j)); do str+=${V[j]} done string=${str::-1} # remove the last comma -echo "The string is:" -echo \"$string\" +echo "FF_MAPPING_STRING=\"$string\"" +echo "FF_NUM_CORES=$logical" +echo "FF_NUM_REAL_CORES=$physical" read -p "Do you want that I change the ./config.hpp file for you? (y/N) " -n 1 -r echo @@ -59,11 +65,27 @@ then sed -i -e "s/#define FF_MAPPING_STRING \"\"/#define FF_MAPPING_STRING \"$string\"/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_MAPPING_STRING variable in the ./config.hpp file:" - grep -1 "^#define FF_MAPPING_STRING \"" config.hpp + echo -e "\e[1m $(grep -m1 "^#define FF_MAPPING_STRING \"" config.hpp) \e[0m" else - echo "something went wrong...." + echo "something went wrong when replacing the variable FF_MAPPING_STRING...." exit 1 fi + sed -i -e "s/#define FF_NUM_CORES -1/#define FF_NUM_CORES $logical/1" ./config.hpp + if [ $? -eq 0 ]; then + echo "This is the new FF_NUM_CORES variable in the ./config.hpp file:" + echo -e "\e[1m $(grep -m1 "^#define FF_NUM_CORES " config.hpp) \e[0m" + else + echo "something went wrong when replacing the variable FF_NUM_CORES...." + exit 1 + fi + sed -i -e "s/#define FF_NUM_REAL_CORES -1/#define FF_NUM_REAL_CORES $physical/1" ./config.hpp + if [ $? -eq 0 ]; then + echo "This is the new FF_NUM_REAL_CORES variable in the ./config.hpp file:" + echo -e "\e[1m $(grep -m1 "^#define FF_NUM_REAL_CORES " config.hpp) \e[0m" + else + echo "something went wrong when replacing the variable FF_NUM_REAL_CORES...." + exit 1 + fi else echo "Ok, nothing has been changed" fi diff --git a/ff/mapping_utils.hpp b/ff/mapping_utils.hpp index e61e2589..7c43aa89 100644 --- a/ff/mapping_utils.hpp +++ b/ff/mapping_utils.hpp @@ -143,6 +143,7 @@ static inline unsigned long ff_getCpuFreq() { * \return An integer value showing the number of cores. */ static inline ssize_t ff_numCores() { + if (FF_NUM_CORES != -1) return FF_NUM_CORES; ssize_t n=-1; #if defined(__linux__) #if defined(MAMMUT) @@ -210,6 +211,7 @@ static inline ssize_t ff_numCores() { * \return An integer value showing the number of cores. */ static inline ssize_t ff_realNumCores() { + if (FF_NUM_REAL_CORES != -1) return FF_NUM_REAL_CORES; ssize_t n=-1; #if defined(_WIN32) n = 2; // Not yet implemented diff --git a/ff/parallel_for_internals.hpp b/ff/parallel_for_internals.hpp index e55180bc..39317ddc 100644 --- a/ff/parallel_for_internals.hpp +++ b/ff/parallel_for_internals.hpp @@ -978,7 +978,7 @@ class ff_forall_farm: public ff_farm { if (ff_farm::prepare() < 0) error("running base forall farm(2)\n"); - // NOTE: the warmup phase has to be done, if not now latern on. + // NOTE: the warmup phase has to be done, if not now later on. // The run_then_freeze method will fail if skipwarmup is true. if (!skipwarmup) { auto r=-1; diff --git a/tests/test_combine.cpp b/tests/test_combine.cpp index 1e1ac6a4..a5b6eaec 100644 --- a/tests/test_combine.cpp +++ b/tests/test_combine.cpp @@ -109,6 +109,7 @@ int main() { const secondStage2 _3; const secondStage3 _4; const thirdStage _5; +#if 0 { auto comb = combine_nodes(_1, combine_nodes(_2, combine_nodes(_3, combine_nodes(_4, _5)))); if (comb.run_and_wait_end()<0) @@ -203,11 +204,15 @@ int main() { std::cout << "TEST10 DONE Time = " << pipe.ffwTime() << " ms\n"; } usleep(500000); +#endif { // testing multi-input + multi-output combined struct miStage:ff_minode_t { - long* svc(long* in) { return in;} + long* svc(long* in) { + printf("MULTI INPUT input channels %ld\n", get_num_inchannels()); + return in; + } void eosnotify(ssize_t) { printf("MULTI INPUT %ld, eosnotify\n", get_my_id()); } @@ -234,7 +239,6 @@ int main() { error("running pipe\n"); std::cout << "TEST11 DONE\n"; } - } return 0; diff --git a/tests/test_ofarm2.cpp b/tests/test_ofarm2.cpp index d29e1a7e..5c1099cf 100644 --- a/tests/test_ofarm2.cpp +++ b/tests/test_ofarm2.cpp @@ -135,7 +135,7 @@ int main(int argc, char * argv[]) { std::vector w; for(int i=0;i Date: Thu, 16 Apr 2020 09:54:24 +0200 Subject: [PATCH 002/202] - more checks added to verify the correct usage of the parallel_for/reduce methods of the ParallelFor* classes - more one-shot functions (i.e, static functions that do not use the ParallelFor* class) added for the parallel_for/parallel_reduce - more test for the BLOCKING_MODE (minor fixes to the new protocol) - some polishing to the code here and there --- ff/all2all.hpp | 5 +- ff/combine.hpp | 5 +- ff/farm.hpp | 152 +++++++++++++++++++++------------- ff/gt.hpp | 5 +- ff/lb.hpp | 5 +- ff/multinode.hpp | 12 +-- ff/node.hpp | 21 +++-- ff/parallel_for.hpp | 94 +++++++++++++++------ ff/parallel_for_internals.hpp | 24 +++++- ff/pipeline.hpp | 24 ++++-- tests/test_accelerator.cpp | 4 +- tests/test_accelerator2.cpp | 15 +--- tests/test_farm+pipe2.cpp | 2 +- tests/test_multi_input3.cpp | 52 ++++++------ 14 files changed, 269 insertions(+), 151 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index d94eab3e..9ceaf440 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -653,10 +653,11 @@ class ff_a2a: public ff_node { return true; } void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { + pthread_cond_t *&c, + bool canoverwrite=false) { size_t nworkers2 = workers2.size(); for(size_t i=0;iset_output_blocking(m,c); + workers2[i]->set_output_blocking(m,c, canoverwrite); } } diff --git a/ff/combine.hpp b/ff/combine.hpp index 0f11e54d..e77c8208 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -648,8 +648,9 @@ class ff_comb: public ff_minode { return comp_nodes[1]->init_output_blocking(m,c); } void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { - comp_nodes[1]->set_output_blocking(m,c); + pthread_cond_t *&c, + bool canoverwrite=false) { + comp_nodes[1]->set_output_blocking(m,c, canoverwrite); } // the following calls are needed because a composition diff --git a/ff/farm.hpp b/ff/farm.hpp index 7cb7ebe2..96f0f177 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -264,38 +264,21 @@ class ff_farm: public ff_node { setgt(_gt, true); } } - + // accelerator if (has_input_channel) { if (create_input_buffer(in_buffer_entries, fixedsize)<0) { error("FARM, creating input buffer\n"); return -1; } - if (!init_input_blocking(p_cons_m,p_cons_c)) { - error("FARM, init input blocking mode for accelerator\n"); - return -1; - } - // blocking stuff ------------- - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!ff_node::init_output_blocking(m,c)) { - error("FARM, init output blocking mode for accelerator\n"); - return -1; - } - // NOTE: the queue is forced to be unbounded - if (create_output_buffer(out_buffer_entries, false)<0) return -1; - - if (!ff_node::init_input_blocking(m,c)) { - error("FARM, add_collector, init output blocking mode for accelerator\n"); - return -1; - } - set_output_blocking(m,c); - if (!init_output_blocking(m,c)) { - error("FARM, add_collector, init input blocking mode for accelerator\n"); + if (hasCollector()) { + // NOTE: the queue is forced to be unbounded + if (create_output_buffer(out_buffer_entries, false)<0) return -1; } - // ---------------------------- } + + for(size_t i=0;imasterworker()) { bool last_multioutput = [&]() { - // WARNING: we consider homogeneous workers! - if (lb->parallel_workers) { - svector w; - workers[0]->get_out_nodes(w); - if (w[0]->isMultiOutput()) return true; - return false; - } - if (workers[0]->isMultiOutput()) return true; - return false; + // WARNING: we consider homogeneous workers! + if (lb->parallel_workers) { + svector w; + workers[0]->get_out_nodes(w); + if (w[0]->isMultiOutput()) return true; + return false; + } + if (workers[0]->isMultiOutput()) return true; + return false; } (); pthread_mutex_t *m = NULL; @@ -614,15 +597,58 @@ class ff_farm: public ff_node { } for(size_t j=0;j w; - if (last_multioutput) + if (last_multioutput) { workers[j]->get_out_nodes_feedback(w); - else + if (w.size() == 0) + workers[j]->get_out_nodes(w); + } else workers[j]->get_out_nodes(w); assert(w.size()>0); + // NOTE: it is possible that we have to overwrite the + // p_cons_* variables that could have been set in the + // wrap_around method. This can happen when the last + // stage is multi-output and it has a feedback channel + // and one (or more) forward channels to the next stage. for(size_t i=0;iset_output_blocking(m,c); + w[i]->set_output_blocking(m,c, true); } - } + } + + if (has_input_channel) { + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!init_input_blocking(m,c)) { + error("FARM, init input blocking\n"); + return -1; + } + // this is to notify the Emitter when the queue + // is not anymore empty + ff_node::set_output_blocking(m,c); + // this is to setup the condition variable where + // the thread (main?) that is using the accelerator + // will sleep if the queue fill up + if (!ff_node::init_output_blocking(m,c)) { + error("FARM, init output blocking\n"); + return -1; + } + + if (hasCollector()) { + m=NULL,c=NULL; + if (!init_output_blocking(m,c)) { + error("FARM, init output blocking\n"); + return -1; + } + + m=NULL,c=NULL; + if (!ff_node::init_input_blocking(m,c)) { + error("FARM, init input blocking\n"); + return -1; + } + set_output_blocking(m,c); + } + } + + prepared=true; return 0; } @@ -669,16 +695,17 @@ class ff_farm: public ff_node { return true; } virtual inline void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { + pthread_cond_t *&c, + bool canoverwrite=false) { if (collector && !collector_removed) { if (collector == (ff_node*)gt) - gt->set_output_blocking(m,c); + gt->set_output_blocking(m,c, canoverwrite); else - collector->set_output_blocking(m,c); + collector->set_output_blocking(m,c, canoverwrite); } else { for(size_t i=0;iset_output_blocking(m,c); + workers[i]->set_output_blocking(m,c, canoverwrite); } } @@ -1124,22 +1151,10 @@ class ff_farm: public ff_node { if (!has_input_channel) lb->skipfirstpop(true); return 0; } - - // blocking stuff -------------------------------------------- - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!init_input_blocking(m,c)) { - error("FARM, wrap_around, init input blocking mode for emitter\n"); - return -1; - } - set_output_blocking(m,c); - m=NULL,c=NULL; - if (!init_output_blocking(m,c)) { - error("FARM, wrap_around, init output blocking mode for collector\n"); + if (has_input_channel) { + error("FARM: wrap_around: cannot create feedback if accelerator mode is set, and the collector is present!\n"); return -1; } - // ------------------------------------------------------------ - ff_buffernode *tmpbuffer = new ff_buffernode(out_buffer_entries, false); assert(tmpbuffer); internalSupportNodes.push_back(tmpbuffer); @@ -1151,6 +1166,22 @@ class ff_farm: public ff_node { collector->set_output_feedback(tmpbuffer); lb->set_input_feedback(tmpbuffer); + + // blocking stuff ------------------------ + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!init_input_blocking(m,c)) { + error("FARM, wrap_around, init input blocking mode for emitter\n"); + return -1; + } + set_output_blocking(m,c); + m=NULL,c=NULL; + if (!init_output_blocking(m,c)) { + error("FARM, wrap_around, init output blocking mode for collector\n"); + return -1; + } + // --------------------------------------- + lb->skipfirstpop(true); return 0; } @@ -1484,7 +1515,10 @@ class ff_farm: public ff_node { inline bool load_result(void ** task, unsigned long retry=((unsigned long)-1), unsigned long ticks=ff_gatherer::TICKS2WAIT) { - if (!collector) return false; + if (!collector) { + error("FARM: load_result: no collector present!!"); + return false; + } if (blocking_in) { _retry: @@ -1523,7 +1557,10 @@ class ff_farm: public ff_node { * */ inline bool load_result_nb(void ** task) { - if (!collector) return false; + if (!collector) { + error("FARM: load_result_nb: no collector present!!"); + return false; + } return gt->pop_nb(task); } @@ -1618,7 +1655,6 @@ class ff_farm: public ff_node { if (collector && !collector_removed) { if ((ff_node*)gt == collector) { assert(gt->get_out_buffer()); - assert(gt->get_out_buffer() == this->get_out_buffer()); w.push_back(this); } else { collector->get_out_nodes(w); @@ -1982,7 +2018,7 @@ class ff_farm: public ff_node { } protected: - bool has_input_channel; // for accelerator + bool has_input_channel; // for the accelerator mode bool collector_removed; bool ordered; bool fixedsize; diff --git a/ff/gt.hpp b/ff/gt.hpp index ab320401..134981b0 100644 --- a/ff/gt.hpp +++ b/ff/gt.hpp @@ -108,12 +108,13 @@ class ff_gatherer: public ff_thread { return true; } inline void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { + pthread_cond_t *&c, + bool canoverwrite=false) { p_cons_m = m, p_cons_c = c; if (filter && (filter->get_out_buffer()!=nullptr)) { // CHECK: asimmetry with test here (*) if (filter->isMultiInput() && !filter->isComp()) return; // to avoid recursive calls to set_output_blocking - filter->set_output_blocking(m,c); + filter->set_output_blocking(m,c, canoverwrite); } } diff --git a/ff/lb.hpp b/ff/lb.hpp index 0fb9a881..8ac2e13b 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -111,7 +111,8 @@ class ff_loadbalancer: public ff_thread { return true; } inline void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { + pthread_cond_t *&c, + bool canoverwrite=false) { assert(1==0); } @@ -344,7 +345,7 @@ class ff_loadbalancer: public ff_thread { } while(1); if (blocking_in) { struct timespec tv; - timedwait_timeout(tv); + timedwait_timeout(tv); pthread_mutex_lock(cons_m); pthread_cond_timedwait(cons_c, cons_m, &tv); pthread_mutex_unlock(cons_m); diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 57765d0a..7d2f6435 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -169,15 +169,16 @@ class ff_minode: public ff_node { //return gt->init_output_blocking(m,c); } virtual inline void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { + pthread_cond_t *&c, + bool canoverwrite=false) { ff_node* filter = gt->get_filter(); if (filter && ( (filter->get_out_buffer()!=nullptr) || filter->isMultiOutput() ) ) { // see gt.hpp - filter->set_output_blocking(m, c); + filter->set_output_blocking(m, c, canoverwrite); } //gt->set_output_blocking(m,c); - ff_node::set_output_blocking(m,c); + ff_node::set_output_blocking(m,c, canoverwrite); } inline pthread_mutex_t &get_prod_m() { return gt->get_prod_m(); } @@ -503,8 +504,9 @@ class ff_monode: public ff_node { return lb->init_output_blocking(m,c, feedback); } virtual inline void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { - ff_node::set_output_blocking(m,c); // <---- CHECK: ci vuole?????? + pthread_cond_t *&c, + bool canoverwrite=false) { + ff_node::set_output_blocking(m,c, canoverwrite); } virtual inline pthread_mutex_t &get_prod_m() { return lb->get_prod_m();} diff --git a/ff/node.hpp b/ff/node.hpp index 1a20b5e5..093ba633 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -613,8 +613,11 @@ class ff_node { return true; } virtual inline void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { - assert(p_cons_m == nullptr || (p_cons_m == m && p_cons_c == c)); + pthread_cond_t *&c, + bool canoverwrite=false) { + assert(canoverwrite || + (p_cons_m == nullptr && p_cons_c == nullptr) || + (p_cons_m == m && p_cons_c == c)); p_cons_m = m, p_cons_c = c; } @@ -1620,14 +1623,20 @@ struct ff_buffernode: ff_node { pthread_cond_t &get_cons_c() { return *p_cons_c;} - // REMEMBER: + // New Blocking protocol (both for bounded and unbounded buffer): // init_output_blocking initializes prod_* // set_output_blocking sets p_cons_* // init_input_blocking initializes cons_* - // set_input_blocking sets p_prod_* - // send : if ok then 'p_cons_m' else 'prod_m' - // receive: if ok then 'p_prod_m' else 'cons_m' + // sender: + // empty=channel.empty(); + // r=channel.send() + // if (!r) timewait(prod_c); + // if empty then signal(p_cons_c) // the channel was empty + + // receive: + // r=channel.receive() + // if (!r) timewait(cons_c); // channel empty }; diff --git a/ff/parallel_for.hpp b/ff/parallel_for.hpp index 8f743696..609b720a 100644 --- a/ff/parallel_for.hpp +++ b/ff/parallel_for.hpp @@ -4,11 +4,8 @@ * \file parallel_for.hpp * \ingroup high_level_patterns * - * \brief This file describes the parallel_for/parallel_reduce skeletons. + * \brief It describes the ParallelFor/ParallelForReduce/ParallelForPipeReduce patterns. * - * Realize a parallel for loop. Different iterations should be - * independent (data-races management is not autimatically provided - * but can be introduced by programmers via mutex). * */ @@ -37,22 +34,18 @@ * This file contains the ParallelFor and the ParallelForReduce classes * (and also some static functions). * - * Iterations scheduling options: - * - * 1 - default static scheduling - * 2 - static scheduling with grain size greater than 0 - * 3 - dynamic scheduling with grain size greater than 0 + * Iterations scheduling: * * As a general rule, the scheduling strategy is selected according to the chunk value: * - chunk == 0 means default static scheduling, that is, ~(#iteration_space/num_workers) - * iterations per thread) - * - chunk > 0 means dynamic scheduling with grain equal to chunk, that is, - * no more than chunk iterations at a time is computed by one thread, the - * chunk is assigned to worker threads dynamically - * - chunk < 0 means static scheduling with grain equal to chunk, that is, + * iterations per thread assigned in one single shot at the beginning. + * - chunk > 0 means dynamic scheduling with grain equal to the chunk size, that is, + * no more than chunk iterations at a time is assigned to one Worker, the + * chunk is assigned to Workers dynamically + * - chunk < 0 means static scheduling with grain equal to the chunk size, that is, * the iteration space is divided into chunks each one of no more - * than chunk iterations. Then chunks are assigned to the workers threads - * statically and in a round-robin fashion. + * than chunk iterations. Then chunks are assigned to the Workers statically + * and in a round-robin fashion. * * If you want to use the static scheduling policy (either default or with a given grain), * please use the **parallel_for_static** method. @@ -78,9 +71,24 @@ * A[i]=f(i); A[i]=f(i); * long sum=0; ---> }); * for(long i=0; i > { * they are not automatically walked. Each chunk can be traversed within the * parallel_for body (e.g. with a for loop within f with the same step). * - * \note Useful in few cases only - requires some expertise. + * \note It requires some expertise. * * @param first first value of the iteration variable * @param last last value of the iteration variable @@ -640,6 +648,16 @@ class ParallelForReduce: public ff_forall_farm > { } FF_PARFORREDUCE_F_STOP(this, var, finalreduce); } + template + inline void parallel_reduce_idx(T& var, const T& identity, + long first, long last, long step, long grain, + const Function& body, const FReduction& finalreduce, + const long nw=FF_AUTO) { + FF_PARFORREDUCE_START_IDX(this, var, identity, idx,first,last,step,PARFOR_DYNAMIC(grain),nw) { + body(ff_start_idx, ff_stop_idx, var, _ff_thread_id); + } FF_PARFORREDUCE_F_STOP(this, var, finalreduce); + } + /** * \brief Parallel reduce region (step) - static * @@ -680,7 +698,7 @@ class ParallelForReduce: public ff_forall_farm > { //! ParallelForPipeReduce class /** - * \brief Parallel pipelined for-reduce + * \brief Parallel pipelined map+reduce * */ template @@ -791,10 +809,12 @@ class ParallelForPipeReduce: public ff_pipeline { }; //#endif //VS12 -// -//---- static functions, useful for one-shot parallel for execution or when no extra settings are needed -// +/// --------------------------------------------------------------------------------- +/// These are the one-shot versions. It is not needed to create an object instance. +/// They are useful (and more efficient) for a one-shot parallel loop execution +/// or when no extra settings are needed. +// ----------------- parallel_for ---------------------- //! Parallel loop over a range of indexes (step=1) template static void parallel_for(long first, long last, const Function& body, @@ -820,17 +840,43 @@ static void parallel_for(long first, long last, long step, long grain, } FF_PARFOR_END(pfor); } +// advanced version +template +inline void parallel_for_idx(long first, long last, long step, long grain, + const Function& f, const long nw=FF_AUTO) { + FF_PARFOR_BEGIN_IDX(pfor,parforidx,first,last,step,PARFOR_DYNAMIC(grain),nw) { + f(ff_start_idx, ff_stop_idx,_ff_thread_id); + } FF_PARFOR_END(pfor); +} + + + +// -------------- parallel_reduce ------------------- template void parallel_reduce(Value_t& var, const Value_t& identity, - long first, long last, + long first, long last, long step, long grain, const Function& body, const FReduction& finalreduce, const long nw=FF_AUTO) { Value_t _var = var; - FF_PARFORREDUCE_BEGIN(pfr, _var, identity, parforidx, first, last, 1, PARFOR_STATIC(0), nw) { + FF_PARFORREDUCE_BEGIN(pfr, _var, identity, parforidx, first, last, step, PARFOR_DYNAMIC(grain), nw) { body(parforidx, _var); } FF_PARFORREDUCE_F_END(pfr, _var, finalreduce); var=_var; } + +// advanced version +template +void parallel_reduce_idx(Value_t& var, const Value_t& identity, + long first, long last, long step, long grain, + const Function& body, const FReduction& finalreduce, + const long nw=FF_AUTO) { + Value_t _var = var; + FF_PARFORREDUCE_BEGIN_IDX(pfr, _var, identity, idx,first,last,step,PARFOR_DYNAMIC(grain),nw) { + body(ff_start_idx, ff_stop_idx, _var, _ff_thread_id); + } FF_PARFORREDUCE_F_END(pfr, _var, finalreduce); + var=_var; +} + } // namespace ff diff --git a/ff/parallel_for_internals.hpp b/ff/parallel_for_internals.hpp index 39317ddc..146dd718 100644 --- a/ff/parallel_for_internals.hpp +++ b/ff/parallel_for_internals.hpp @@ -137,10 +137,10 @@ namespace ff { * This can be useful if you have to perform some actions before starting * the loop. */ -#define FF_PARFOR2_BEGIN(name, idx, begin, end, step, chunk, nw) \ +#define FF_PARFOR_BEGIN_IDX(name, idx, begin, end, step, chunk, nw) \ ff_forall_farm > name(nw,false,true); \ name.setloop(begin,end,step,chunk, nw); \ - auto F_##name = [&] (const long ff_start_##idx, const long ff_stop_##idx, \ + auto F_##name = [&] (const long ff_start_idx, const long ff_stop_idx, \ const int _ff_thread_id, const int) { \ /* here you have to define the for loop using ff_start/stop_idx */ @@ -156,6 +156,7 @@ namespace ff { } else F_##name(name.startIdx(),name.stopIdx(),0,0); \ } + /* ---------------------------------------------- */ /** @@ -180,6 +181,14 @@ namespace ff { PRAGMA_IVDEP; \ for(long idx=start;idx > name(nw,false,true); \ + name.setloop(begin,end,step,chunk,nw); \ + auto idtt_##name =identity; \ + auto F_##name =[&](const long ff_start_idx,const long ff_stop_idx,const int _ff_thread_id, \ + decltype(var) &var) { + + #define FF_PARFORREDUCE_END(name, var, op) \ }; \ if (name.getnw()>1) { \ @@ -317,6 +326,13 @@ namespace ff { PRAGMA_IVDEP \ for(long idx=ff_start_##idx;idxsetloop(begin,end,step,chunk,nw); \ + auto idtt_##name =identity; \ + auto F_##name =[&](const long ff_start_idx, const long ff_stop_idx, \ + const int _ff_thread_id, decltype(var) &var) { + + #define FF_PARFORREDUCE_START_STATIC(name, var,identity, idx,begin,end,step, chunk, nw) \ assert(chunk<=0); \ name->setloop(begin,end,step,chunk,nw); \ @@ -1168,6 +1184,10 @@ class ff_forall_farm: public ff_farm { * in a round-robin fashion. */ inline void setloop(long begin,long end,long step,long chunk,long nw) { + if (nw>(ssize_t)getNWorkers()) { + error("The number of threads specified is greater than the number set in the ParallelFor* constructor, it will be downsized\n"); + nw = getNWorkers(); + } assert(nw<=(ssize_t)getNWorkers()); forall_Scheduler *sched = (forall_Scheduler*)getEmitter(); sched->setloop(begin,end,step,chunk,(nw<=0)?getNWorkers():(size_t)nw); diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 5381b530..cd3a9334 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -97,7 +97,6 @@ class ff_pipeline: public ff_node { // - it's a single node (comp or pipeline) with the last stage multi-output [prev_single_multioutput] // // - // for(int i=1;iisAll2All(); const bool curr_single_standard = (!nodes_list[i]->isMultiInput()); @@ -154,10 +153,14 @@ class ff_pipeline: public ff_node { pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (curr_single_standard) { + bool skip_set_output_blocking = false; if (nodes_list[i]->create_input_buffer(in_buffer_entries, fixedsize)<0) return -1; if (prev_single_standard) { if (nodes_list[i-1]->set_output_buffer(nodes_list[i]->get_in_buffer())<0) return -1; } else { + skip_set_output_blocking = true; + // WARNING: here we add as output node of the previous stage the + // current node and not a buffer-node. if (prev_multi_standard || prev_multi_multioutput) { svector w(1); nodes_list[i-1]->get_out_nodes(w); @@ -175,8 +178,9 @@ class ff_pipeline: public ff_node { if (!nodes_list[i]->init_input_blocking(m,c)) { error("PIPE, init input blocking mode for node %d\n", i); return -1; - } - nodes_list[i-1]->set_output_blocking(m,c); + } + if (!skip_set_output_blocking) // we do not wait to overwrite previous setting + nodes_list[i-1]->set_output_blocking(m,c); if (!nodes_list[i-1]->init_output_blocking(m,c,false)) { error("PIPE, init output blocking mode for node %d\n", i-1); return -1; @@ -691,7 +695,7 @@ class ff_pipeline: public ff_node { // // the last stage is a multi-output node: // - it's a farm with a multi-output collector [last_single_multioutput] - // - it's a farm without collecto and workers are standard node [last_multi_standard] + // - it's a farm without collector and workers are standard nodes [last_multi_standard] // - it's a farm without collector with multi-output workers [last_multi_multioutput] // - it's a all2all with standard nodes [last_multi_standard] // - it's a all2all with multi-output nodes [last_multi_multioutput] @@ -1433,6 +1437,13 @@ class ff_pipeline: public ff_node { nodes_list[last]->get_out_nodes(w); } + inline void get_out_nodes_feedback(svector&w) { + assert(nodes_list.size()>0); + int last = static_cast(nodes_list.size())-1; + nodes_list[last]->get_out_nodes_feedback(w); + } + + inline void get_in_nodes(svector&w) { assert(nodes_list.size()>0); nodes_list[0]->get_in_nodes(w); @@ -1488,10 +1499,11 @@ class ff_pipeline: public ff_node { return nodes_list[last]->init_output_blocking(m,c); } virtual inline void set_output_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c) { + pthread_cond_t *&c, + bool canoverwrite=false) { const int last = static_cast(nodes_list.size())-1; if (last<0) return; - nodes_list[last]->set_output_blocking(m,c); + nodes_list[last]->set_output_blocking(m,c, canoverwrite); } virtual inline pthread_mutex_t &get_cons_m() { return nodes_list[0]->get_cons_m();} diff --git a/tests/test_accelerator.cpp b/tests/test_accelerator.cpp index 0403e89a..1c66b532 100644 --- a/tests/test_accelerator.cpp +++ b/tests/test_accelerator.cpp @@ -45,7 +45,6 @@ class Worker: public ff_node { } }; - int main(int argc, char * argv[]) { int nworkers = 3; int streamlen= 1000; @@ -87,6 +86,7 @@ int main(int argc, char * argv[]) { void * result=NULL; for (int i=0;i w; - for(int i=0;i W; + for(int i=0;i (stage1->stage2)-- - * (farm) v | | * Emitter -- |--> Gatherer - * ^ | | (multi-input + * ^ | | (multi-input) * | --> (stage1->stage2)-- - * | | * ---------------------------- diff --git a/tests/test_multi_input3.cpp b/tests/test_multi_input3.cpp index e13e45fd..ee923232 100644 --- a/tests/test_multi_input3.cpp +++ b/tests/test_multi_input3.cpp @@ -28,12 +28,12 @@ /* * 2-stages pipeline * - * --> Worker1 --> --> Worker2--> - * | | | | - * Emitter1 -- --> Worker1 --> --> Collector1 --> Emitter2 -- --> Worker2--> --> Collector2 - * | | ^ | | | - * --> Worker1 --> | --> Worker2--> | - * |________________________________| + * --> Worker1 --> --> Worker2--> + * | | | | + * Emitter1 - --> Worker1 --> -> DefColl -> Emitter2 - --> Worker2--> --> DefColl + * | | ^ | | | + * --> Worker1 --> | --> Worker2--> | + * |_____________________________| * * * @@ -43,24 +43,22 @@ #include using namespace ff; -class Emitter1: public ff_node { +class Emitter1: public ff_node_t { public: Emitter1(int ntasks):ntasks(ntasks) {} - void* svc(void*) { + long* svc(long*) { for(long i=0;i { public: - Emitter2(ff_loadbalancer *lb):lb(lb) {} - void* svc(void* t) { - int wid = lb->get_channel_id(); - if (wid == -1) { + long* svc(long* t) { + if (get_channel_id() == -1) { printf("TASK FROM INPUT %ld\n", (long)(t)); // task coming from previous stage return t; @@ -69,28 +67,25 @@ class Emitter2: public ff_node { return GO_ON; } void eosnotify(ssize_t) { - lb->broadcast_task((void*)EOS); + broadcast_task(EOS); } -private: - ff_loadbalancer *lb; }; -class Worker1: public ff_node { +class Worker1: public ff_node_t { public: - void* svc(void* task) { + long* svc(long* task) { return task; } }; -class Worker2: public ff_node { +class Worker2: public ff_node_t { public: - void* svc(void* task) { + long* svc(long* task) { printf("Worker2(%ld): TASK COMPUTED\n", get_my_id()); return task; } }; - int main(int argc, char* argv[]) { int nworkers = 3; int ntasks = 1000; @@ -108,22 +103,23 @@ int main(int argc, char* argv[]) { pipe.add_stage(&farm1); pipe.add_stage(&farm2); farm1.add_emitter(new Emitter1(ntasks)); - farm1.add_collector(NULL); // default collector + farm1.add_collector(nullptr); // default collector std::vector w; for(int i=0;i Date: Mon, 15 Jun 2020 12:29:34 +0200 Subject: [PATCH 003/202] - Fixed warnings when compiling with c++20 (mainly related to volatile). - Minor adjustments (done() API, some comments, etc...) - Extended interface for the ParallelFor* (macros) --- ff/all2all.hpp | 14 +++++++++++ ff/buffer.hpp | 21 ++++++++++++---- ff/combine.hpp | 37 ++++++++++++++++++---------- ff/dynqueue.hpp | 3 ++- ff/multinode.hpp | 41 ++++++++++++++++++++++---------- ff/node.hpp | 7 ++++-- ff/parallel_for.hpp | 9 +++---- ff/parallel_for_internals.hpp | 10 +++++--- ff/pipeline.hpp | 4 ++++ tests/Makefile | 2 +- tests/bench_masterworker.cpp | 7 ++++-- tests/perf_parfor.cpp | 3 ++- tests/test_multi_output2.cpp | 2 +- tests/test_parfor_unbalanced.cpp | 2 +- tests/test_parforpipereduce.cpp | 2 +- tests/test_pool3.cpp | 13 ++++++---- tests/test_stopstartthreads.cpp | 4 ++-- 17 files changed, 128 insertions(+), 53 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 9ceaf440..4504d1a6 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -395,6 +395,20 @@ class ff_a2a: public ff_node { return 0; } + /** + * \brief checks if the node is running + * + */ + bool done() const { + const size_t nworkers1 = workers1.size(); + const size_t nworkers2 = workers2.size(); + for(size_t i=0;idone()) return false; + for(size_t i=0;idone()) return false; + return true; + } + const svector& getFirstSet() const { return workers1; } const svector& getSecondSet() const { return workers2; } diff --git a/ff/buffer.hpp b/ff/buffer.hpp index b1ce51e7..9ec4eaf1 100644 --- a/ff/buffer.hpp +++ b/ff/buffer.hpp @@ -233,7 +233,10 @@ class SWSR_Ptr_Buffer { WMB(); //std::atomic_thread_fence(std::memory_order_release); buf[pwrite] = data; - pwrite += (pwrite+1 >= size) ? (1-size): 1; // circular buffer + if ((pwrite+1) >= size) + pwrite = 1-size; + else pwrite = 1; + //pwrite += (pwrite+1 >= size) ? (1-size): 1; // circular buffer return true; } return false; @@ -312,7 +315,10 @@ class SWSR_Ptr_Buffer { */ inline bool inc() { buf[pread]=NULL; - pread += (pread+1 >= size) ? (1-size): 1; // circular buffer + if ((pread+1) >= size) + pread = 1-size; + else pread = 1; + //pread += (pread+1 >= size) ? (1-size): 1; // circular buffer return true; } @@ -352,7 +358,8 @@ class SWSR_Ptr_Buffer { pwrite = longxCacheLine-1; pread = longxCacheLine-1; } else { - pread=pwrite=0; + pread=0; + pwrite=0; } #if defined(SWSR_MULTIPUSH) mcnt = 0; @@ -487,7 +494,10 @@ class Lamport_Buffer { if (empty()) return false; *data = buf[pread]; - pread += (pread+1 >= size) ? (1-size): 1; + if ((pread+1) >= size) + pread = 1-size; + else pread = 1; + // pread += (pread+1 >= size) ? (1-size): 1; return true; } @@ -495,7 +505,8 @@ class Lamport_Buffer { * TODO */ inline void reset() { - pread=pwrite=0; + pread=0; + pwrite=0; if (size<=512) for(unsigned long i=0;iisComp()) - return ((ff_comb*)comp_nodes[0])->getFirst(); - return comp_nodes[0]; - } - ff_node* getLast() const { - if (comp_nodes[1]->isComp()) - return ((ff_comb*)comp_nodes[1])->getLast(); - return comp_nodes[1]; - } - +private: void registerAllGatherCallback(int (*cb)(void *,void **, void*), void * arg) { assert(isMultiInput()); // NOTE: the gt of the first node will be replaced by the ff_comb gt. @@ -174,6 +163,17 @@ class ff_comb: public ff_minode { if (wait()<0) return -1; return 0; } + + /** + * \brief checks if the node is running + * + */ + bool done() const { + if (comp_nodes[0]->isMultiInput()) + return ff_minode::done(); + return ff_node::done(); + } + // NOTE: it is multi-input only if the first node is multi-input bool isMultiInput() const { if (getFirst()->isMultiInput()) return true; @@ -186,6 +186,19 @@ class ff_comb: public ff_minode { } inline bool isComp() const { return true; } + // returns the first node on the left-hand side + ff_node* getFirst() const { + if (comp_nodes[0]->isComp()) + return ((ff_comb*)comp_nodes[0])->getFirst(); + return comp_nodes[0]; + } + // returns the last node on the right-hand side + ff_node* getLast() const { + if (comp_nodes[1]->isComp()) + return ((ff_comb*)comp_nodes[1])->getLast(); + return comp_nodes[1]; + } + double ffTime() { return diffmsec(getstoptime(),getstarttime()); } diff --git a/ff/dynqueue.hpp b/ff/dynqueue.hpp index 667626f9..c993354d 100644 --- a/ff/dynqueue.hpp +++ b/ff/dynqueue.hpp @@ -134,7 +134,8 @@ class dynqueue { dynqueue(int cachesize=DEFAULT_CACHE_SIZE, bool fillcache=false):cache(cachesize) { Node * n = (Node *)::malloc(sizeof(Node)); n->data = NULL; n->next = NULL; - head=tail=n; + head=n; + tail=n; cache.init(); if (fillcache) { for(int i=0;iwait()<0) return -1; - return 0; - } - + void blocking_mode(bool blk=true) { blocking_in = blocking_out = blk; gt->blocking_mode(blk); @@ -348,8 +343,22 @@ class ff_minode: public ff_node { return run(); } + int wait(/* timeout */) { + if (gt->wait()<0) return -1; + return 0; + } + int wait_freezing() { return gt->wait_freezing(); } + + /** + * \brief checks if the node is running + * + */ + bool done() const { + return gt->done(); + } + bool fromInput() const { return gt->fromInput(); } /** @@ -480,12 +489,7 @@ class ff_monode: public ff_node { if (lb->getnworkers() == 0) ff_send_out(task); lb->propagateEOS(task); } - - int wait(/* timeout */) { - if (lb->waitlb()<0) return -1; - return 0; - } - + void blocking_mode(bool blk=true) { blocking_in = blocking_out = blk; lb->blocking_mode(blk); @@ -698,6 +702,19 @@ class ff_monode: public ff_node { return run(true); } + int wait(/* timeout */) { + if (lb->waitlb()<0) return -1; + return 0; + } + + /** + * \brief checks if the node is running + * + */ + bool done() const { + return lb->done(); + } + /** * \internal * \brief Gets the internal lb (Emitter) diff --git a/ff/node.hpp b/ff/node.hpp index 093ba633..b0293536 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -830,8 +830,11 @@ class ff_node { return thread->isfrozen(); } - - virtual bool done() const { + /** + * \brief checks if the node is running + * + */ + virtual bool done() const { if (!thread) return true; return thread->done(); } diff --git a/ff/parallel_for.hpp b/ff/parallel_for.hpp index 609b720a..782cacdb 100644 --- a/ff/parallel_for.hpp +++ b/ff/parallel_for.hpp @@ -843,8 +843,9 @@ static void parallel_for(long first, long last, long step, long grain, // advanced version template inline void parallel_for_idx(long first, long last, long step, long grain, - const Function& f, const long nw=FF_AUTO) { - FF_PARFOR_BEGIN_IDX(pfor,parforidx,first,last,step,PARFOR_DYNAMIC(grain),nw) { + const Function& f, const long nw=FF_AUTO, + const bool noActiveScheduler=false) { + FF_PARFOR_BEGIN_IDX(pfor,parforidx,first,last,step,PARFOR_DYNAMIC(grain),nw,noActiveScheduler){ f(ff_start_idx, ff_stop_idx,_ff_thread_id); } FF_PARFOR_END(pfor); } @@ -869,9 +870,9 @@ template void parallel_reduce_idx(Value_t& var, const Value_t& identity, long first, long last, long step, long grain, const Function& body, const FReduction& finalreduce, - const long nw=FF_AUTO) { + const long nw=FF_AUTO, const bool noActiveScheduler=false) { Value_t _var = var; - FF_PARFORREDUCE_BEGIN_IDX(pfr, _var, identity, idx,first,last,step,PARFOR_DYNAMIC(grain),nw) { + FF_PARFORREDUCE_BEGIN_IDX(pfr, _var, identity, idx,first,last,step,PARFOR_DYNAMIC(grain),nw,noActiveScheduler) { body(ff_start_idx, ff_stop_idx, _var, _ff_thread_id); } FF_PARFORREDUCE_F_END(pfr, _var, finalreduce); var=_var; diff --git a/ff/parallel_for_internals.hpp b/ff/parallel_for_internals.hpp index 146dd718..423e80f1 100644 --- a/ff/parallel_for_internals.hpp +++ b/ff/parallel_for_internals.hpp @@ -135,11 +135,14 @@ namespace ff { /* This is equivalent to the above one except that the user has to define * the for loop in the range (ff_start_idx,ff_stop_idx( * This can be useful if you have to perform some actions before starting - * the loop. + * the local loop and/or some actions after the local loop finishes. + * The onoff parameter allow to disable/enable the scheduler thread + * (by default the scheduler is active. */ -#define FF_PARFOR_BEGIN_IDX(name, idx, begin, end, step, chunk, nw) \ +#define FF_PARFOR_BEGIN_IDX(name, idx, begin, end, step, chunk, nw, onoff) \ ff_forall_farm > name(nw,false,true); \ name.setloop(begin,end,step,chunk, nw); \ + name.disableScheduler(onoff); \ auto F_##name = [&] (const long ff_start_idx, const long ff_stop_idx, \ const int _ff_thread_id, const int) { \ /* here you have to define the for loop using ff_start/stop_idx */ @@ -181,9 +184,10 @@ namespace ff { PRAGMA_IVDEP; \ for(long idx=start;idx > name(nw,false,true); \ name.setloop(begin,end,step,chunk,nw); \ + name.disableScheduler(onoff); \ auto idtt_##name =identity; \ auto F_##name =[&](const long ff_start_idx,const long ff_stop_idx,const int _ff_thread_id, \ decltype(var) &var) { diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index cd3a9334..32e86d65 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -1236,6 +1236,10 @@ class ff_pipeline: public ff_node { for(unsigned int i=0;ifreeze(); } + /** + * \brief checks if the pipeline is still running or not + * + */ inline bool done() const { int nstages=static_cast(nodes_list.size()); for(int i=0;i { long *svc(long *in) { - for(volatile size_t i=0; i<1000; ++i) - __asm__("nop"); + volatile size_t i=0; + while(i<1000) { + __asm__("nop"); + i = i +1; + } return in; } diff --git a/tests/perf_parfor.cpp b/tests/perf_parfor.cpp index 11b030cd..6cafcee4 100644 --- a/tests/perf_parfor.cpp +++ b/tests/perf_parfor.cpp @@ -91,7 +91,8 @@ static inline void compute(long id, int nticks) { simSRandom(id + 1L); } long val = simRandomRange(1,nticks); - for(volatile long k=0;k { struct N: ff_minode_t { N(const long numtasks):numtasks(numtasks) {} long *svc(long *task){ - for(volatile long i=0;i<10000; ++i); + ticks_wait(10000); //for(volatile long i=0;i<10000; ++i); printf("N got task %ld\n",(long)task); ++received; return GO_ON; diff --git a/tests/test_parfor_unbalanced.cpp b/tests/test_parfor_unbalanced.cpp index 93318fec..4c31c3f1 100644 --- a/tests/test_parfor_unbalanced.cpp +++ b/tests/test_parfor_unbalanced.cpp @@ -67,7 +67,7 @@ inline static void SRandom(unsigned long seed) { } inline static void compute(long end) { - for(volatile long j=0;jreserve(stop-start); for(long i=start;ipush_back(A[i]*B[i]); } node.ff_send_out(C); diff --git a/tests/test_pool3.cpp b/tests/test_pool3.cpp index 2fc01a0d..7c1f4e85 100644 --- a/tests/test_pool3.cpp +++ b/tests/test_pool3.cpp @@ -74,8 +74,9 @@ void selection(ParallelForReduce & pfr, std::vector &P, std::vector< env.avg = fitness(pfr, P, nwS); auto S = [&](const long start, const long stop, const int thread_id) { - - for(volatile long j=0;j<(stop-start);++j); // lose time + + // losing time + ticks_wait(stop-start); //for(volatile long j=0;j<(stop-start);++j); for(long j=start; j env.avg) bufferPool[thread_id].push_back(P[j]); @@ -90,7 +91,8 @@ void selection(ParallelForReduce & pfr, std::vector &P, std::vector< } const long &evolution(long &element, const Env_t&,const int) { - for(volatile long j=0;j & pfr, std::vector &P, std::vectoriter;++i); - + ticks_wait(task->iter); + return t; } From 3aa647a6a66b38ac1b383b2f73a9947527000ef5 Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Tue, 30 Jun 2020 15:35:03 +0200 Subject: [PATCH 004/202] - reverted back buffer.hpp and dynqueue.hpp to the previous versions because of severe performance problems --- ff/buffer.hpp | 21 +++++---------------- ff/dynqueue.hpp | 3 +-- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/ff/buffer.hpp b/ff/buffer.hpp index 9ec4eaf1..b1ce51e7 100644 --- a/ff/buffer.hpp +++ b/ff/buffer.hpp @@ -233,10 +233,7 @@ class SWSR_Ptr_Buffer { WMB(); //std::atomic_thread_fence(std::memory_order_release); buf[pwrite] = data; - if ((pwrite+1) >= size) - pwrite = 1-size; - else pwrite = 1; - //pwrite += (pwrite+1 >= size) ? (1-size): 1; // circular buffer + pwrite += (pwrite+1 >= size) ? (1-size): 1; // circular buffer return true; } return false; @@ -315,10 +312,7 @@ class SWSR_Ptr_Buffer { */ inline bool inc() { buf[pread]=NULL; - if ((pread+1) >= size) - pread = 1-size; - else pread = 1; - //pread += (pread+1 >= size) ? (1-size): 1; // circular buffer + pread += (pread+1 >= size) ? (1-size): 1; // circular buffer return true; } @@ -358,8 +352,7 @@ class SWSR_Ptr_Buffer { pwrite = longxCacheLine-1; pread = longxCacheLine-1; } else { - pread=0; - pwrite=0; + pread=pwrite=0; } #if defined(SWSR_MULTIPUSH) mcnt = 0; @@ -494,10 +487,7 @@ class Lamport_Buffer { if (empty()) return false; *data = buf[pread]; - if ((pread+1) >= size) - pread = 1-size; - else pread = 1; - // pread += (pread+1 >= size) ? (1-size): 1; + pread += (pread+1 >= size) ? (1-size): 1; return true; } @@ -505,8 +495,7 @@ class Lamport_Buffer { * TODO */ inline void reset() { - pread=0; - pwrite=0; + pread=pwrite=0; if (size<=512) for(unsigned long i=0;idata = NULL; n->next = NULL; - head=n; - tail=n; + head=tail=n; cache.init(); if (fillcache) { for(int i=0;i Date: Tue, 7 Jul 2020 02:59:59 +0100 Subject: [PATCH 005/202] Fix CMakefiles, parameterise Makefile --- CMakeLists.txt | 275 +++++++++++++++++++++---------------------- tests/CMakeLists.txt | 229 ++++++++++------------------------- tests/Makefile | 60 +++++++--- 3 files changed, 236 insertions(+), 328 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6de35099..6f5829e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,24 +2,23 @@ # If the user specifies -DCMAKE_BUILD_TYPE on the command line, take # their definition and dump it in the cache along with proper # documentation, otherwise set CMAKE_BUILD_TYPE to Debug prior to -# calling PROJECT() +# calling PROJECT() # IF(DEFINED CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Choose the type of + SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") - MESSAGE(STATUS "Build Type from command line " ${CMAKE_BUILD_TYPE}) + MESSAGE( STATUS "Build Type from command line " ${CMAKE_BUILD_TYPE}) ELSE() - SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, + SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") - MESSAGE(STATUS "Default Build Type: Release (change with - -DCMAKE_BUILD_TYPE=Debug | Release | RelWithDebInfo | MinSizeRel") + MESSAGE( STATUS "Default Build Type: Release (change with + -DCMAKE_BUILD_TYPE=Debug | Release | RelWithDebInfo | MinSizeRel") ENDIF() - project( mc-fastflow ) cmake_minimum_required( VERSION 2.8 ) @@ -34,86 +33,85 @@ INCLUDE (${CMAKE_ROOT}/Modules/CheckCXXSourceCompiles.cmake) INCLUDE (${CMAKE_ROOT}/Modules/TestBigEndian.cmake) INCLUDE (${CMAKE_ROOT}/Modules/CheckSymbolExists.cmake) -message (STATUS "Detected processor is " ${CMAKE_SYSTEM_PROCESSOR}) +MESSAGE( STATUS "Detected processor is " ${CMAKE_SYSTEM_PROCESSOR}) MESSAGE( STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER} ) MESSAGE( STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER} ) if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") MESSAGE( STATUS "C++ Compiler is Clang") SET(FFCM_HAS_CLANGXX "TRUE") - # SET(CMAKE_CXX_FLAGS "-stdlib=libc++ -std=gnu++11") - SET(CMAKE_CXX_FLAGS "-std=c++11") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-register") -# ADD_DEFINITIONS(-stdlib=libc++ -std=gnu++11) # Or -std=c++11 +# SET(CMAKE_CXX_FLAGS "-stdlib=libc++ -std=gnu++17") + SET(CMAKE_CXX_FLAGS "-std=c++17") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-register") +# ADD_DEFINITIONS(-stdlib=libc++ -std=gnu++17) # Or -std=c++17 elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") MESSAGE( STATUS "C++ Compiler is GNU") execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GXX_VERSION) if (NOT(GXX_VERSION VERSION_GREATER 4.1)) - set(GCC_TOO_OLD 1) - message(WARNING "g++ Version < 4.1, some targets will be skipped") - message(STATUS "Use CC/CXX env variable to define a newer compiler, e.g. export CC=/usr/bin/gcc-4.2; export CXX=/usr/bin/g++-4.2 ") + set(GCC_TOO_OLD 1) + MESSAGE( WARNING "g++ Version < 4.1, some targets will be skipped") + MESSAGE( STATUS "Use CC/CXX env variable to define a newer compiler, e.g. export CC=/usr/bin/gcc-4.2; export CXX=/usr/bin/g++-4.2 ") else (NOT(GXX_VERSION VERSION_GREATER 4.1)) - ADD_DEFINITIONS(-std=c++11) # or -std=c++0x - endif (NOT(GXX_VERSION VERSION_GREATER 4.1)) + ADD_DEFINITIONS(-std=c++17) # or -std=c++0x + endif (NOT(GXX_VERSION VERSION_GREATER 4.1)) elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel") - MESSAGE( STATUS "C++ Compiler is Intel") - MESSAGE( WARNING "Compliance check not fully implemented yet") + MESSAGE( STATUS "C++ Compiler is Intel") + MESSAGE( WARNING "Compliance check not fully implemented yet") elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") - MESSAGE( STATUS "C++ Compiler is Visual Studio") + MESSAGE( STATUS "C++ Compiler is Visual Studio") endif() if (CMAKE_SYSTEM_NAME MATCHES "Windows") - ADD_DEFINITIONS(-DNO_DEFAULT_MAPPING) - message(WARNING "Pinning on Win platforn currently is not default" ) - if(CMAKE_CL_64) - message(STATUS "64-bit microsoft compiler found") - ADD_DEFINITIONS(-D_WIN64) - ADD_DEFINITIONS(-D_AMD64_) - ADD_DEFINITIONS(-DNOMINMAX) -# ADD_DEFINITIONS(-D_WIN32_WINNT=0x0403) - else(CMAKE_CL_64) - message(STATUS "32-bit microsoft compiler found") -# ADD_DEFINITIONS(-D_WIN32_WINNT=0x0403) - endif(CMAKE_CL_64) -endif (CMAKE_SYSTEM_NAME MATCHES "Windows") + ADD_DEFINITIONS(-DNO_DEFAULT_MAPPING) + MESSAGE( WARNING "Pinning on Win platforn currently is not default" ) + if(CMAKE_CL_64) + MESSAGE( STATUS "64-bit microsoft compiler found") + ADD_DEFINITIONS(-D_WIN64) + ADD_DEFINITIONS(-D_AMD64_) + ADD_DEFINITIONS(-DNOMINMAX) +# ADD_DEFINITIONS(-D_WIN32_WINNT=0x0403) + else(CMAKE_CL_64) + MESSAGE( STATUS "32-bit microsoft compiler found") +# ADD_DEFINITIONS(-D_WIN32_WINNT=0x0403) + endif(CMAKE_CL_64) +endif (CMAKE_SYSTEM_NAME MATCHES "Windows") #if (NOT(CMAKE_SYSTEM_NAME MATCHES "Windows")) -# MESSAGE(STATUS "[Unix-like system with pthread]") +# MESSAGE( STATUS "[Unix-like system with pthread]") #else (NOT(CMAKE_SYSTEM_NAME MATCHES "Windows")) -# MESSAGE(STATUS (${CMAKE_SYSTEM} " system: using Windows native threads]") +# MESSAGE( STATUS (${CMAKE_SYSTEM} " system: using Windows native threads]") #endif (NOT(CMAKE_SYSTEM_NAME MATCHES "Windows")) ADD_DEFINITIONS(-DUSE_CMAKE_CONFIG) find_package(Threads) if (NOT(CMAKE_HAVE_PTHREAD_H)) - if (CMAKE_SYSTEM_NAME MATCHES "Windows") - ADD_DEFINITIONS(-D_FF_SYSTEM_HAVE_WIN_PTHREAD) - message(STATUS "Using FF Pthread minport") - set(FFHEADERS_PLAT - ${FF}/platforms/pthread_minport_windows.h - ${FF}/platforms/stdint.h - ) - if (${CMAKE_SYSTEM} MATCHES Windows-5.1) - ADD_DEFINITIONS(-D_FF_WIN_XP) - MESSAGE(STATUS ${CMAKE_SYSTEM} " system: using CondVar emulation") - endif (${CMAKE_SYSTEM} MATCHES Windows-5.1) - else (CMAKE_SYSTEM_NAME MATCHES "Windows") - message(FATAL_ERROR "Cannot find Pthreads") - endif (CMAKE_SYSTEM_NAME MATCHES "Windows") + if (CMAKE_SYSTEM_NAME MATCHES "Windows") + ADD_DEFINITIONS(-D_FF_SYSTEM_HAVE_WIN_PTHREAD) + MESSAGE( STATUS "Using FF Pthread minport") + set(FFHEADERS_PLAT + ${FF}/platforms/pthread_minport_windows.h + ${FF}/platforms/stdint.h) + if (${CMAKE_SYSTEM} MATCHES Windows-5.1) + ADD_DEFINITIONS(-D_FF_WIN_XP) + MESSAGE( STATUS ${CMAKE_SYSTEM} " system: using CondVar emulation") + endif (${CMAKE_SYSTEM} MATCHES Windows-5.1) + else (CMAKE_SYSTEM_NAME MATCHES "Windows") + MESSAGE( FATAL_ERROR "Cannot find Pthreads") + endif (CMAKE_SYSTEM_NAME MATCHES "Windows") endif (NOT(CMAKE_HAVE_PTHREAD_H)) if (NOT(CMAKE_SYSTEM_NAME MATCHES "Windows")) - if(CMAKE_HAVE_PTHREAD_H) - MESSAGE(STATUS "Linking to pthreads") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread") - endif(CMAKE_HAVE_PTHREAD_H) + if(CMAKE_HAVE_PTHREAD_H) + MESSAGE( STATUS "Linking to pthreads") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread") + endif(CMAKE_HAVE_PTHREAD_H) endif (NOT(CMAKE_SYSTEM_NAME MATCHES "Windows")) if (CMAKE_SYSTEM_NAME MATCHES "Windows") # Avoid security in run time support and generate inlined intrinsics - ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) - if (CMAKE_BUILD_TYPE MATCHES Release) - ADD_DEFINITIONS(-Oy -Oi -Ot -Ob2) - endif (CMAKE_BUILD_TYPE MATCHES Release) + ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + if (CMAKE_BUILD_TYPE MATCHES Release) + ADD_DEFINITIONS(-Oy -Oi -Ot -Ob2) + endif (CMAKE_BUILD_TYPE MATCHES Release) endif (CMAKE_SYSTEM_NAME MATCHES "Windows") @@ -125,114 +123,110 @@ INCLUDE (cmake.modules/ffconfig.cmake) CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H) -if (NOT(HAVE_STDINT_H)) - INCLUDE_DIRECTORIES(${FF}/platforms/) +if (NOT(HAVE_STDINT_H)) + INCLUDE_DIRECTORIES(${FF}/platforms/) endif (NOT(HAVE_STDINT_H)) #find_path(STDINT_INCLUDE_DIR stdint.h -# HINTS /usr/include CMAKE_INCLUDE_PATH CMAKE_FRAMEWORK_PATH -# ) +# HINTS /usr/include CMAKE_INCLUDE_PATH CMAKE_FRAMEWORK_PATH +#) #if (STDINT_INCLUDE_DIR MATCHES STDINT_INCLUDE_DIR-NOTFOUND) -# message(STATUS "System hasn't stdint.h, using FF" ) +# MESSAGE( STATUS "System hasn't stdint.h, using FF" ) # INCLUDE_DIRECTORIES(${FF}/platforms/stdint.h) # # On WinXP using our own ${FF}/platforms/stdint.h #endif (STDINT_INCLUDE_DIR MATCHES STDINT_INCLUDE_DIR-NOTFOUND) set(FFHEADERS - ${FF}/allocator.hpp - ${FF}/barrier.hpp - ${FF}/buffer.hpp - ${FF}/config.hpp - ${FF}/cycle.h - ${FF}/dc.hpp - ${FF}/dinout.hpp - ${FF}/dnode.hpp - ${FF}/dynlinkedlist.hpp - ${FF}/dynqueue.hpp - ${FF}/farm.hpp - ${FF}/ff_queue.hpp - ${FF}/fftree.hpp - ${FF}/gsearch.hpp - ${FF}/gt.hpp - ${FF}/icl_hash.h - ${FF}/lb.hpp - ${FF}/make_unique.hpp - ${FF}/map.hpp - ${FF}/mapCUDAManaged.hpp - ${FF}/mapper.hpp - ${FF}/mapping_utils.hpp - ${FF}/mdf.hpp - ${FF}/multinode.hpp - ${FF}/node.hpp - ${FF}/oclallocator.hpp - ${FF}/oclnode.hpp - ${FF}/parallel_for.hpp - ${FF}/parallel_for_internals.hpp - ${FF}/pipeline.hpp - ${FF}/poolEvolution.hpp - ${FF}/poolEvolutionCUDA.hpp - ${FF}/selector.hpp - ${FF}/spin-lock.hpp - ${FF}/squeue.hpp - ${FF}/staticlinkedlist.hpp - ${FF}/stencilReduce.hpp - ${FF}/stencilReduceCUDA.hpp - ${FF}/stencilReduceOCL.hpp - ${FF}/stencilReduceOCL_macros.hpp - ${FF}/svector.hpp - ${FF}/sysdep.h - ${FF}/task_internals.hpp - ${FF}/taskf.hpp - ${FF}/tpcallocator.hpp - ${FF}/tpcnode.hpp - ${FF}/ubuffer.hpp - ${FF}/utils.hpp - ${FF}/version.h -) - -set(FFHEADERS_PLAT - ${FF}/platforms/getopt.h - ${FF}/platforms/getopt.hpp - ${FF}/platforms/liblfds.h - ${FF}/platforms/platform.h -) - + ${FF}/allocator.hpp + ${FF}/barrier.hpp + ${FF}/buffer.hpp + ${FF}/config.hpp + ${FF}/cycle.h + ${FF}/dc.hpp + ${FF}/dinout.hpp + ${FF}/dnode.hpp + ${FF}/dynlinkedlist.hpp + ${FF}/dynqueue.hpp + ${FF}/farm.hpp + ${FF}/ff_queue.hpp + ${FF}/fftree.hpp + ${FF}/gsearch.hpp + ${FF}/gt.hpp + ${FF}/icl_hash.h + ${FF}/lb.hpp + ${FF}/make_unique.hpp + ${FF}/map.hpp + ${FF}/mapCUDAManaged.hpp + ${FF}/mapper.hpp + ${FF}/mapping_utils.hpp + ${FF}/mdf.hpp + ${FF}/multinode.hpp + ${FF}/node.hpp + ${FF}/oclallocator.hpp + ${FF}/oclnode.hpp + ${FF}/parallel_for.hpp + ${FF}/parallel_for_internals.hpp + ${FF}/pipeline.hpp + ${FF}/poolEvolution.hpp + ${FF}/poolEvolutionCUDA.hpp + ${FF}/selector.hpp + ${FF}/spin-lock.hpp + ${FF}/squeue.hpp + ${FF}/staticlinkedlist.hpp + ${FF}/stencilReduce.hpp + ${FF}/stencilReduceCUDA.hpp + ${FF}/stencilReduceOCL.hpp + ${FF}/stencilReduceOCL_macros.hpp + ${FF}/svector.hpp + ${FF}/sysdep.h + ${FF}/task_internals.hpp + ${FF}/taskf.hpp + ${FF}/tpcallocator.hpp + ${FF}/tpcnode.hpp + ${FF}/ubuffer.hpp + ${FF}/utils.hpp + ${FF}/version.h) + +set(FFHEADERS_PLAT + ${FF}/platforms/getopt.h + ${FF}/platforms/getopt.hpp + ${FF}/platforms/liblfds.h + ${FF}/platforms/platform.h) set(FFHEADERS_D ${FF}/d/inter.hpp - ${FF}/d/zmqImpl.hpp - ${FF}/d/zmqTransport.hpp - ) + ${FF}/d/zmqImpl.hpp + ${FF}/d/zmqTransport.hpp) if (CMAKE_SYSTEM_PROCESSOR MATCHES "ppc") -message(WARNING "Pinning on Power8 currently is not default" ) -ADD_DEFINITIONS(-DNO_DEFAULT_MAPPING) + MESSAGE( WARNING "Pinning on Power8 currently is not default" ) + ADD_DEFINITIONS(-DNO_DEFAULT_MAPPING) endif (CMAKE_SYSTEM_PROCESSOR MATCHES "ppc") # Examples currently not tested on arm if (CMAKE_SYSTEM_PROCESSOR MATCHES "arm") - find_library(ARM_RT_LIBRARY rt HINT /usr/lib DOC "arm rt library") - if (ARM_RT_LIBRARY) - link_libraries(${ARM_RT_LIBRARY}) - else (ARM_RT_LIBRARY) - message(FATAL_ERROR "Cannot find RT library that is required on arm processor ${ERR_MSG}") - endif (ARM_RT_LIBRARY) + find_library(ARM_RT_LIBRARY rt HINT /usr/lib DOC "arm rt library") + if (ARM_RT_LIBRARY) + link_libraries(${ARM_RT_LIBRARY}) + else (ARM_RT_LIBRARY) + MESSAGE( FATAL_ERROR "Cannot find RT library that is required on arm processor ${ERR_MSG}") + endif (ARM_RT_LIBRARY) option( BUILD_EXAMPLES "Build examples" ON ) # MESSAGE(WARNING "NOT Configuring >>examples<< directory (arm processor detected)") else (CMAKE_SYSTEM_PROCESSOR MATCHES "arm") - if (CMAKE_SYSTEM_NAME MATCHES "Windows") - option( BUILD_EXAMPLES "Build examples" ON ) - MESSAGE(STATUS "Configuring >>examples<< directory") - else (CMAKE_SYSTEM_NAME MATCHES "Windows") - option( BUILD_EXAMPLES "Build examples" ON ) - MESSAGE(STATUS "Configuring >>examples<< directory") - endif (CMAKE_SYSTEM_NAME MATCHES "Windows") + if (CMAKE_SYSTEM_NAME MATCHES "Windows") + option( BUILD_EXAMPLES "Build examples" ON ) + MESSAGE( STATUS "Configuring >>examples<< directory") + else (CMAKE_SYSTEM_NAME MATCHES "Windows") + option( BUILD_EXAMPLES "Build examples" ON ) + MESSAGE( STATUS "Configuring >>examples<< directory") + endif (CMAKE_SYSTEM_NAME MATCHES "Windows") endif (CMAKE_SYSTEM_PROCESSOR MATCHES "arm") # NAME "Description" DEFAULT -option( BUILD_TESTS "Build tests" ON ) -MESSAGE(STATUS "Configuring >>tests<< directory") +option( BUILD_TESTS "Build tests" ON ) +MESSAGE( STATUS "Configuring >>tests<< directory") configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @@ -256,4 +250,3 @@ if( BUILD_TESTS ) endif( BUILD_TESTS ) #FIND_PACKAGE( zeromq ) - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ec8bfade..942ec912 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,145 +1,39 @@ set ( TESTS -bench_masterworker -latptr11 -perf_parfor -perf_parfor2 -perf_test1 -perf_test_alloc1 -perf_test_alloc2 -perf_test_alloc3 -perf_test_noalloc -simplest -sizeof -test1 -test1b -test2 -test3 -test3_farm -test3b -test4 -test5 -test6 -test7 -test8 -test_MISD -test_accelerator -test_accelerator+pinning -test_accelerator2 -test_accelerator3 -test_accelerator_farm+pipe -test_accelerator_ofarm -test_accelerator_ofarm_multiple_freezing -test_accelerator_pipe -test_accelerator_pipe+farm -test_accelerator_pipe2 -test_all-or-none -test_all-to-all -test_all-to-all10 -test_all-to-all11 -test_all-to-all12 -test_all-to-all13 -test_all-to-all14 -test_all-to-all15 -test_all-to-all2 -test_all-to-all3 -test_all-to-all4 -test_all-to-all5 -test_all-to-all6 -test_all-to-all7 -test_all-to-all8 -test_all-to-all9 -test_blk -test_blk2 -test_blk3 -test_blk4 -test_combine -test_combine1 -test_combine10 -test_combine11 -test_combine12 -test_combine2 -test_combine3 -test_combine4 -test_combine5 -test_combine6 -test_combine7 -test_combine8 -test_combine9 -test_dataflow -test_dataflow2 -test_dc -test_devicequery -test_dotprod_parfor -test_dt -test_eosw -test_farm -test_farm+A2A -test_farm+A2A2 -test_farm+farm -test_farm+pipe -test_farm2 -test_ffthread -test_freeze -test_graphsearch -test_lb_affinity -test_mammut -test_map -test_masterworker -test_mdf -test_mdf2 -test_multi_input -test_multi_input10 -test_multi_input11 -test_multi_input2 -test_multi_input3 -test_multi_input4 -test_multi_input5 -test_multi_input6 -test_multi_input7 -test_multi_input8 -test_multi_input9 -test_multi_masterworker -test_multi_output -test_multi_output2 -test_multi_output3 -test_multi_output4 -test_multi_output5 -test_multi_output6 -test_nodeselector -test_noinput_pipe -test_ofarm -test_ofarm2 -test_optimize -test_optimize2 -test_optimize3 -test_optimize4 -test_optimize5 -test_parfor -test_parfor2 -test_parfor_multireduce -test_parfor_multireduce2 -test_parfor_unbalanced -test_parforpipereduce -test_pipe -test_pipe+masterworker -test_pool1 -test_pool2 -test_pool3 -test_scheduling -test_scheduling2 -test_sendq -test_spinBarrier -test_stats -test_stopstartall -test_stopstartthreads -test_stopstartthreads2 -test_stopstartthreads3 -test_taskcallbacks -test_taskf -test_torus -test_torus2 -test_uBuffer -) + simplest + test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 + perf_test1 + test_accelerator test_accelerator2 test_accelerator3 + test_accelerator_farm+pipe test_accelerator_pipe + test_ofarm test_ofarm2 + test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing + test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 + test_freeze + test_masterworker bench_masterworker + test_multi_masterworker test_pipe+masterworker + test_scheduling + test_dt test_torus test_torus2 + perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 + perf_test_noalloc test_uBuffer test_sendq test_spinBarrier + test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 + test_accelerator+pinning + test_dataflow test_dataflow2 + test_noinput_pipe + test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall + test_MISD + test_parfor test_parfor2 test_parforpipereduce + test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 + test_lb_affinity + test_farm test_farm2 + test_pipe test_pipe2 + perf_parfor perf_parfor2 + test_graphsearch + test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 + test_pool1 test_pool2 test_pool3 + test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc + test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 + test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 + test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 + test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2) foreach( t ${TESTS} ) add_executable( ${t}_NONBLOCKING ${t}.cpp) @@ -160,44 +54,43 @@ foreach( t ${TESTS} ) endforeach( t ) # tests with special compilation parameters -set_target_properties(test_scheduling2_NONBLOCKING PROPERTIES - COMPILE_DEFINITIONS LB_CALLBACK) -set_target_properties(test_scheduling2_BLOCKING PROPERTIES - COMPILE_DEFINITIONS LB_CALLBACK) +# set_target_properties(test_scheduling2_NONBLOCKING PROPERTIES +# COMPILE_DEFINITIONS LB_CALLBACK) +# set_target_properties(test_scheduling2_BLOCKING PROPERTIES +# COMPILE_DEFINITIONS LB_CALLBACK) #layer2 tests -add_subdirectory( layer2-tests-HAL ) +# add_subdirectory( layer2-tests-HAL ) # tests MPMC for x86 only -if ( (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") OR ( - CMAKE_SYSTEM_PROCESSOR MATCHES "i386") ) - add_subdirectory( mpmc ) -endif ( ) - +# if ( (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") OR ( +# CMAKE_SYSTEM_PROCESSOR MATCHES "i386") ) +# add_subdirectory( mpmc ) +# endif ( ) # TODO # OpenCL -find_package(OpenCL) -if ( NOT OPENCL_FOUND ) - message ( WARNING "OpenCL not found - skipping OpenCL tests" ) -else ( ) -# add_subdirectory( ocl ) -endif ( ) +# find_package(OpenCL) +# if ( NOT OPENCL_FOUND ) +# MESSAGE( WARNING "OpenCL not found - skipping OpenCL tests" ) +# else ( ) +# add_subdirectory( ocl ) +# endif ( ) # TODO # CUDA -find_package(CUDA) -if (NOT CUDA_FOUND) - message (WARING "CUDA not found - skipping CUDA tests") -else ( ) +# find_package(CUDA) +# if (NOT CUDA_FOUND) +# MESSAGE( WARING "CUDA not found - skipping CUDA tests") +# else ( ) # add_subdirectory( cuda ) -endif ( ) +# endif ( ) # TODO # Distributed -find_package(ZeroMQ) -if(NOT (ZMQ_FOUND)) - message (WARNING "0mq not found - skipping 0mq tests") -else(NOT (ZMQ_FOUND)) -# add_subdirectory( d ) -endif(NOT (ZMQ_FOUND)) +# find_package(ZeroMQ) +# if(NOT (ZMQ_FOUND)) +# MESSAGE( WARNING "0mq not found - skipping 0mq tests") +# else(NOT (ZMQ_FOUND)) +# add_subdirectory( d ) +# endif(NOT (ZMQ_FOUND)) diff --git a/tests/Makefile b/tests/Makefile index 77335224..8401f2cc 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -45,7 +45,7 @@ endif # # -DBLOCKING_MODE It enables passive waiting communication protocol, # This option makes the nodes less reactive, but -# more energy friendly. +# more energy friendly. # # -DTRACE_FASTFLOW It enable statistics. Typically this has an inpact # on the application performance of about 2-4% @@ -62,24 +62,45 @@ endif # contained in the ff directory. # ###############################à############################################# -CC = gcc -CXX = g++ -std=c++17 #-DBLOCKING_MODE -DDEFAULT_BUFFER_CAPACITY=32768 -DFF_BOUNDED_BUFFER -#CC = icc -mmic -#CXX = icpc -mmic -#CXX = clang++ -LINK_OPT = -VERSION = -OPTIMIZE_FLAGS = -O3 -finline-functions -DNDEBUG -CXXFLAGS = -DNO_CMAKE_CONFIG -Wall -CFLAGS = -LDFLAGS = +#CC ?= gcc +#CXX ?= g++ -std=c++17 #-DBLOCKING_MODE -DDEFAULT_BUFFER_CAPACITY=32768 -DFF_BOUNDED_BUFFER +#CC ?= icc -mmic +#CXX ?= icpc -mmic +#CXX ?= clang++ +#LINK_OPT = +#VERSION = +#CFLAGS = +#LDFLAGS = +CXXFLAGS += -std=c++17 +ifdef DEBUG + OPTIMIZE_FLAGS += -g -fno-inline-functions +else + OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG +endif + +ifdef FF_ESAVER + CXXFLAGS += -DFF_ESAVER +endif +ifdef NO_DEFAULT_MAPPING + CXXFLAGS += -DNO_DEFAULT_MAPPING +endif +ifdef BLOCKING_MODE + CXXFLAGS += -DBLOCKING_MODE +endif +ifdef TRACE_FASTFLOW + CXXFLAGS += -DTRACE_FASTFLOW +endif +ifdef DEFAULT_BUFFER_CAPACITY + CXXFLAGS += -DDEFAULT_BUFFER_CAPACITY=${DEFAULT_BUFFER_CAPACITY} +endif +CXXFLAGS += -DNO_CMAKE_CONFIG -Wall INCS = -I.. LIBS = -pthread -#MAMMUT_HOME = ../mammut +#MAMMUT_HOME = ../mammut ifdef MAMMUT_HOME -CXX += -DMAMMUT -INCS += -I $(MAMMUT_HOME) -LIBS += $(MAMMUT_HOME)/mammut/libmammut.a + CXX += -DMAMMUT + INCS += -I $(MAMMUT_HOME) + LIBS += $(MAMMUT_HOME)/mammut/libmammut.a endif ARCH = -march=$(shell uname -m) @@ -87,10 +108,10 @@ ARCH = -march=$(shell uname -m) # FIXME: quick and dirty fix for ARM platform ifneq ($(findstring $(shell uname -m),armv7l),) ARCH = -mcpu=cortex-a9 -march=armv7-a - LIBS += -lrt + LIBS += -lrt -latomic endif -OS = $(shell uname) +OS = $(shell uname) ifeq ($(ARCH),-march=x86_64) ARCH = -march=core2 endif @@ -103,7 +124,8 @@ ifeq ($(strip $(OS)),Darwin) endif endif -INCLUDES = -I. $(INCS) +#INCLUDES = -I. $(INCS) +INCLUDES = $(INCS) TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 From 115e2be5957d91d4cec3249ce217975525496445 Mon Sep 17 00:00:00 2001 From: lucarin91 Date: Fri, 10 Jul 2020 11:22:03 +0200 Subject: [PATCH 006/202] fixing gcc -Wextra warning In particular the warnings was - uninitialized base class in derived constructor - unused functions parameter - ignored return qualifiers --- ff/all2all.hpp | 12 ++++++------ ff/allocator.hpp | 6 +++--- ff/barrier.hpp | 2 +- ff/buffer.hpp | 4 ++-- ff/combine.hpp | 6 +++--- ff/config.hpp | 2 ++ ff/dc.hpp | 6 +++--- ff/farm.hpp | 10 +++++----- ff/gt.hpp | 4 ++-- ff/lb.hpp | 18 ++++++++--------- ff/mapping_utils.hpp | 4 ++-- ff/mdf.hpp | 6 +++--- ff/multinode.hpp | 10 +++++----- ff/node.hpp | 31 +++++++++++++++--------------- ff/parallel_for.hpp | 4 ++-- ff/parallel_for_internals.hpp | 21 +++++++++++++++++--- ff/pipeline.hpp | 8 ++++---- ff/selector.hpp | 2 +- ff/spin-lock.hpp | 6 +++--- ff/staticlinkedlist.hpp | 2 +- ff/utils.hpp | 4 ++-- tests/sizeof.cpp | 2 +- tests/test3.cpp | 2 +- tests/test8.cpp | 3 +++ tests/test_accelerator+pinning.cpp | 2 +- tests/test_all-or-none.cpp | 4 ++-- tests/test_all-to-all11.cpp | 2 +- tests/test_all-to-all12.cpp | 4 ++-- tests/test_all-to-all14.cpp | 4 ++-- tests/test_all-to-all15.cpp | 6 +++--- tests/test_all-to-all16.cpp | 2 +- tests/test_all-to-all2.cpp | 4 ++-- tests/test_all-to-all3.cpp | 4 ++-- tests/test_combine.cpp | 2 +- tests/test_combine10.cpp | 2 +- tests/test_combine12.cpp | 2 +- tests/test_combine13.cpp | 2 +- tests/test_combine14.cpp | 2 +- tests/test_combine4.cpp | 2 +- tests/test_combine5.cpp | 2 +- tests/test_combine7.cpp | 2 +- tests/test_dc.cpp | 2 +- tests/test_farm+A2A.cpp | 2 +- tests/test_farm+A2A2.cpp | 2 +- tests/test_farm2.cpp | 2 +- tests/test_ffthread.cpp | 2 +- tests/test_map.cpp | 2 +- tests/test_multi_input10.cpp | 2 +- tests/test_multi_input11.cpp | 2 +- tests/test_multi_input9.cpp | 2 +- tests/test_multi_output3.cpp | 4 ++-- tests/test_multi_output5.cpp | 4 ++-- tests/test_multi_output6.cpp | 2 +- tests/test_noinput_pipe.cpp | 2 +- tests/test_optimize.cpp | 4 ++-- tests/test_optimize2.cpp | 2 +- tests/test_optimize3.cpp | 2 +- tests/test_optimize4.cpp | 2 +- tests/test_optimize5.cpp | 8 ++++---- tests/test_parforpipereduce.cpp | 2 +- tests/test_pipe2.cpp | 4 ++-- tests/test_pool3.cpp | 2 +- tests/test_sendq.cpp | 2 +- tests/test_stopstartall.cpp | 2 +- tests/test_stopstartthreads3.cpp | 2 +- tests/test_taskcallbacks.cpp | 8 +++++--- tests/test_uBuffer.cpp | 6 +++--- 67 files changed, 161 insertions(+), 138 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 4504d1a6..30904ff3 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -644,9 +644,9 @@ class ff_a2a: public ff_node { return 0; } - bool init_input_blocking(pthread_mutex_t *&m, - pthread_cond_t *&c, - bool feedback=true) { + bool init_input_blocking(pthread_mutex_t *&, + pthread_cond_t *&, + bool /*feedback*/=true) { size_t nworkers1 = workers1.size(); for(size_t i=0;iisComp()) { comp_nodes.push_back(new ff_comb(*(ff_comb*)s)); @@ -638,7 +638,7 @@ class ff_comb: public ff_minode { // consumer bool init_input_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { ff_node *n = getFirst(); if (n->isMultiInput()) { // inits local gt, which is used for gathering tasks.... @@ -657,7 +657,7 @@ class ff_comb: public ff_minode { // producer bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { return comp_nodes[1]->init_output_blocking(m,c); } void set_output_blocking(pthread_mutex_t *&m, diff --git a/ff/config.hpp b/ff/config.hpp index 999064e2..02e1a361 100644 --- a/ff/config.hpp +++ b/ff/config.hpp @@ -240,5 +240,7 @@ // OpenCL additional code needed to compile kernels #define FF_OPENCL_DATATYPES_FILE "ff_opencl_datatypes.cl" +// Convenience macros. +#define FF_IGNORE_UNUSED(x) static_cast(x) #endif /* FF_CONFIG_HPP */ diff --git a/ff/dc.hpp b/ff/dc.hpp index 4f40e314..6232ce1a 100644 --- a/ff/dc.hpp +++ b/ff/dc.hpp @@ -133,7 +133,7 @@ class ff_DC:public ff_node_t { * @param res combine result * @param subops suboperands for this recursion step. They are deleted after the Combine */ - static void CombineFunction(void *w,const std::function&,ResultType&)>& combine_fn, std::vector* ress, ResultType* res, std::vector *subops) + static void CombineFunction(void * /*w*/,const std::function&,ResultType&)>& combine_fn, std::vector* ress, ResultType* res, std::vector *subops) { combine_fn(*ress,*res); @@ -506,7 +506,7 @@ class ff_DC:public ff_node_t { } } - void eosnotify(ssize_t id=-1) { lb->broadcast_task(FF_EOS); } + void eosnotify(ssize_t /*id*/=-1) { lb->broadcast_task(FF_EOS); } int wait_freezing() { return lb->wait_lb_freezing(); } private: @@ -556,7 +556,7 @@ class ff_DC:public ff_node_t { ff_DC(const divide_f_t& divide_fn, const combine_f_t& combine_fn, const seq_f_t& seq_fn, const cond_f_t& cond_fn, const OperandType& op, ResultType& res, - int numw, size_t outstandingTasks=DEFAULT_OUTSTANDING_TASKS, + int numw, size_t /*outstandingTasks*/=DEFAULT_OUTSTANDING_TASKS, int maxnw=ff_numCores(), void (*schedRelaxF)(unsigned long)=NULL): _divide_fn(divide_fn), _combine_fn(combine_fn), _seq_fn(seq_fn), _condition_fn(cond_fn) { diff --git a/ff/farm.hpp b/ff/farm.hpp index 96f0f177..440789bf 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -668,7 +668,7 @@ class ff_farm: public ff_node { // consumer virtual inline bool init_input_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { bool r = lb->init_input_blocking(m,c); if (!r) return false; // NOTE: for all registered input node (or buffernode) we have to set the @@ -683,7 +683,7 @@ class ff_farm: public ff_node { // producer virtual inline bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { if (collector && !collector_removed) { if (collector == (ff_node*)gt) return gt->init_output_blocking(m,c); @@ -804,7 +804,7 @@ class ff_farm: public ff_node { for(size_t i=0;i& W, size_t size) { void *task; for(size_t i=0;iget(&task)); + do {} while(!W[i]->get(&task)); assert((task == FF_EOS) || (task == FF_EOS_NOFREEZE)); } } diff --git a/ff/mapping_utils.hpp b/ff/mapping_utils.hpp index 7c43aa89..794ef313 100644 --- a/ff/mapping_utils.hpp +++ b/ff/mapping_utils.hpp @@ -271,13 +271,13 @@ static inline ssize_t ff_realNumCores() { if (!found) S.insert(i); if (--cnt==0) { n=0; - std::for_each(S.begin(), S.end(), [&n](int const& i) { ++n;}); + std::for_each(S.begin(), S.end(), [&n](int const&) { ++n;}); return n; } } } n=0; - std::for_each(S.begin(), S.end(), [&n](int const& i) { ++n;}); + std::for_each(S.begin(), S.end(), [&n](int const&) { ++n;}); return n; } while(0); #endif // HAVE_PTHREAD_SETAFFINITY_NP diff --git a/ff/mdf.hpp b/ff/mdf.hpp index 896caa30..201d8839 100644 --- a/ff/mdf.hpp +++ b/ff/mdf.hpp @@ -70,7 +70,7 @@ class ff_mdf:public ff_pipeline { virtual inline void setMaxTasks(size_t) {} virtual inline void activate(bool) {} virtual inline void alloc_and_send(std::vector &, base_f_t *) {} - virtual inline void thaw(bool freeze=false,ssize_t=-1) {}; + virtual inline void thaw(bool /*freeze*/=false,ssize_t=-1) {}; virtual inline int wait_freezing() { return 0; }; }; template @@ -171,7 +171,7 @@ class ff_mdf:public ff_pipeline { return baseSched::GO_ON; } - void eosnotify(ssize_t id=-1) { lb->broadcast_task(FF_EOS); } + void eosnotify(ssize_t /*id*/=-1) { lb->broadcast_task(FF_EOS); } int wait_freezing() { return lb->wait_lb_freezing(); } private: @@ -256,7 +256,7 @@ class ff_mdf:public ff_pipeline { ff_numCores()); farmworkers=(std::min)(ff_numCores(),nw); } - void setThreshold(size_t th=0) {} // FIX: + void setThreshold(size_t /*th*/=0) {} // FIX: // FIX: TODO diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 8cf4ff5e..6fdabe82 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -219,7 +219,7 @@ class ff_minode: public ff_node { } gt->set_filter(filter); } - ff_minode(const ff_minode& n) { + ff_minode(const ff_minode& n) : ff_node(n) { // here we re-initialize a new gatherer gt = new ff_gatherer(n.gt->max_nworkers); if (!gt) { @@ -540,7 +540,7 @@ class ff_monode: public ff_node { lb->set_filter(filter); } - ff_monode(const ff_monode& n) { + ff_monode(const ff_monode& n) : ff_node(n) { // here we re-initialize a new gatherer lb = new ff_loadbalancer(n.lb->max_nworkers); if (!lb) { @@ -681,7 +681,7 @@ class ff_monode: public ff_node { * * \return 0 if successful, otherwise -1 is returned. */ - int run(bool skip_init=false) { + int run(bool /*skip_init*/=false) { if (!lb) return -1; if (lb->get_filter() == nullptr) lb->set_filter(this); @@ -864,7 +864,7 @@ struct internal_mo_transformer: ff_monode { cleanup=true; ff_monode::set_filter(n); } - internal_mo_transformer(const internal_mo_transformer& t) { + internal_mo_transformer(const internal_mo_transformer& t) : ff_monode(t) { cleanup=t.cleanup; n = t.n; ff_monode::set_filter(n); @@ -931,7 +931,7 @@ struct internal_mi_transformer: ff_minode { ff_minode::set_filter(n); } - internal_mi_transformer(const internal_mi_transformer& t) { + internal_mi_transformer(const internal_mi_transformer& t) : ff_minode(t) { cleanup=t.cleanup; n = t.n; ff_minode::set_filter(n); diff --git a/ff/node.hpp b/ff/node.hpp index b0293536..3d5ccc26 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -297,8 +297,8 @@ class ff_thread { #if defined(FF_TASK_CALLBACK) - virtual void callbackIn(void *t=NULL) { } - virtual void callbackOut(void *t=NULL) { } + virtual void callbackIn(void * =NULL) { } + virtual void callbackOut(void * =NULL) { } #endif public: @@ -585,7 +585,7 @@ class ff_node { // consumer virtual inline bool init_input_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { if (cons_m == nullptr) { assert(cons_c==nullptr); cons_m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); @@ -600,7 +600,7 @@ class ff_node { // producer virtual inline bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { if (prod_m == nullptr) { assert(prod_c==nullptr); prod_m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); @@ -618,6 +618,7 @@ class ff_node { assert(canoverwrite || (p_cons_m == nullptr && p_cons_c == nullptr) || (p_cons_m == m && p_cons_c == c)); + FF_IGNORE_UNUSED(canoverwrite); p_cons_m = m, p_cons_c = c; } @@ -729,10 +730,10 @@ class ff_node { return 0; } - virtual inline int set_input(const svector & w) { return -1;} + virtual inline int set_input(const svector &) { return -1;} virtual inline int set_input(ff_node *) { return -1;} virtual inline int set_input_feedback(ff_node *) { return -1;} - virtual inline int set_output(const svector & w) { return -1;} + virtual inline int set_output(const svector &) { return -1;} virtual inline int set_output(ff_node *n) { return set_output_buffer(n->get_in_buffer()); } @@ -740,14 +741,14 @@ class ff_node { virtual inline void set_input_channelid(ssize_t, bool=true) {} virtual inline void get_out_nodes(svector&w) { w.push_back(this); } - virtual inline void get_out_nodes_feedback(svector&w) {} + virtual inline void get_out_nodes_feedback(svector&) {} virtual inline void get_in_nodes(svector&w) { w.push_back(this); } - virtual inline void get_in_nodes_feedback(svector&w) {} + virtual inline void get_in_nodes_feedback(svector&) {} virtual int prepare() { prepared=true; return 0; } virtual int dryrun() { if (!prepared) return prepare(); return 0; } - virtual void set_scheduling_ondemand(const int inbufferentries=1) {} + virtual void set_scheduling_ondemand(const int /*inbufferentries*/=1) {} virtual int ondemand_buffer() const { return 0;} @@ -883,7 +884,7 @@ class ff_node { void *const EOSW = FF_EOSW; - ff_node(const ff_node& node):ff_node() {} + ff_node(const ff_node&):ff_node() {} /** * \brief Destructor, polymorphic deletion through base pointer is allowed. @@ -972,7 +973,7 @@ class ff_node { * (this is not possible in the svc_end method). * The parameter \param id is the ID of the channel that received the EOS. */ - virtual void eosnotify(ssize_t id=-1) {} + virtual void eosnotify(ssize_t /*id*/=-1) {} /** * \brief Returns the number of EOS the node has to receive before terminating. @@ -994,8 +995,8 @@ class ff_node { #if defined(FF_TASK_CALLBACK) - virtual void callbackIn(void *t=NULL) { } - virtual void callbackOut(void *t=NULL) { } + virtual void callbackIn(void * =NULL) { } + virtual void callbackOut(void * =NULL) { } #endif /** @@ -1207,7 +1208,7 @@ class ff_node { /** * used for composition (see ff_comb) */ - static inline bool ff_send_out_comp(void * task,unsigned long retry,unsigned long ticks, void *obj) { + static inline bool ff_send_out_comp(void * task,unsigned long /*retry*/,unsigned long /*ticks*/, void *obj) { return ((ff_node *)obj)->push_comp_local(task); } @@ -1284,7 +1285,7 @@ class ff_node { callback=cb; callback_arg=arg; } - virtual void registerAllGatherCallback(int (*cb)(void *,void **, void*), void * arg) {} + virtual void registerAllGatherCallback(int (* /*cb*/)(void *,void **, void*), void * /*arg*/) {} /* WARNING: these method must be called before the run() method */ virtual void blocking_mode(bool blk=true) { diff --git a/ff/parallel_for.hpp b/ff/parallel_for.hpp index 782cacdb..fc0a1727 100644 --- a/ff/parallel_for.hpp +++ b/ff/parallel_for.hpp @@ -381,7 +381,7 @@ class ParallelForReduce: public ff_forall_farm > { * small kernels), \p false blocking support * @param skipWarmup Skip warmup phase (autotuning) */ - ParallelForReduce(const long maxnw, bool spinWait, bool skipWarmup, bool spinbarrier): + ParallelForReduce(const long maxnw, bool /*spinWait*/, bool /*skipWarmup*/, bool /*spinbarrier*/): ff_forall_farm >(maxnw,false, true, false) {} @@ -719,7 +719,7 @@ class ParallelForPipeReduce: public ff_pipeline { } reduce; public: - explicit ParallelForPipeReduce(const long maxnw=FF_AUTO, bool spinwait=false, bool spinbarrier=false): + explicit ParallelForPipeReduce(const long maxnw=FF_AUTO, bool spinwait=false, bool /*spinbarrier*/=false): pfr(maxnw,false,true,false) // skip loop warmup and disable spinwait/spinbarrier { ff_pipeline::add_stage(&pfr); diff --git a/ff/parallel_for_internals.hpp b/ff/parallel_for_internals.hpp index 423e80f1..f5bb6034 100644 --- a/ff/parallel_for_internals.hpp +++ b/ff/parallel_for_internals.hpp @@ -129,6 +129,7 @@ namespace ff { name.setloop(begin,end,step,chunk,nw); \ auto F_##name = [&] (const long ff_start_##idx, const long ff_stop_##idx, \ const int _ff_thread_id, const int) { \ + FF_IGNORE_UNUSED(_ff_thread_id); \ PRAGMA_IVDEP; \ for(long idx=ff_start_##idx;idxsetloop(begin,end,step,chunk,nw); \ auto F_##name = [&] (const long ff_start_##idx, const long ff_stop_##idx, \ const int _ff_thread_id, const int) { \ + FF_IGNORE_UNUSED(_ff_thread_id); \ PRAGMA_IVDEP; \ for(long idx=ff_start_##idx;idxsetloop(begin,end,step,chunk,nw); \ auto F_##name = [&] (const long ff_start_##idx, const long ff_stop_##idx, \ const int _ff_thread_id, const int) { \ + FF_IGNORE_UNUSED(_ff_thread_id); /* here you have to define the for loop using ff_start/stop_##idx */ /* this is equivalent to FF_PARFOR2_START but the start/stop indexes have a fixed name */ @@ -270,6 +276,7 @@ namespace ff { name->setloop(begin,end,step,chunk,nw); \ auto F_##name = [&] (const long ff_start_idx, const long ff_stop_idx, \ const int _ff_thread_id, const int) { \ + FF_IGNORE_UNUSED(_ff_thread_id); /* here you have to define the for loop using ff_start/stop_idx */ @@ -278,6 +285,7 @@ namespace ff { name->setloop(begin,end,step,chunk,nw); \ auto F_##name = [&] (const long ff_start_##idx, const long ff_stop_##idx, \ const int _ff_thread_id, const type&) { \ + FF_IGNORE_UNUSED(_ff_thread_id); \ PRAGMA_IVDEP; \ for(long idx=ff_start_##idx;idxgetnw())*(-chunk*step); \ const long _ff_jump1=(-chunk*step); \ + FF_IGNORE_UNUSED(_ff_thread_id); \ PRAGMA_IVDEP; \ for(long _ff_##idx=ff_start_##idx;_ff_##idxsetloop(begin,end,step,chunk,nw); \ auto F_##name = [&] (const long ff_start_idx, const long ff_stop_idx, \ const int _ff_thread_id, const type&) { \ + FF_IGNORE_UNUSED(_ff_thread_id); /* here you have to use the fixed indexes ff_idx_start, ff_idx_stop */ #define FF_PARFOR_STOP(name) \ @@ -327,6 +337,7 @@ namespace ff { auto idtt_##name =identity; \ auto F_##name =[&](const long ff_start_##idx, const long ff_stop_##idx, \ const int _ff_thread_id, decltype(var) &var) { \ + FF_IGNORE_UNUSED(_ff_thread_id); \ PRAGMA_IVDEP \ for(long idx=ff_start_##idx;idxsetloop(begin,end,step,chunk,nw); \ auto idtt_##name =identity; \ auto F_##name =[&](const long ff_start_idx, const long ff_stop_idx, \ - const int _ff_thread_id, decltype(var) &var) { + const int _ff_thread_id, decltype(var) &var) { \ + FF_IGNORE_UNUSED(_ff_thread_id); + #define FF_PARFORREDUCE_START_STATIC(name, var,identity, idx,begin,end,step, chunk, nw) \ @@ -617,6 +630,8 @@ class forall_Scheduler: public ff_node { task->set(r, r + _step); return true; #else + FF_IGNORE_UNUSED(task); + FF_IGNORE_UNUSED(wid); error("To use nextTaskConcurrentNoStealing you need to define macro FF_PARFOR_PASSIVE_NOSTEALING\n"); return false; #endif @@ -783,7 +798,7 @@ class forall_Scheduler: public ff_node { } return GO_OUT; } - if (nextTask((forall_task_t*)t, (const int) wid)) lb->ff_send_out_to(t, int(wid)); + if (nextTask((forall_task_t*)t, (int) wid)) lb->ff_send_out_to(t, int(wid)); else { if (!eossent[wid]) { lb->ff_send_out_to((workersspinwait?EOS_NOFREEZE:GO_OUT), int(wid)); diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 32e86d65..05488342 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -523,7 +523,7 @@ class ff_pipeline: public ff_node { out_buffer_entries(out_buffer_entries) { } - ff_pipeline(const ff_pipeline& p) { + ff_pipeline(const ff_pipeline& p) : ff_node(p) { if (p.prepared) { error("ff_pipeline, copy constructor, the input pipeline is already prepared\n"); return; @@ -1467,7 +1467,7 @@ class ff_pipeline: public ff_node { // returns the pipeline starting time const struct timeval startTime() { return nodes_list[0]->getstarttime(); } - void* svc(void * task) { return NULL; } + void* svc(void *) { return NULL; } int svc_init() { return -1; }; void svc_end() {} @@ -1491,13 +1491,13 @@ class ff_pipeline: public ff_node { // consumer virtual inline bool init_input_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { return nodes_list[0]->init_input_blocking(m,c); } // producer virtual inline bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, - bool feedback=true) { + bool /*feedback*/=true) { const int last = static_cast(nodes_list.size())-1; if (last<0) return false; return nodes_list[last]->init_output_blocking(m,c); diff --git a/ff/selector.hpp b/ff/selector.hpp index 5971f7ca..41db2002 100644 --- a/ff/selector.hpp +++ b/ff/selector.hpp @@ -141,7 +141,7 @@ class ff_nodeSelector: public ff_node_t { return devices.size()-1; } - const size_t numNodes() const { return devices.size(); } + size_t numNodes() const { return devices.size(); } int run(bool = false) { return ff_node::run(); } diff --git a/ff/spin-lock.hpp b/ff/spin-lock.hpp index d5816511..c532c20f 100644 --- a/ff/spin-lock.hpp +++ b/ff/spin-lock.hpp @@ -79,8 +79,8 @@ ALIGN_TO_PRE(CACHE_LINE_SIZE) struct AtomicFlagWrapper { typedef AtomicFlagWrapper lock_t[1]; -_INLINE void init_unlocked(lock_t l) { } -_INLINE void init_locked(lock_t l) { abort(); } +_INLINE void init_unlocked(lock_t) { } +_INLINE void init_locked(lock_t) { abort(); } _INLINE void spin_lock(lock_t l) { while(l->test_and_set(std::memory_order_acquire)) ; } @@ -164,7 +164,7 @@ ALIGN_TO_PRE(CACHE_LINE_SIZE) struct CLHSpinLock { typedef CLHSpinLock clh_lock_t[1]; _INLINE void init_unlocked(clh_lock_t l) { l->init();} -_INLINE void init_locked(clh_lock_t l) { abort(); } +_INLINE void init_locked(clh_lock_t) { abort(); } _INLINE void spin_lock(clh_lock_t l, const int pid) { l->spin_lock(pid); } _INLINE void spin_unlock(clh_lock_t l, const int pid) { l->spin_unlock(pid); } diff --git a/ff/staticlinkedlist.hpp b/ff/staticlinkedlist.hpp index 1666b9a2..251054af 100644 --- a/ff/staticlinkedlist.hpp +++ b/ff/staticlinkedlist.hpp @@ -130,7 +130,7 @@ class staticlinkedlist { */ enum {DEFAULT_CACHE_SIZE=1024}; - staticlinkedlist(int cachesize=DEFAULT_CACHE_SIZE, bool fillcache=false){ + staticlinkedlist(int cachesize=DEFAULT_CACHE_SIZE, bool /*fillcache*/=false){ // avoid unused field warning for padding if (longxCacheLine>1) padding1[0]=padding2[0]; diff --git a/ff/utils.hpp b/ff/utils.hpp index c9391176..5f083b32 100644 --- a/ff/utils.hpp +++ b/ff/utils.hpp @@ -63,7 +63,7 @@ enum { START_TIME=0, STOP_TIME=1, GET_TIME=2 }; /*!!!----Mehdi-- required for DSRIMANAGER NODE----!!*/ static inline void waitCall(double milisec, double sec){ if(milisec!=0.0 || sec!=0.0){ - struct timespec req = {0}; + struct timespec req; req.tv_sec = sec; req.tv_nsec = milisec * 1000000L; nanosleep(&req, (struct timespec *)NULL); @@ -226,7 +226,7 @@ static inline unsigned int nextMultipleOfIf(unsigned int x, unsigned int m) { } -static inline double ffTime(int tag, bool lock=false) { +static inline double ffTime(int tag/*, bool lock=false*/) { static struct timeval tv_start = {0,0}; static struct timeval tv_stop = {0,0}; // needed to protect gettimeofday diff --git a/tests/sizeof.cpp b/tests/sizeof.cpp index fda79b90..7ddc6090 100644 --- a/tests/sizeof.cpp +++ b/tests/sizeof.cpp @@ -39,7 +39,7 @@ typedef SSIZE_T ssize_t; #endif -int main(int argc, char * argv[]) { +int main() { std::cout << "char " << sizeof(char) << "\n"; std::cout << "short " << sizeof(short) << "\n"; diff --git a/tests/test3.cpp b/tests/test3.cpp index 5cb6c2b8..de83fc9a 100644 --- a/tests/test3.cpp +++ b/tests/test3.cpp @@ -71,7 +71,7 @@ class Stage: public ff_node { }; -int main(int argc, char * argv[]) { +int main() { int streamlen = 1000; // bild a 2-stage pipeline ff_pipeline pipe; diff --git a/tests/test8.cpp b/tests/test8.cpp index bfcc5dbf..0e54b62a 100644 --- a/tests/test8.cpp +++ b/tests/test8.cpp @@ -51,6 +51,9 @@ void * operator new(size_t size) { void operator delete(void* p) noexcept { return ff_free(p); } +void operator delete(void* p, size_t) noexcept { + return ff_free(p); +} typedef std::vector Task; struct firstStage: ff_node_t { diff --git a/tests/test_accelerator+pinning.cpp b/tests/test_accelerator+pinning.cpp index 49d01acd..dc819006 100644 --- a/tests/test_accelerator+pinning.cpp +++ b/tests/test_accelerator+pinning.cpp @@ -35,7 +35,7 @@ class TasksGenerator: public ff_node { nsims(nsims) {} - void * svc(void* task) { + void * svc(void*) { //prepare sim. tasks and send them out vector *tasks = new vector(nsims); for(int i=0; i { Generator(long niter):niter(niter) {} - Task_t* svc(Task_t* item) { + Task_t* svc(Task_t*) { // NOTE: if you change the number of writes please remember to // update NUM_WRITES_PER_ITARATION for(int i=0;i { // last stage of the pipeline struct Gatherer: ff_minode_t { - Task_t* svc(Task_t* item) { return GO_ON; } + Task_t* svc(Task_t*) { return GO_ON; } void svc_end() { printf("number of writes %ld\n", some_global_state); } diff --git a/tests/test_all-to-all11.cpp b/tests/test_all-to-all11.cpp index 7a5338b5..a356b766 100644 --- a/tests/test_all-to-all11.cpp +++ b/tests/test_all-to-all11.cpp @@ -154,7 +154,7 @@ struct Worker: ff_monode_t { return GO_ON; } - void eosnotify(ssize_t id) { + void eosnotify(ssize_t) { broadcast_task(EOS); ff_send_out_to(EOS, get_num_feedbackchannels()); } diff --git a/tests/test_all-to-all12.cpp b/tests/test_all-to-all12.cpp index 74f05abe..7d560647 100644 --- a/tests/test_all-to-all12.cpp +++ b/tests/test_all-to-all12.cpp @@ -89,7 +89,7 @@ struct Worker1: ff_monode_t { struct MultiInputHelper2: ff_minode_t { long *svc(long *in) { return in; } - void eosnotify(ssize_t id) { + void eosnotify(ssize_t) { if (++neos == get_num_inchannels()) ff_send_out(EOS); } @@ -104,7 +104,7 @@ struct Worker2: ff_monode_t { return GO_ON; } - void eosnotify(ssize_t id) { + void eosnotify(ssize_t) { ff_send_out_to(last, get_num_feedbackchannels() ); ff_send_out_to(EOS, get_num_feedbackchannels()); } diff --git a/tests/test_all-to-all14.cpp b/tests/test_all-to-all14.cpp index 39d11b41..512741b6 100644 --- a/tests/test_all-to-all14.cpp +++ b/tests/test_all-to-all14.cpp @@ -55,7 +55,7 @@ using namespace ff; struct Generator: ff_node_t { Generator(long streamlen):streamlen(streamlen) {} - long* svc(long* in) { + long* svc(long*) { for(long i=1;i<=streamlen;++i) { ff_send_out((long*)i); } @@ -64,7 +64,7 @@ struct Generator: ff_node_t { long streamlen; }; struct Gatherer: ff_minode_t { - long* svc(long* in) { + long* svc(long*) { return GO_ON; } }; diff --git a/tests/test_all-to-all15.cpp b/tests/test_all-to-all15.cpp index 52f64a47..0d57c7b1 100644 --- a/tests/test_all-to-all15.cpp +++ b/tests/test_all-to-all15.cpp @@ -53,7 +53,7 @@ using namespace ff; const long ntasks=100000; struct Source: ff_monode_t { - long* svc(long* in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { ff_send_out((long*)i); } @@ -63,7 +63,7 @@ struct Source: ff_monode_t { struct PipeA2A: ff_pipeline { struct Sink: ff_minode_t { - long* svc(long* in) { + long* svc(long*) { ++cnt; return GO_ON; } @@ -95,7 +95,7 @@ struct PipeA2A: ff_pipeline { } }; - PipeA2A(int nsources) { + PipeA2A(int /*nsources*/) { const long nworkers=2; ff_a2a* a2a = new ff_a2a; diff --git a/tests/test_all-to-all16.cpp b/tests/test_all-to-all16.cpp index cccace2d..7b423bc6 100644 --- a/tests/test_all-to-all16.cpp +++ b/tests/test_all-to-all16.cpp @@ -90,7 +90,7 @@ struct SourcePipe: ff_pipeline { if (get_my_id() == 0) ff::ffTime(ff::START_TIME); return 0; } - long* svc(long* in) { + long* svc(long*) { if (ntasks<=0) return EOS; long *x = new long; F(*x); diff --git a/tests/test_all-to-all2.cpp b/tests/test_all-to-all2.cpp index 117f2493..ee5ae52c 100644 --- a/tests/test_all-to-all2.cpp +++ b/tests/test_all-to-all2.cpp @@ -100,7 +100,7 @@ struct Even: ff_node_t { delete in; return GO_ON; } - void eosnotify(ssize_t id=-1) { + void eosnotify(ssize_t=-1) { printf("Even EOS received\n"); ff_send_out(new long(sum)); } @@ -113,7 +113,7 @@ struct Odd: ff_node_t { delete in; return GO_ON; } - void eosnotify(ssize_t id=-1) { + void eosnotify(ssize_t=-1) { printf("Odd EOS received\n"); ff_send_out(new long(sum)); } diff --git a/tests/test_all-to-all3.cpp b/tests/test_all-to-all3.cpp index d1962619..f670bb71 100644 --- a/tests/test_all-to-all3.cpp +++ b/tests/test_all-to-all3.cpp @@ -96,7 +96,7 @@ struct Even: ff_node_t { delete in; return GO_ON; } - void eosnotify(ssize_t id=-1) { + void eosnotify(ssize_t=-1) { printf("Even received EOS\n"); ff_send_out(new long(sum)); } @@ -109,7 +109,7 @@ struct Odd: ff_node_t { delete in; return GO_ON; } - void eosnotify(ssize_t id=-1) { + void eosnotify(ssize_t=-1) { printf("Odd received EOS\n"); ff_send_out(new long(sum)); } diff --git a/tests/test_combine.cpp b/tests/test_combine.cpp index a5b6eaec..f3c9de8c 100644 --- a/tests/test_combine.cpp +++ b/tests/test_combine.cpp @@ -76,7 +76,7 @@ struct secondStage3: ff_node_t { // 2nd stage } }; struct thirdStage: ff_node_t { // 3rd stage - long *svc(long *task) { + long *svc(long *) { ticks_wait(worktime); return GO_ON; } diff --git a/tests/test_combine10.cpp b/tests/test_combine10.cpp index 597c9d94..b59e7bed 100644 --- a/tests/test_combine10.cpp +++ b/tests/test_combine10.cpp @@ -39,7 +39,7 @@ using namespace ff; struct First: ff_node_t { First(const int ntasks):ntasks(ntasks) {} - long* svc(long*in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { struct timespec req; req.tv_sec = 0; diff --git a/tests/test_combine12.cpp b/tests/test_combine12.cpp index 5bc8d44a..ba3e6272 100644 --- a/tests/test_combine12.cpp +++ b/tests/test_combine12.cpp @@ -83,7 +83,7 @@ struct Collector1: ff_node_t { long cnt=1; }; struct Collector2: ff_node_t { - long *svc(long *in) { + long *svc(long *) { return GO_ON; } }; diff --git a/tests/test_combine13.cpp b/tests/test_combine13.cpp index fe3d9847..12b5efc3 100644 --- a/tests/test_combine13.cpp +++ b/tests/test_combine13.cpp @@ -90,7 +90,7 @@ struct Collector: ff_monode_t { }; struct Sink: ff_minode_t { - long* svc(long* in) { + long* svc(long*) { return GO_ON; } }; diff --git a/tests/test_combine14.cpp b/tests/test_combine14.cpp index bd75e96f..9e15377c 100644 --- a/tests/test_combine14.cpp +++ b/tests/test_combine14.cpp @@ -91,7 +91,7 @@ struct Collector: ff_monode_t { }; struct Sink: ff_minode_t { - long* svc(long* in) { + long* svc(long*) { return GO_ON; } }; diff --git a/tests/test_combine4.cpp b/tests/test_combine4.cpp index 30d8cb71..d911caad 100644 --- a/tests/test_combine4.cpp +++ b/tests/test_combine4.cpp @@ -87,7 +87,7 @@ struct Worker1: ff_node_t { struct Collector: ff_minode_t { Collector(long ntasks):ntasks(ntasks) {} - long *svc(long *in) { + long *svc(long *) { --ntasks; // std::cout << "received input from " << get_channel_id() << "\n"; return GO_ON; diff --git a/tests/test_combine5.cpp b/tests/test_combine5.cpp index 5e66a69e..9e609413 100644 --- a/tests/test_combine5.cpp +++ b/tests/test_combine5.cpp @@ -85,7 +85,7 @@ struct Worker1: ff_node_t { struct Collector: ff_minode_t { Collector(long ntasks):ntasks(ntasks) {} - long *svc(long *in) { + long *svc(long *) { --ntasks; //std::cout << "Collector received input from " << get_channel_id() << "\n"; return GO_ON; diff --git a/tests/test_combine7.cpp b/tests/test_combine7.cpp index fb5ca1cf..d7a8d8c1 100644 --- a/tests/test_combine7.cpp +++ b/tests/test_combine7.cpp @@ -99,7 +99,7 @@ struct Collector2: ff_minode_t { long ntasks; }; -int main(int argc, char* argv[]) { +int main() { int nworkers1 = 2; int nworkers2 = 3; int ntasks = 150; diff --git a/tests/test_dc.cpp b/tests/test_dc.cpp index 87c23347..551027b3 100644 --- a/tests/test_dc.cpp +++ b/tests/test_dc.cpp @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) { subops.push_back(op-2); }, [](vector& res, Result &ret){ ret=res[0]+res[1]; }, - [](const Problem &op, Result &res) { res=1; }, + [](const Problem &, Result &res) { res=1; }, [](const Problem &op){ return (op<=2); }, Problem(start), res, nwork ); diff --git a/tests/test_farm+A2A.cpp b/tests/test_farm+A2A.cpp index dc8d9391..7a5a2575 100644 --- a/tests/test_farm+A2A.cpp +++ b/tests/test_farm+A2A.cpp @@ -90,7 +90,7 @@ struct Second: ff_monode_t { long* svc(long* in) { return in;} }; struct Collector: ff_minode_t { - long* svc(long* in) { + long* svc(long*) { printf("Collector, received from pipe%ld\n", get_channel_id()); return GO_ON; } diff --git a/tests/test_farm+A2A2.cpp b/tests/test_farm+A2A2.cpp index f46c8cc3..9a92baab 100644 --- a/tests/test_farm+A2A2.cpp +++ b/tests/test_farm+A2A2.cpp @@ -104,7 +104,7 @@ struct miHelper: ff_minode_t { } }; struct Collector: ff_minode_t { - long* svc(long* in) { + long* svc(long*) { printf("Collector, received from %ld\n", get_channel_id()); return GO_ON; } diff --git a/tests/test_farm2.cpp b/tests/test_farm2.cpp index 3ce72910..d9b5eda6 100644 --- a/tests/test_farm2.cpp +++ b/tests/test_farm2.cpp @@ -69,7 +69,7 @@ struct Worker: ff_node_t { }; struct Collector: ff_minode_t { Collector(long ntasks):ntasks(ntasks) {} - long *svc(long *in) { + long *svc(long *) { --ntasks; return GO_ON; } diff --git a/tests/test_ffthread.cpp b/tests/test_ffthread.cpp index e8feae05..c80a4b84 100644 --- a/tests/test_ffthread.cpp +++ b/tests/test_ffthread.cpp @@ -36,7 +36,7 @@ class manager:public ff::ff_thread { freeze(); } - void* svc(void* task) + void* svc(void*) { if (!running) { diff --git a/tests/test_map.cpp b/tests/test_map.cpp index d32249a2..8636b057 100644 --- a/tests/test_map.cpp +++ b/tests/test_map.cpp @@ -56,7 +56,7 @@ typedef std::vector ff_task_t; #endif struct mapWorker : ff_Map { - ff_task_t *svc(ff_task_t *in) { + ff_task_t *svc(ff_task_t *) { ff_task_t *A = new ff_task_t(MYSIZE); // this is the parallel_for provided by the ff_Map class diff --git a/tests/test_multi_input10.cpp b/tests/test_multi_input10.cpp index 02dfd8d9..d3a37cc8 100644 --- a/tests/test_multi_input10.cpp +++ b/tests/test_multi_input10.cpp @@ -53,7 +53,7 @@ using namespace ff; struct Generator: ff_node_t { - long* svc(long* in) { + long* svc(long*) { for(long i=1;i<=10;++i) { ff_send_out((long*)i); } diff --git a/tests/test_multi_input11.cpp b/tests/test_multi_input11.cpp index 3af65216..0ec277bc 100644 --- a/tests/test_multi_input11.cpp +++ b/tests/test_multi_input11.cpp @@ -53,7 +53,7 @@ using namespace ff; struct Generator: ff_node_t { - long* svc(long* in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { ff_send_out((long*)i); } diff --git a/tests/test_multi_input9.cpp b/tests/test_multi_input9.cpp index 8d7ec536..b9089f26 100644 --- a/tests/test_multi_input9.cpp +++ b/tests/test_multi_input9.cpp @@ -53,7 +53,7 @@ using namespace ff; struct Generator: ff_node_t { const long ntasks=10; - long* svc(long* in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { ff_send_out((long*)i); } diff --git a/tests/test_multi_output3.cpp b/tests/test_multi_output3.cpp index b1ccd37c..081b826e 100644 --- a/tests/test_multi_output3.cpp +++ b/tests/test_multi_output3.cpp @@ -94,7 +94,7 @@ struct multiInputHelper: ff_minode_t { p->second = (fromInput()?-1:0); return p; } - void eosnotify(ssize_t id) { + void eosnotify(ssize_t) { // NOTE: we have to send EOS explicitly to the next stage // because for multi-input node the EOS is propagated only // it has been received from all input channels @@ -124,7 +124,7 @@ struct Worker: ff_monode_t { } return GO_ON; } - void eosnotify(ssize_t id) { + void eosnotify(ssize_t) { eosarrived=true; if (eosarrived && ntasks==0) ff_send_out(EOS); diff --git a/tests/test_multi_output5.cpp b/tests/test_multi_output5.cpp index 61c587fe..722dd48f 100644 --- a/tests/test_multi_output5.cpp +++ b/tests/test_multi_output5.cpp @@ -332,8 +332,8 @@ struct Manager: ff_node_t { int wait() { return ff_node_t::wait(); } - ff_buffernode * const getChannel1() { return &channel1;} - ff_buffernode * const getChannel2() { return &channel2;} + ff_buffernode * getChannel1() { return &channel1;} + ff_buffernode * getChannel2() { return &channel2;} ff_buffernode channel1; diff --git a/tests/test_multi_output6.cpp b/tests/test_multi_output6.cpp index cf054837..8cbf1a5b 100644 --- a/tests/test_multi_output6.cpp +++ b/tests/test_multi_output6.cpp @@ -287,7 +287,7 @@ struct Manager: ff_node_t { int wait() { return ff_node_t::wait(); } - ff_buffernode * const getChannel() { return &channel;} + ff_buffernode * getChannel() { return &channel;} ff_buffernode channel; }; diff --git a/tests/test_noinput_pipe.cpp b/tests/test_noinput_pipe.cpp index 33f2e40a..86e6422b 100644 --- a/tests/test_noinput_pipe.cpp +++ b/tests/test_noinput_pipe.cpp @@ -77,7 +77,7 @@ class Stage2: public ff_node { class Stage3: public ff_node { public: - void * svc(void * task) { + void * svc(void *) { printf("got task\n"); return GO_ON; } diff --git a/tests/test_optimize.cpp b/tests/test_optimize.cpp index 516a2eff..f30ecbce 100644 --- a/tests/test_optimize.cpp +++ b/tests/test_optimize.cpp @@ -61,7 +61,7 @@ using namespace ff; struct First: ff_node_t { First(const int ntasks):ntasks(ntasks) {} - long* svc(long*in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { struct timespec req; req.tv_sec = 0; @@ -141,7 +141,7 @@ struct Stage6: ff_node_t { } }; struct Last: ff_node_t { - long* svc(long*in) { + long* svc(long*) { //printf("received %ld\n", (long)in); ++counter; return GO_ON; diff --git a/tests/test_optimize2.cpp b/tests/test_optimize2.cpp index eb288a64..a732c6a6 100644 --- a/tests/test_optimize2.cpp +++ b/tests/test_optimize2.cpp @@ -54,7 +54,7 @@ using namespace ff; struct First: ff_node_t { - long* svc(long*in) { + long* svc(long*) { for(long i=1;i<=1000;++i) ff_send_out((long*)i); return EOS; diff --git a/tests/test_optimize3.cpp b/tests/test_optimize3.cpp index 45950c6c..d639a030 100644 --- a/tests/test_optimize3.cpp +++ b/tests/test_optimize3.cpp @@ -40,7 +40,7 @@ using namespace ff; struct First: ff_node_t { First(const int ntasks):ntasks(ntasks) {} - long* svc(long*in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { struct timespec req; req.tv_sec = 0; diff --git a/tests/test_optimize4.cpp b/tests/test_optimize4.cpp index 97e2ee3c..a6f955cc 100644 --- a/tests/test_optimize4.cpp +++ b/tests/test_optimize4.cpp @@ -56,7 +56,7 @@ using namespace ff; struct First: ff_node_t { First(const int ntasks):ntasks(ntasks) {} - long* svc(long*in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { struct timespec req; req.tv_sec = 0; diff --git a/tests/test_optimize5.cpp b/tests/test_optimize5.cpp index b3a974bb..2ecba435 100644 --- a/tests/test_optimize5.cpp +++ b/tests/test_optimize5.cpp @@ -41,7 +41,7 @@ using namespace ff; struct Generator: ff_node_t { - long* svc(long* in) { + long* svc(long*) { for(long i=1;i<=10;++i) { ff_send_out((long*)i); } @@ -53,12 +53,12 @@ struct Collector: ff_minode_t { int svc_init() { return 0; } - long* svc(long* in) { + long* svc(long*) { return GO_ON; } }; struct Worker: ff_node_t { - long* svc(long* in) { + long* svc(long*) { return GO_ON; } }; @@ -106,7 +106,7 @@ struct PipeWorker: ff_pipeline { } - long* svc(long* in) { abort();} + long* svc(long*) { abort();} }; diff --git a/tests/test_parforpipereduce.cpp b/tests/test_parforpipereduce.cpp index aef902a4..d8be7c38 100644 --- a/tests/test_parforpipereduce.cpp +++ b/tests/test_parforpipereduce.cpp @@ -69,7 +69,7 @@ int main(int argc, char * argv[]) { ParallelForPipeReduce* > pfr(nworkers,true); // spinwait is set to true pfr.disableScheduler(); - auto Map = [&](const long start, const long stop, const int thid, ff_buffernode &node) { + auto Map = [&](const long start, const long stop, const int /*thid*/, ff_buffernode &node) { if (start == stop) return; std::vector* C = new std::vector; C->reserve(stop-start); diff --git a/tests/test_pipe2.cpp b/tests/test_pipe2.cpp index a1041ca3..fd86548b 100644 --- a/tests/test_pipe2.cpp +++ b/tests/test_pipe2.cpp @@ -45,7 +45,7 @@ using namespace ff; struct First: ff_node_t { First(const int ntasks):ntasks(ntasks) {} - long* svc(long*in) { + long* svc(long*) { for(long i=1;i<=ntasks;++i) { struct timespec req; req.tv_sec = 0; @@ -79,7 +79,7 @@ struct Worker: ff_node_t { } }; struct Last: ff_node_t { - long* svc(long*in) { + long* svc(long*) { ++counter; return GO_ON; } diff --git a/tests/test_pool3.cpp b/tests/test_pool3.cpp index 7c1f4e85..ee0dbe2a 100644 --- a/tests/test_pool3.cpp +++ b/tests/test_pool3.cpp @@ -99,7 +99,7 @@ const long &evolution(long &element, const Env_t&,const int) { return element; } -void filter(ParallelForReduce & pfr, std::vector &P, std::vector &buffer,Env_t &env) { +void filter(ParallelForReduce & pfr, std::vector &, std::vector &buffer,Env_t &env) { env.avg = fitness(pfr, buffer, nwF); env.iter +=1; diff --git a/tests/test_sendq.cpp b/tests/test_sendq.cpp index 981222cf..db3fc830 100644 --- a/tests/test_sendq.cpp +++ b/tests/test_sendq.cpp @@ -81,7 +81,7 @@ class Emitter: public ff_node { void * recv_data= (void*)q; for(unsigned i=0;ipop(&recv_data)); + do {} while(!q->pop(&recv_data)); assert(recv_data == (void*)q); } return NULL; diff --git a/tests/test_stopstartall.cpp b/tests/test_stopstartall.cpp index a18a5880..f02a066a 100644 --- a/tests/test_stopstartall.cpp +++ b/tests/test_stopstartall.cpp @@ -78,7 +78,7 @@ struct Collector: ff_node { ntasks=0; return 0; } - void *svc(void *t) { + void *svc(void *) { ++ntasks; return GO_ON; } diff --git a/tests/test_stopstartthreads3.cpp b/tests/test_stopstartthreads3.cpp index 941ded7a..c1d31ae8 100644 --- a/tests/test_stopstartthreads3.cpp +++ b/tests/test_stopstartthreads3.cpp @@ -94,7 +94,7 @@ class Emitter: public ff_node_t { class Collector: public ff_minode_t { public: - Collector(long streamlen): error(false) {} + Collector(long): error(false) {} long* svc(long *task) { const long &t = *task; diff --git a/tests/test_taskcallbacks.cpp b/tests/test_taskcallbacks.cpp index 8ae4da98..45e30542 100644 --- a/tests/test_taskcallbacks.cpp +++ b/tests/test_taskcallbacks.cpp @@ -46,7 +46,7 @@ struct Worker1: ff_node_t { printf("Worker%ld callbackIn\n", get_my_id()); } - void callbackOut(void *p) { + void callbackOut(void *) { printf("Worker%ld callbackOut\n", get_my_id()); } @@ -59,11 +59,11 @@ struct Worker1: ff_node_t { struct Worker2: ff_node_t { - void callbackIn(void *p) { + void callbackIn(void *) { printf("Worker%ld callbackIn\n", get_my_id()); } - void callbackOut(void *p) { + void callbackOut(void *) { printf("Worker%ld callbackOut\n", get_my_id()); } long *svc(long *in) { @@ -79,11 +79,13 @@ struct Emitter: ff_node_t { void callbackIn(void *p) { assert(reinterpret_cast(p) == lb); + FF_IGNORE_UNUSED(p); printf("Emitter callbackIn\n"); } void callbackOut(void *p) { assert(reinterpret_cast(p) == lb); + FF_IGNORE_UNUSED(p); printf("Emitter callbackOut\n"); } diff --git a/tests/test_uBuffer.cpp b/tests/test_uBuffer.cpp index 24ed9ee9..84f1ec69 100644 --- a/tests/test_uBuffer.cpp +++ b/tests/test_uBuffer.cpp @@ -156,13 +156,13 @@ static inline void PUSH(const int i) { #if !defined(USE_DEQUE) #if defined(TWO_LOCK) - do ; while(!(b->mp_push(p))); + do {} while(!(b->mp_push(p))); #else #if defined(MULTIPUSH) - do ; while(!(b->mpush(p))); + do {} while(!(b->mpush(p))); #else - do ; while(!(b->push(p))); + do {} while(!(b->push(p))); #endif #endif // TWO_LOCK #else // USE_DEQUE From 021f9695cc84f3fe62d1ef709567459ea70a5063 Mon Sep 17 00:00:00 2001 From: lucarin91 Date: Wed, 22 Jul 2020 10:21:57 +0200 Subject: [PATCH 007/202] remove bash 4 features from the mapping script, MacOS still uses bash 3 --- ff/mapping_string.sh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ff/mapping_string.sh b/ff/mapping_string.sh index a5aac0e4..329a2757 100755 --- a/ff/mapping_string.sh +++ b/ff/mapping_string.sh @@ -2,7 +2,7 @@ # # Author: Massimo Torquati # -# Requires: bash >=4, hwloc +# Requires: bash >=3.2, hwloc # # The script builds a string containing the list of logical core of the # machine that are topologically contiguous, i.e the first context of the @@ -42,8 +42,11 @@ nway=$(($logical/$physical)) # The right-hand side script returns the ids of the Processing Unit of # the machine in the linear order. # Considering the example above the topocmd command returns something like: -# 0 8 4 12 2 10 6 14 1 9 5 13 3 11 7 15. -mapfile -t array < <( $topocmd --only pu | awk -F'[#)]' '{print $3}' ) +# 0 8 4 12 2 10 6 14 1 9 5 13 3 11 7 15. +# (We do not use mapfile command for portability on MacOS and bash<4) +while IFS= read -r line; do + array+=("$line") +done < <( $topocmd --only pu | awk -F'[#)]' '{print $3}' ) for((i=0;i<${#array[@]};i+=nway)); do for((j=0;j<$nway;++j)); do @@ -53,7 +56,8 @@ done for((j=0;j<$nway;++j)); do str+=${V[j]} done -string=${str::-1} # remove the last comma +# remove the last comma +string=${str::${#str}-1} # on bash>4 just ${str::-1} echo "FF_MAPPING_STRING=\"$string\"" echo "FF_NUM_CORES=$logical" echo "FF_NUM_REAL_CORES=$physical" @@ -65,7 +69,7 @@ then sed -i -e "s/#define FF_MAPPING_STRING \"\"/#define FF_MAPPING_STRING \"$string\"/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_MAPPING_STRING variable in the ./config.hpp file:" - echo -e "\e[1m $(grep -m1 "^#define FF_MAPPING_STRING \"" config.hpp) \e[0m" + echo -e "\033[1m $(grep -m1 "^#define FF_MAPPING_STRING \"" config.hpp) \033[0m" else echo "something went wrong when replacing the variable FF_MAPPING_STRING...." exit 1 @@ -73,7 +77,7 @@ then sed -i -e "s/#define FF_NUM_CORES -1/#define FF_NUM_CORES $logical/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_NUM_CORES variable in the ./config.hpp file:" - echo -e "\e[1m $(grep -m1 "^#define FF_NUM_CORES " config.hpp) \e[0m" + echo -e "\033[1m $(grep -m1 "^#define FF_NUM_CORES " config.hpp) \033[0m" else echo "something went wrong when replacing the variable FF_NUM_CORES...." exit 1 @@ -81,7 +85,7 @@ then sed -i -e "s/#define FF_NUM_REAL_CORES -1/#define FF_NUM_REAL_CORES $physical/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_NUM_REAL_CORES variable in the ./config.hpp file:" - echo -e "\e[1m $(grep -m1 "^#define FF_NUM_REAL_CORES " config.hpp) \e[0m" + echo -e "\033[1m $(grep -m1 "^#define FF_NUM_REAL_CORES " config.hpp) \033[0m" else echo "something went wrong when replacing the variable FF_NUM_REAL_CORES...." exit 1 From 9ba56e76bd0b3705e314bba9442607df85201a35 Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Wed, 23 Sep 2020 11:11:09 +0200 Subject: [PATCH 008/202] fixed a bug in the all-to-all when "heterogeneous" building blocks are added in the right-hand side vector --- ff/all2all.hpp | 25 +++++++--- ff/multinode.hpp | 3 ++ tests/Makefile | 2 +- tests/test_all-to-all17.cpp | 99 +++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 tests/test_all-to-all17.cpp diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 30904ff3..43e898d4 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -59,7 +59,21 @@ class ff_a2a: public ff_node { error("A2A, nodes of the first set cannot be farm or all-to-all\n"); return -1; } + if (workers2[0]->isFarm() || workers2[0]->isAll2All()) { + error("A2A, nodes of the second set cannot be farm or all-to-all\n"); + return -1; + } + if (workers1[0]->isPipe()) { + + // all other Workers must be pipe + for(size_t i=1;iisPipe()) { + error("A2A, workers of the first set are not homogeneous, all of them must be of the same kind of building-block (e.g., all pipelines)\n"); + return -1; + } + } + if (!workers1[0]->isMultiOutput()) { error("A2A, workers of the first set can be pipelines but only if they are multi-output (automatic transformation not yet supported)\n"); return -1; @@ -85,11 +99,7 @@ class ff_a2a: public ff_node { } } } - if (workers2[0]->isFarm() || workers2[0]->isAll2All()) { - error("A2A, nodes of the second set cannot be farm or all-to-all\n"); - return -1; - } - + // checking L-Workers if (!workers1[0]->isMultiOutput()) { // NOTE: we suppose all others to be the same // the nodes in the first set cannot be multi-input nodes without being @@ -117,7 +127,7 @@ class ff_a2a: public ff_node { workers1[i]->set_id(int(i)); } // checking R-Workers - if (!workers2[0]->isMultiInput()) { // we suppose that all others are the same + if (!workers2[0]->isMultiInput()) { // NOTE: we suppose that all others are the same if (workers2[0]->isMultiOutput()) { error("A2A, the nodes of the second set cannot be multi-output nodes without being also multi-input (i.e., a composition of nodes). The node must be either standard node or multi-input node or compositions where the first stage is a multi-input node\n"); return -1; @@ -446,9 +456,10 @@ class ff_a2a: public ff_node { w += getSecondSet(); } void get_in_nodes(svector&w) { + size_t len=w.size(); for(size_t i=0;iget_in_nodes(w); - if (w.size() == 0) + if (len == w.size()) w += getFirstSet(); } diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 6fdabe82..ff1f1b63 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -187,12 +187,15 @@ class ff_minode: public ff_node { } virtual inline void get_in_nodes(svector&w) { + size_t len=w.size(); // it is possible that the multi-input node is register // as collector of farm if (inputNodes.size() == 0 && gt->getNWorkers()>0) { w += gt->getWorkers(); } w += inputNodes; + + if (len == w.size()) w.push_back(this); } virtual void get_in_nodes_feedback(svector&w) { diff --git a/tests/Makefile b/tests/Makefile index 8401f2cc..3eda8fa1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 #test_taskf2 test_taskf3 diff --git a/tests/test_all-to-all17.cpp b/tests/test_all-to-all17.cpp new file mode 100644 index 00000000..78a3cbb1 --- /dev/null +++ b/tests/test_all-to-all17.cpp @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ +/* Testing: + * + * Left -->| + * | | -> pipe(Right -> Right) + * ... | -->| + * | | -> comb(Right,Right) + * Left -->| + * + * In this simple test, the right-hand side Workers are "heterogeneous", the top one is + * a pipeline (starting with a multi-input node) and the bottom one is a combine node. + */ +/* + * + * Author: Massimo Torquati + */ + +#include +#include +#include +#include + +using namespace ff; + +struct Left: ff_monode_t { + Left(long ntasks): ntasks(ntasks) {} + + long* svc(long*) { + for (long i=1;i<=ntasks;++i) + ff_send_out(&i); + return EOS; + } + long ntasks; +}; +struct Right: ff_minode_t { + Right(const bool propagate=false):propagate(propagate) {} + long* svc(long* in) { + if (propagate) return in; + return GO_ON; + } + const bool propagate; +}; + + +int main(int argc, char* argv[]) { + long ntasks = 1000000; + long nlefts = 4; + + std::vector W1; + for(long i=0;i W2; + ff_pipeline pipe; + pipe.add_stage(new Right(true), true); + pipe.add_stage(new Right(false), true); + W2.push_back(&pipe); + ff_comb comb(new Right(true), new Right(false), true,true); + W2.push_back(&comb); + + ff_a2a a2a; + a2a.add_firstset(W1, 0, true); + a2a.add_secondset(W2); + + if (a2a.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + std::cout << "Done\n"; + return 0; +} + + + From 60521618b6348a96b71392d5a93ce5431841e40b Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Tue, 3 Nov 2020 09:51:28 +0100 Subject: [PATCH 009/202] - Implemented a new mechanism for the ff_send_out in ff_node(s) within a combine building block. - First version and first test of the StaticAllocator (work in progress). - More checks in the all-to-all to discover wrong usage - From now on, if a combine building block is used as an Emitter filter of a farm, it must be multi-output (i.e. it must terminate with a multi-output node). --- ff/all2all.hpp | 35 ++++- ff/combine.hpp | 227 +++++++++++++-------------------- ff/farm.hpp | 37 ++++-- ff/multinode.hpp | 29 ++++- ff/node.hpp | 13 +- ff/optimize.hpp | 17 ++- ff/staticallocator.hpp | 97 ++++++++++++++ tests/Makefile | 2 +- tests/test_all-to-all17.cpp | 8 +- tests/test_combine.cpp | 14 +- tests/test_combine1.cpp | 2 +- tests/test_combine6.cpp | 49 +++++-- tests/test_staticallocator.cpp | 146 +++++++++++++++++++++ 13 files changed, 477 insertions(+), 199 deletions(-) create mode 100644 ff/staticallocator.hpp create mode 100644 tests/test_staticallocator.cpp diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 43e898d4..d750f415 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -65,7 +65,6 @@ class ff_a2a: public ff_node { } if (workers1[0]->isPipe()) { - // all other Workers must be pipe for(size_t i=1;iisPipe()) { @@ -75,9 +74,10 @@ class ff_a2a: public ff_node { } if (!workers1[0]->isMultiOutput()) { - error("A2A, workers of the first set can be pipelines but only if they are multi-output (automatic transformation not yet supported)\n"); + error("A2A, workers of the first set can be pipelines but only if they are multi-output (automatic transformation NOT YET SUPPORTED)\n"); return -1; } + ff_node* last = ispipe_getlast(workers1[0]); // NOTE: we suppose homogeneous first set assert(last); if (last->isFarm() && !last->isOFarm()) { // standard farm ... @@ -85,21 +85,45 @@ class ff_a2a: public ff_node { svector w1; last->get_out_nodes(w1); if (!w1[0]->isMultiOutput()) { // NOTE: we suppose homogeneous workers - error("A2A, workers of the first set are pipelines but their last stage are not multi-output (automatic transformation not yet supported)\n"); + error("A2A, workers (farm/ofarm) of the first set are pipelines but their last stage is not multi-output (automatic transformation NOT YET SUPPORTED)\n"); return -1; } } } + // since by default the a2a is multi-output, we have to check if its workers + // are multi-output if (last->isAll2All()) { svector w1; last->get_out_nodes(w1); if (!w1[0]->isMultiOutput()) { // NOTE: we suppose homogeneous second set - error("A2A, workers of the first set are pipelines but their last stage are not multi-output (automatic transformation not yet supported)\n"); + error("A2A, workers (a2a) of the first set are pipelines but their last stage is not multi-output (automatic transformation NOT YET SUPPORTED)\n"); + return -1; + } + } + } + if (workers2[0]->isPipe()) { + // all other Workers must be pipe + for(size_t i=1;iisPipe()) { + error("A2A, workers of the second set are not homogeneous, all of them must be of the same kind (e.g., all pipelines)\n"); return -1; } } + + if (!workers2[0]->isMultiInput()) { + error("A2A, workers of the second set can be pipelines but only if they are multi-input (automatic transformation NOT YET SUPPORTED)\n"); + return -1; + } + // since by default the a2a is multi-input, we have to check if its workers + // are multi-input + svector w1; + workers2[0]->get_in_nodes(w1); + if (!w1[0]->isMultiInput()) { // NOTE: we suppose homogeneous second set + error("A2A, workers of the second set are pipelines but their first stage is not multi-input (automatic transformation NOT YET SUPPORTED)\n"); + return -1; + } } - + // checking L-Workers if (!workers1[0]->isMultiOutput()) { // NOTE: we suppose all others to be the same // the nodes in the first set cannot be multi-input nodes without being @@ -175,7 +199,6 @@ class ff_a2a: public ff_node { for(size_t i=0;iget_out_nodes(L); if (L.size()==0) L=workers1; - svector R; for(size_t i=0;iget_in_nodes(R); diff --git a/ff/combine.hpp b/ff/combine.hpp index b29ee9e2..7d1119aa 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -177,12 +177,12 @@ class ff_comb: public ff_minode { // NOTE: it is multi-input only if the first node is multi-input bool isMultiInput() const { if (getFirst()->isMultiInput()) return true; - return false; //comp_multi_input; + return false; } // NOTE: it is multi-output only if the last node is multi-output bool isMultiOutput() const { if (getLast()->isMultiOutput()) return true; - return false; //comp_multi_output; + return false; } inline bool isComp() const { return true; } @@ -310,9 +310,9 @@ class ff_comb: public ff_minode { error("COMP, connecting callbacks\n"); return; } + ff_node *n1 = (w1.size() == 0)? comp_nodes[0]:w1[0]; - ff_node *n2 = (w2.size() == 0)? comp_nodes[1]:w2[0]; - n1->registerCallback(n2->ff_send_out_comp, n2); + n1->registerCallback(this->ff_send_out_comp, this); } int dryrun() { @@ -357,20 +357,17 @@ class ff_comb: public ff_minode { prepared = true; return 0; } + void set_multiinput() { // see farm.hpp - // when the composition is passed as filter of a farm collector (which is a multi-input) - // the filter is seen as multi-input because we want to avoid to call eosnotify - // too many times (see ff_comb::eosnotify) + // when the composition is passed as filter of a farm collector (which is by + // default a multi-input node) the filter is seen as multi-input because we want + // to avoid calling eosnotify multiple times (see ff_comb::eosnotify) + // The same applies for the farm emitter. if (comp_nodes[0]->isComp()) return comp_nodes[0]->set_multiinput(); comp_multi_input=true; } - void set_multioutput() { - if (comp_nodes[1]->isComp()) - return comp_nodes[1]->set_multioutput(); - comp_multi_output=true; - } void set_neos(ssize_t n) { getFirst()->set_neos(n); @@ -399,45 +396,6 @@ class ff_comb: public ff_minode { return 0; } - inline void* svc_comp_node1(void* task, void*r) { - void* ret = r, *r2; - if (comp_nodes[1]->isComp()) - ret = comp_nodes[1]->svc(task); - else { - svector &localdata1 = comp_nodes[1]->get_local_data(); - size_t n = localdata1.size(); - for(size_t k=0;keosnotify(); - /*ret = FF_GO_OUT;*/ // NOTE: we cannot exit if the previous comp node does not exit - if (comp_nodes[1]->isMultiOutput()) - comp_nodes[1]->propagateEOS(FF_EOS); - else { - if (comp_multi_output) ret = FF_EOS; - else comp_nodes[1]->ff_send_out(FF_EOS); // if no output is present it calls devnull - } - break; - } - r2= comp_nodes[1]->svc(t); - if (r2 == FF_GO_ON || r2 == FF_GO_OUT || r2 == FF_EOS_NOFREEZE) continue; - if (r2 == FF_EOS) { - ret = FF_GO_OUT; - if (comp_nodes[1]->isMultiOutput()) - comp_nodes[1]->propagateEOS(FF_EOS); - else { - if (comp_multi_output) ret = FF_EOS; - comp_nodes[1]->ff_send_out(FF_EOS); // if no output is present it calls devnull - } - break; - } - comp_nodes[1]->ff_send_out(r2); // if no output is present it calls devnull - } - localdata1.clear(); - } - return ret; - } - // main service function void *svc(void *task) { void *ret = FF_GO_ON; @@ -446,36 +404,15 @@ class ff_comb: public ff_minode { if (comp_nodes[0]->isComp()) ret = comp_nodes[0]->svc(task); else { - svector &localdata1 = comp_nodes[0]->get_local_data(); - size_t n = localdata1.size(); - if (n>0) { - for(size_t k=0;keosnotify(); - ret = FF_GO_OUT; - comp_nodes[1]->push_comp_local(t); - break; - } - r1= comp_nodes[0]->svc(t); - if (r1 == FF_GO_ON || r1 == FF_GO_OUT || r1 == FF_EOS_NOFREEZE) continue; - comp_nodes[1]->push_comp_local(r1); - if (r1 == FF_EOS) { - ret = FF_GO_OUT; - break; - } - } - localdata1.clear(); - } else { - if (task || comp_nodes[0]->skipfirstpop()) { - r1= comp_nodes[0]->svc(task); - if (!(r1 == FF_GO_ON || r1 == FF_GO_OUT || r1 == FF_EOS_NOFREEZE)) - comp_nodes[1]->push_comp_local(r1); - if (r1 == FF_EOS) ret=FF_GO_OUT; + if (task || comp_nodes[0]->skipfirstpop()) { + r1= comp_nodes[0]->svc(task); + if (!(r1 == FF_GO_ON || r1 == FF_GO_OUT || r1 == FF_EOS_NOFREEZE)) { + comp_nodes[0]->ff_send_out(r1); } + if (r1 == FF_EOS) + ret=FF_GO_OUT; } } - ret = svc_comp_node1(nullptr, ret); return ret; } @@ -485,8 +422,20 @@ class ff_comb: public ff_minode { } } - bool push_comp_local(void *task) { - return comp_nodes[0]->push_comp_local(task); + // this is called by the ff_send_out for those nodes that are inside a combine + bool push_comp_local(void *task) { + if (task == FF_EOS) { + comp_nodes[1]->eosnotify(); + propagateEOS(); + return true; + } + void *r = comp_nodes[1]->svc(task); + if (r == FF_GO_ON || r== FF_GO_OUT || r == FF_EOS_NOFREEZE) return true; + if (r == FF_EOS) { + propagateEOS(); + return true; + } + return comp_nodes[1]->ff_send_out(r); } int set_output(const svector & w) { @@ -499,9 +448,6 @@ class ff_comb: public ff_minode { return comp_nodes[1]->set_output_feedback(n); } int set_input(const svector & w) { - - //if (comp_nodes[0]->isComp()) return comp_nodes[0]->set_input(w); // WARNING: <-------- - //assert(comp_nodes[0]->isMultiInput()); if (comp_nodes[0]->set_input(w)<0) return -1; // if the first node of the comp is a multi-input node @@ -510,9 +456,6 @@ class ff_comb: public ff_minode { return ff_minode::set_input(w); } int set_input(ff_node *n) { - //if (comp_nodes[0]->isComp()) return comp_nodes[0]->set_input(n); // WARNING: <------- - - //assert(comp_nodes[0]->isMultiInput()); if (comp_nodes[0]->set_input(n)<0) return -1; // if the first node of the comp is a multi-input node @@ -521,8 +464,6 @@ class ff_comb: public ff_minode { return ff_minode::set_input(n); } int set_input_feedback(ff_node *n) { - //if (comp_nodes[0]->isComp()) return comp_nodes[0]->set_input_feedback(n); // WARNING: <----- - //assert(comp_nodes[0]->isMultiInput()); if (comp_nodes[0]->set_input_feedback(n)<0) return -1; // if the first node of the comp is a multi-input node @@ -553,22 +494,16 @@ class ff_comb: public ff_minode { void eosnotify(ssize_t id=-1) { comp_nodes[0]->eosnotify(); - // the eosnotify might produce some data in output - void *ret = svc_comp_node1(nullptr, GO_ON); - ++neos; // if the first node is multi-input or is a comp passed as filter to a farm collector, // then we have to call eosnotify only if we have received all EOSs if (comp_nodes[0]->isMultiInput() || comp_multi_input) { const ssize_t n=getFirst()->get_neos(); - if ((neos >= n) && - (ret != FF_GO_OUT) /*this means that svc_comp_node1 has already called eosnotify */ - ) + if (neos >= n) comp_nodes[1]->eosnotify(id); return; } - if (ret != FF_GO_OUT) - comp_nodes[1]->eosnotify(id); + comp_nodes[1]->eosnotify(id); } void propagateEOS(void *task=FF_EOS) { @@ -577,8 +512,7 @@ class ff_comb: public ff_minode { return; } - if (comp_nodes[1]->isMultiOutput() || - comp_multi_output) + if (comp_nodes[1]->isMultiOutput()) comp_nodes[1]->propagateEOS(task); else comp_nodes[1]->ff_send_out(task); @@ -721,7 +655,6 @@ class ff_comb: public ff_minode { svector comp_nodes; svector cleanup_stages; bool comp_multi_input = false; - bool comp_multi_output= false; ssize_t neos=0; }; @@ -778,8 +711,8 @@ static inline const ff_pipeline combine_nodes_in_pipeline(ff_node& node1, ff_nod if (cleanup2) farm2->cleanup_all(); return combine_ofarm_farm(*farm1, *farm2); } - error("combine_nodes_in_pipeline, FEATURE NOT YET SUPPORTED (node1 ordered farm and node1 standard or combine node\n"); - abort(); // TODO + error("combine_nodes_in_pipeline, FEATURE NOT YET SUPPORTED (node1 ordered farm and node2 standard or combine node\n"); + abort(); // FIX: TODO <--------- return ff_pipeline(); } if (!node1.isFarm() && !node2.isFarm()) { // two sequential nodes @@ -793,7 +726,27 @@ static inline const ff_pipeline combine_nodes_in_pipeline(ff_node& node1, ff_nod ff_node *e = farm->getEmitter(); if (!e) farm->add_emitter(&node1); else { - ff_comb *p = new ff_comb(&node1, e, cleanup1, farm->isset_cleanup_emitter()); + ff_comb *p; + if (!e->isMultiOutput()) { // we have to transform the emitter node into a multi-output + if (e->isMultiInput()) { // this is a "strange" case: the emitter is multi-input without being also multi-output (through a combine node) + struct hnode:ff_monode { + void* svc(void*in) {return in;} + }; + + // c is a multi-input AND multi-output node + ff_comb *c = new ff_comb(e, new hnode, + farm->isset_cleanup_emitter(), true); + assert(c); + p = new ff_comb(&node1, c, cleanup1, true); + } else { + auto mo = new internal_mo_transformer(e, farm->isset_cleanup_emitter()); + assert(mo); + p = new ff_comb(&node1, mo, cleanup1, true); + } + } else { + p = new ff_comb(&node1, e, cleanup1, farm->isset_cleanup_emitter()); + } + assert(p); if (farm->isset_cleanup_emitter()) farm->cleanup_emitter(false); farm->change_emitter(p, true); } @@ -928,9 +881,6 @@ static inline const ff_farm combine_farms_a2a(ff_farm &farm1, const E_t& node, f std::vector Wtmp(W1.size()); for(size_t i=0;i Wtmp1(W1.size()); for(size_t i=0;iisMultiOutput()) { - error("combine_farms, node1 cannot be a multi-output node\n"); + if (node2->isComp() && !node2->isMultiOutput()) { + error("combine_farms, if node2 is a combine node, then it must be multi-output\n"); return newpipe; } // here we compose node1 and node2 and we set this new // node as emitter of the second farm + // we require that the last stage of the combine is a multi-output node + if (!node2->isMultiOutput()) { + if (node2->isMultiInput()) { // this is a multi-input node + error("combine_farms, node2 is multi-input without being a combine, this is currently needed to apply the transformation (FEATURE NONT YET SUPPORTED)\n"); + return newpipe; + } + auto second = new internal_mo_transformer(*node2); + assert(second); + auto first = new C_t(*node1); + assert(first); + auto p = new ff_comb(first, second, true, true); + assert(p); + farm2.change_emitter(p,true); // cleanup set + } else { + auto ec= combine_nodes(*node1, *node2); + auto pec = new decltype(ec)(ec); + farm2.change_emitter(pec,true); // cleanup set + } farm1.remove_collector(); - auto ec= combine_nodes(*node1, *node2); - auto pec = new decltype(ec)(ec); - farm2.change_emitter(pec,true); // cleanup set - newpipe.add_stage(&farm1); newpipe.add_stage(&farm2); return newpipe; @@ -1260,7 +1220,7 @@ static inline const ff_pipeline combine_farms(ff_farm& farm1, const C_t *node1, } assert(node1!=nullptr); // case3.2 if (node1->isMultiInput()) { - const struct hnode:ff_node { + const struct hnode:ff_monode { void* svc(void*in) {return in;} } helper_node; farm1.remove_collector(); @@ -1327,9 +1287,6 @@ static inline const ff_pipeline combine_farms(ff_farm& farm1, const C_t *node1, std::vector Wtmp1(W1.size()); for(size_t i=0;iisMultiOutput()) { - //const auto collector_mo = mo_transformer(*node1); - //auto c = combine_nodes(*W1[i], collector_mo); - //auto pc = new decltype(c)(c); auto mo = new internal_mo_transformer(*node1); assert(mo); auto pc = new ff_comb(W1[i], mo, @@ -1337,8 +1294,6 @@ static inline const ff_pipeline combine_farms(ff_farm& farm1, const C_t *node1, assert(pc); Wtmp1[i]=pc; } else { - //auto c = combine_nodes(*W1[i], *node1); - //auto pc = new decltype(c)(c); auto c = new C_t(*node1); assert(c); auto pc = new ff_comb(W1[i], c, @@ -1364,14 +1319,16 @@ static inline const ff_pipeline combine_farms(ff_farm& farm1, const C_t *node1, } assert(node2!=nullptr && node1!=nullptr); // case4.1 - // TODO: we can relax the following two constraints. - if (node1->isMultiInput()) { - error("combine_farms, node1 cannot be a multi-input node\n"); - return newpipe; - } - if (node2->isMultiOutput()) { - error("combine_farms, node2 cannot be a multi-output node\n"); - return newpipe; + if (!mergeCE) { + // TODO: we can relax the following two constraints. + if (node1->isMultiInput()) { + error("combine_farms, node1 cannot be a multi-input node\n"); + return newpipe; + } + if (node2->isMultiOutput()) { + error("combine_farms, node2 cannot be a multi-output node\n"); + return newpipe; + } } ff_a2a *a2a = new ff_a2a; @@ -1409,17 +1366,12 @@ static inline const ff_pipeline combine_farms(ff_farm& farm1, const C_t *node1, std::vector Wtmp1(W1.size()); for(size_t i=0;iisMultiOutput()) { - //const auto collector_mo = mo_transformer(*node1); - //auto c = combine_nodes(*W1[i], collector_mo); - //auto pc = new decltype(c)(c); auto mo = new internal_mo_transformer(*node1); assert(mo); auto pc = new ff_comb(W1[i], mo, farm1.isset_cleanup_workers() , true); Wtmp1[i]=pc; } else { - //auto c = combine_nodes(*W1[i], *node1); - //auto pc = new decltype(c)(c); auto c = new C_t(*node1); assert(c); auto pc = new ff_comb(W1[i], c, @@ -1433,9 +1385,6 @@ static inline const ff_pipeline combine_farms(ff_farm& farm1, const C_t *node1, std::vector Wtmp2(W2.size()); for(size_t i=0;iisMultiInput()) { - //const auto emitter_mi = mi_transformer(*node2); - //auto c = combine_nodes(emitter_mi, *W2[i]); - //auto pc = new decltype(c)(c); auto mi = new internal_mi_transformer(*node2); assert(mi); auto pc = new ff_comb(mi,W2[i], @@ -1443,8 +1392,6 @@ static inline const ff_pipeline combine_farms(ff_farm& farm1, const C_t *node1, assert(pc); Wtmp2[i]=pc; } else { - //auto c = combine_nodes(*node2, *W2[i]); - //auto pc = new decltype(c)(c); auto e = new E_t(*node2); assert(e); auto pc = new ff_comb(e, W2[i], diff --git a/ff/farm.hpp b/ff/farm.hpp index 440789bf..c815c3a5 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -949,9 +949,25 @@ class ff_farm: public ff_node { int add_emitter(T * e) { if (e==nullptr) return 0; if (e->isFarm() || e->isAll2All() || e->isPipe()) { - error("FARM, add_emitter: wrong king of node, the Emitter filter can be either a standard node or a multi-output node\n"); + error("FARM, add_emitter: wrong kind of node, the Emitter filter cannot be a parallel building block (i.e. farm, all2all, pipeline)\n"); return -1; } + if (e->isComp()) { + // NOTE: if a comp is set as a filter in the emitter of a farm, + // it must terminate with a multi-output node. + // In the previous version, it was allowed to add whatever kind of sequantial BB + // as filter, but in some cases we experienced problems with EOS propagation + if (!e->isMultiOutput()) { + error("FARM, add_emitter: wrong kind of node, if the filter is a combine building block, it must terminate with a multi-output node\n"); + + + abort(); // WARNING: here for debugging purposes, it must be removed! + return -1; + } + + // the combine is forced to appear as multi-input even if it is not + e->set_multiinput(); + } if (emitter) { error("FARM, add_emitter: emitter already present\n"); return -1; @@ -962,15 +978,7 @@ class ff_farm: public ff_node { // the all_gather call if (e->isMultiInput()) { e->registerAllGatherCallback(lb->ff_all_gather_emitter, lb); - } - - if (e->isComp()) { - // NOTE: if a comp is set as a filter in the emitter of a farm, - // it is a multiinput and multioutput node (like an emitter) even if - // the first/last stage of the composition are not a multi-input/output. - e->set_multioutput(); - e->set_multiinput(); - } + } return 0; } template @@ -1124,7 +1132,14 @@ class ff_farm: public ff_node { // NOTE: if a comp is set as a filter in the collector of a farm, // it is a multiinput node even if the first stage of the composition // is not a multi-input. - if (c && c->isComp()) c->set_multiinput(); + if (c && c->isComp()) { + // NOTE: if a comp is set as a filter in the collector of a farm, + // it should start with a multi-input node. + // However, if it is not the case, the combine is forced to appear as multi-input + // even if its first stage is not. This is because EOS should be propagated + // only if all EOSs from previous stages were received (see eosnotify in the combine) + c->set_multiinput(); + } if (c) { collector = c; if (cleanup) collector_cleanup=true; diff --git a/ff/multinode.hpp b/ff/multinode.hpp index ff1f1b63..4e48fdb3 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -667,7 +667,7 @@ class ff_monode: public ff_node { unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { // NOTE: this callback should be set only if the multi-output node is part of - // a composition the it is not the last stage + // a composition and it is not the last stage if (callback) return callback(task,retry,ticks,callback_arg); return lb->schedule_task(task,retry,ticks); } @@ -728,8 +728,8 @@ class ff_monode: public ff_node { */ inline ff_loadbalancer * getlb() const { return lb;} - // used when a multi-input node is a filter of an emitter (i.e. a comp) - // it can also be used to set a particular gathering policy like for + // used when a multi-output node is used as a filter in the emitter of a farm + // it can also be used to set a particular scheduling policy like for // example the ones pre-defined in the file ordering_policies.hpp void setlb(ff_loadbalancer *elb, bool cleanup=false) { if (lb && myownlb) { @@ -857,10 +857,12 @@ struct ff_monode_t: ff_monode { struct internal_mo_transformer: ff_monode { internal_mo_transformer(ff_node* n, bool cleanup=false): ff_monode(n),cleanup(cleanup),n(n) { + assert(!n->isMultiOutput()); } template internal_mo_transformer(const T& _n) { + assert(!_n.isMultiOutput()); T *t = new T(_n); assert(t); n = t; @@ -893,11 +895,20 @@ struct internal_mo_transformer: ff_monode { return 0; } + void set_id(ssize_t id) { if (n) n->set_id(id); ff_monode::set_id(id); } - + + int dryrun() { + if (prepared) return 0; + if (n) { + n->registerCallback(ff_send_out_motransformer, this); + } + return ff_monode::dryrun(); + } + int run(bool skip_init=false) { assert(n); if (!prepared) { @@ -911,7 +922,15 @@ struct internal_mo_transformer: ff_monode { } ff_monode::getlb()->get_filter()->set_id(get_my_id()); return ff_monode::run(skip_init); - } + } + + static inline bool ff_send_out_motransformer(void * task, + unsigned long retry, + unsigned long ticks, void *obj) { + bool r= ((internal_mo_transformer *)obj)->ff_send_out(task, retry, ticks); + return r; + } + bool cleanup; ff_node *n=nullptr; }; diff --git a/ff/node.hpp b/ff/node.hpp index 3d5ccc26..6ddeaee1 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1155,8 +1155,7 @@ class ff_node { virtual inline bool isPipe() const { return false; } virtual inline void set_multiinput() {} - virtual inline void set_multioutput() {} - + #if defined(FF_REPARA) struct rpr_measure_t { size_t schedule_id; @@ -1213,9 +1212,9 @@ class ff_node { } - virtual bool push_comp_local(void *task) { - comp_localdata.push_back(task); - return true; + virtual bool push_comp_local(void *task) { + (void)task; + abort(); // to be removed, just for debugging purposes } @@ -1449,9 +1448,6 @@ class ff_node { return thread->getTid(); } - inline svector& get_local_data() { return comp_localdata;} - - protected: #if defined(TRACE_FASTFLOW) @@ -1484,7 +1480,6 @@ class ff_node { bool prepared = false; bool initial_barrier = true; bool default_mapping = true; - svector comp_localdata; }; // ff_node diff --git a/ff/optimize.hpp b/ff/optimize.hpp index fc378723..519771a5 100644 --- a/ff/optimize.hpp +++ b/ff/optimize.hpp @@ -7,7 +7,7 @@ * * \brief FastFlow optimization heuristics * - * @detail FastFlow basic contanier for a shared-memory parallel activity + * @detail FastFlow basic container for a shared-memory parallel activity * */ @@ -121,11 +121,20 @@ static inline int combine_with_emitter(ff_farm& farm, ff_node*node, bool cleanup farm.cleanup_emitter(cleanup_node); return 0; } - ff_comb* comb = new ff_comb(node,emitter, - cleanup_node, farm.isset_cleanup_emitter()); + ff_comb* comb; + + if (!emitter->isMultiOutput()) { + internal_mo_transformer *mo_emitter = new internal_mo_transformer(emitter, + farm.isset_cleanup_emitter()); + comb = new ff_comb(node, mo_emitter, cleanup_node, true); + } else { + comb = new ff_comb(node,emitter, + cleanup_node, farm.isset_cleanup_emitter()); + + } if (farm.isset_cleanup_emitter()) farm.cleanup_emitter(false); - + farm.change_emitter(comb, true); return 0; } diff --git a/ff/staticallocator.hpp b/ff/staticallocator.hpp new file mode 100644 index 00000000..a86a2caa --- /dev/null +++ b/ff/staticallocator.hpp @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ + +/* *************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this file + * does not by itself cause the resulting executable to be covered by the GNU + * General Public License. This exception does not however invalidate any + * other reasons why the executable file might be covered by the GNU General + * Public License. + * + * **************************************************************************/ + +#ifndef FF_STATIC_ALLOCATOR_HPP +#define FF_STATIC_ALLOCATOR_HPP + +#include +#include +#include +#include +#include +#include + +/* Author: + * Massimo Torquati + * + */ + + +namespace ff { + +class StaticAllocator { +public: + StaticAllocator(const size_t nslot, const size_t slotsize): + nslot(nslot),slotsize(slotsize), cnt(0), segment(nullptr) { + + void* result = 0; + result = mmap(NULL, nslot*slotsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (result == MAP_FAILED) abort(); + segment = (char*)result; + printf("allocated %ld bytes\n", nslot*slotsize); + } + + ~StaticAllocator() { + if (segment) munmap(segment, nslot*slotsize); + segment=nullptr; + } + + template + void alloc(T*& p) { + size_t m = cnt.fetch_add(1,std::memory_order_relaxed) % nslot; + char *mp = segment + m*slotsize; + new (mp) T(); + p = reinterpret_cast(mp); + } + + template + void realloc(T* in, S*& out) { + dealloc(in); + char *mp = reinterpret_cast(in); + new (mp) S(); + out = reinterpret_cast(mp); + } + +protected: + + template + void dealloc(T* p) { + p->~T(); + } + +private: + const size_t nslot; + const size_t slotsize; + std::atomic_ullong cnt; + char *segment; +}; + +}; + + +#endif /* FF_STATIC_ALLOCATOR_HPP */ diff --git a/tests/Makefile b/tests/Makefile index 3eda8fa1..a1cfccd3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator #test_taskf2 test_taskf3 diff --git a/tests/test_all-to-all17.cpp b/tests/test_all-to-all17.cpp index 78a3cbb1..5b4e48fc 100644 --- a/tests/test_all-to-all17.cpp +++ b/tests/test_all-to-all17.cpp @@ -29,11 +29,11 @@ * Left -->| * | | -> pipe(Right -> Right) * ... | -->| - * | | -> comb(Right,Right) + * | | -> pipe(comb(Right,Right)) * Left -->| * * In this simple test, the right-hand side Workers are "heterogeneous", the top one is - * a pipeline (starting with a multi-input node) and the bottom one is a combine node. + * a pipeline (starting with a multi-input node), whereas the bottom one is a pipeline of a single combine node. */ /* * @@ -81,7 +81,9 @@ int main(int argc, char* argv[]) { pipe.add_stage(new Right(false), true); W2.push_back(&pipe); ff_comb comb(new Right(true), new Right(false), true,true); - W2.push_back(&comb); + ff_pipeline pipe1; + pipe1.add_stage(&comb); + W2.push_back(&pipe1); ff_a2a a2a; a2a.add_firstset(W1, 0, true); diff --git a/tests/test_combine.cpp b/tests/test_combine.cpp index f3c9de8c..80cedcb6 100644 --- a/tests/test_combine.cpp +++ b/tests/test_combine.cpp @@ -38,7 +38,7 @@ using namespace ff; long ntasks = 100000; long worktime = 1*2400; // usecs -#if 0 // two different ways to implement the first stage +#if 1 // two different ways to implement the first stage struct firstStage: ff_node_t { long *svc(long*) { for(long i=1;i<=ntasks;++i) { @@ -87,7 +87,7 @@ int main() { secondStage2 _3; secondStage3 _4; thirdStage _5; - + unsigned long inizio=getusec(); long *r; for(long i=1;i<=ntasks;++i) { @@ -109,7 +109,6 @@ int main() { const secondStage2 _3; const secondStage3 _4; const thirdStage _5; -#if 0 { auto comb = combine_nodes(_1, combine_nodes(_2, combine_nodes(_3, combine_nodes(_4, _5)))); if (comb.run_and_wait_end()<0) @@ -204,17 +203,16 @@ int main() { std::cout << "TEST10 DONE Time = " << pipe.ffwTime() << " ms\n"; } usleep(500000); -#endif { // testing multi-input + multi-output combined struct miStage:ff_minode_t { long* svc(long* in) { - printf("MULTI INPUT input channels %ld\n", get_num_inchannels()); + //printf("MULTI INPUT input channels %ld\n", get_num_inchannels()); return in; } void eosnotify(ssize_t) { - printf("MULTI INPUT %ld, eosnotify\n", get_my_id()); + //printf("MULTI INPUT %ld, eosnotify\n", get_my_id()); } }; struct moStage:ff_monode_t { @@ -222,7 +220,7 @@ int main() { return in; } void eosnotify(ssize_t) { - printf("MULTI OUTPUT %ld, eosnotify\n", get_my_id()); + //printf("MULTI OUTPUT %ld, eosnotify\n", get_my_id()); } }; @@ -237,7 +235,7 @@ int main() { // starts the pipeline if (pipe.run_and_wait_end()<0) error("running pipe\n"); - std::cout << "TEST11 DONE\n"; + std::cout << "TEST11 DONE Time = " << pipe.ffwTime() << " ms\n"; } } diff --git a/tests/test_combine1.cpp b/tests/test_combine1.cpp index b683b65b..24167a3c 100644 --- a/tests/test_combine1.cpp +++ b/tests/test_combine1.cpp @@ -25,7 +25,7 @@ **************************************************************************** */ /* - * Basic tests for combine_with_firststage/laststage. + * Basic tests for the combine_with_firststage/laststage helping functions. */ /* Author: Massimo Torquati diff --git a/tests/test_combine6.cpp b/tests/test_combine6.cpp index 9c080234..7da6dcb4 100644 --- a/tests/test_combine6.cpp +++ b/tests/test_combine6.cpp @@ -27,13 +27,15 @@ /* * Single farm topology where the emitter and the collector are a composition of nodes * - * |<------------ comp ----------->| |--> Worker -->| |<------------- comp ------------>| - * | | - * Generator -->Filter1 -->Emitter --> |--> Worker -->|--> Collector --> Filter2 --> Gatherer - * | | - * |--> Worker -->| - * + * |<------------ comp ----------->| |--> Worker -->| |<----------- comp --------->| + * | | + * Generator -->Filter1 --->Emit --> |--> Worker -->|--> Coll --> Filter2 --> Gatherer + * | | + * |--> Worker -->| * + * NOTE: Since the Emit node is the last stage of a combine, which is added as farm Emitter + * filter, it must be defined as multi-output node. + * */ /* Author: Massimo Torquati * @@ -52,7 +54,7 @@ struct Generator: ff_node_t { int ntasks; }; -struct Emitter: ff_node_t { //ff_monode_t { +struct Emit: ff_monode_t { enum { HowMany=1 }; long *svc(long* in) { for(long i=0;i { //ff_monode_t { } return GO_ON; } + + void eosnotify(ssize_t ) { + std::cout << "Emitter eosnotify EOS received\n"; + } + + }; struct Filter1: ff_node_t { long *svc(long *in) { return in; } + void eosnotify(ssize_t ) { + std::cout << "Filter1 EOS received\n"; + } }; struct Filter2: ff_node_t { long *svc(long *in) { return in; } + void eosnotify(ssize_t ) { + std::cout << "Filter2 EOS received\n"; + } + + }; struct Worker: ff_node_t { long *svc(long *in) { if (get_my_id() == 0) usleep(10000); return in; } + + void eosnotify(ssize_t ) { + std::cout << "Worker" << get_my_id() << " eosnotify EOS received\n"; + } + + }; -struct Collector: ff_node_t { //ff_minode_t { +struct Coll: ff_node_t { long *svc(long *in) { return in; } + + void eosnotify(ssize_t ) { + std::cout << "Collector EOS received\n"; + } + }; struct Gatherer: ff_node_t { Gatherer(long ntasks):ntasks(ntasks) {} @@ -115,12 +142,12 @@ int main(int argc, char* argv[]) { Generator Gen(ntasks); Filter1 Fil1; Filter2 Fil2; - Emitter E; - Collector C; + Emit E; + Coll C; std::vector> W; for(int i=0;i()); - Gatherer Sink(Emitter::HowMany * ntasks); + Gatherer Sink(Emit::HowMany * ntasks); auto First = combine_nodes(Gen, combine_nodes(Fil1, E)); auto Third = combine_nodes(combine_nodes(C,Fil2), Sink); diff --git a/tests/test_staticallocator.cpp b/tests/test_staticallocator.cpp new file mode 100644 index 00000000..ba792e9c --- /dev/null +++ b/tests/test_staticallocator.cpp @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + + +#include +#include +#include +#include +#include + +using namespace ff; + +struct S_t { + int t; + float f; +}; + +long qlen = 2; +long howmany = 10; +long ntasks = 1000; + +std::mutex mtx; // used only for pretty printing + +struct Source: ff_monode_t { + Source(long ntasks): ntasks(ntasks) {} + + S_t* svc(S_t*) { + long start = get_my_id()*ntasks; + for (long i=1;i<=ntasks;++i){ + S_t* p; + SAlloc->alloc(p); + p->t = start+i; + p->f = p->t*1.0; + ff_send_out(p); + } + return EOS; + } + + long ntasks; + StaticAllocator* SAlloc = nullptr; +}; +struct FlatMap: ff_monode_t { + + S_t* svc(S_t* in) { + for(int i=0;ialloc(p); + *p = *in; + ff_send_out(p); + } + return GO_ON; + } + StaticAllocator *SAlloc = nullptr; +}; + +struct Map: ff_monode_t { + S_t* svc(S_t* in) { return in; } +}; + + +struct Sink: ff_minode_t { + S_t* svc(S_t* in) { + ++cnt2; + { + std::lock_guard lck (mtx); + std::cout << "Sink received " << in->t << ", " << in->f << "\n"; + } + if (in->t != cnt || in->f != (cnt*1.0)) + { + std::lock_guard lck (mtx); + std::cout << "Sink ERROR " << in->t << ", " << in->f << " cnt = " << cnt << "\n"; + abort(); + } + if (cnt2 == howmany) { + cnt++; + cnt2= 0; + } + return GO_ON; + } + + long cnt = 1; + long cnt2= 0; +}; + + +int main(int argc, char* argv[]) { + Source Sc(ntasks); + FlatMap FM; + Map M; + Sink Sk; + + + StaticAllocator* SourceAlloc = new StaticAllocator(qlen + 2, sizeof(S_t)); + StaticAllocator* FlatMapAlloc = new StaticAllocator(qlen*2 + 3, sizeof(S_t)); + + Sc.SAlloc = SourceAlloc; + FM.SAlloc = FlatMapAlloc; + + ff_pipeline pipeMain(false, qlen, qlen, true); + + pipeMain.add_stage(&Sc); + pipeMain.add_stage(&FM); + pipeMain.add_stage(&M); + pipeMain.add_stage(&Sk); + + if (pipeMain.run_and_wait_end()<0) { + error("running pipe\n"); + return -1; + } + + delete SourceAlloc; + delete FlatMapAlloc; + + { + std::lock_guard lck (mtx); + std::cout << "Done\n"; + } + return 0; +} + + + From af642cee9ce45c908f84561662bcdb0d323d7f65 Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Fri, 6 Nov 2020 17:08:16 +0100 Subject: [PATCH 010/202] extended the callback to handle both ff_send_out and ff_send_out_to, minor changes to internal_mo_transformer and internal_mi_transformer --- ff/combine.hpp | 20 +++++--------- ff/gt.hpp | 3 +- ff/lb.hpp | 4 +-- ff/mdf.hpp | 2 +- ff/multinode.hpp | 71 ++++++++++++++++++++++++++++++++---------------- ff/node.hpp | 15 +++++----- ff/selector.hpp | 4 +-- 7 files changed, 70 insertions(+), 49 deletions(-) diff --git a/ff/combine.hpp b/ff/combine.hpp index 7d1119aa..3e57b0ec 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -73,7 +73,7 @@ class ff_comb: public ff_minode { friend class ff_a2a; // used if the last stage has no output channel - static bool devnull(void*,unsigned long, unsigned long, void*) {return true;} + static bool devnull(void*,int,unsigned long, unsigned long, void*) {return true;} private: void registerAllGatherCallback(int (*cb)(void *,void **, void*), void * arg) { @@ -291,7 +291,7 @@ class ff_comb: public ff_minode { return ff_node::put(ptr); } - void registerCallback(bool (*cb)(void *,unsigned long,unsigned long,void *), void * arg) { + void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { comp_nodes[1]->registerCallback(cb,arg); } @@ -343,15 +343,9 @@ class ff_comb: public ff_minode { n2->get_out_nodes(w); if (w.size()==0) n2->registerCallback(devnull, nullptr); // devnull callback - else { - // NOTE: if the last stage of a composition is a multi-output node - // we must be sure that the callback is not set. - // see comments in lb.hpp (dryrun) and in multinode.hpp (ff_monode::ff_send_out_to) - n2->registerCallback(nullptr, nullptr); // resetting callback - } } else if (n2->get_out_buffer() == nullptr) - n2->registerCallback(devnull, nullptr); // devnull callback + n2->registerCallback(devnull, nullptr); // devnull callback prepared = true; @@ -622,15 +616,15 @@ class ff_comb: public ff_minode { if (getLast()->isMultiOutput()) return nullptr; return comp_nodes[1]->get_out_buffer(); } - inline bool ff_send_out(void * task, + inline bool ff_send_out(void * task, int id=0, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { - return comp_nodes[1]->ff_send_out(task,retry,ticks); + return comp_nodes[1]->ff_send_out(task,id,retry,ticks); } - inline bool ff_send_out_to(void * task,int, unsigned long retry=((unsigned long)-1), + inline bool ff_send_out_to(void * task,int id, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { - return comp_nodes[1]->ff_send_out(task,retry,ticks); + return comp_nodes[1]->ff_send_out(task,id,retry,ticks); } const struct timeval getstarttime() const { diff --git a/ff/gt.hpp b/ff/gt.hpp index 2de5e502..3b8c7aeb 100644 --- a/ff/gt.hpp +++ b/ff/gt.hpp @@ -307,9 +307,10 @@ class ff_gatherer: public ff_thread { void set_input_channelid(ssize_t id, bool fromin=true) { channelid = id; frominput=fromin;} - static bool ff_send_out_collector(void * task, + static bool ff_send_out_collector(void * task, int id, unsigned long retry, unsigned long ticks, void *obj) { + (void)id; bool r = ((ff_gatherer *)obj)->push(task, retry, ticks); #if defined(FF_TASK_CALLBACK) if (r) ((ff_gatherer *)obj)->callbackOut(obj); diff --git a/ff/lb.hpp b/ff/lb.hpp index cf3dce3a..a8d67db5 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -412,11 +412,11 @@ class ff_loadbalancer: public ff_thread { * \return The status of scheduled task, which can be either \p true or \p * false. */ - static inline bool ff_send_out_emitter(void * task, + static inline bool ff_send_out_emitter(void * task,int id, unsigned long retry, unsigned long ticks, void *obj) { - + (void)id; bool r= ((ff_loadbalancer *)obj)->schedule_task(task, retry, ticks); #if defined(FF_TASK_CALLBACK) // used to notify that a task has been sent out diff --git a/ff/mdf.hpp b/ff/mdf.hpp index 201d8839..3724b24a 100644 --- a/ff/mdf.hpp +++ b/ff/mdf.hpp @@ -91,7 +91,7 @@ class ff_mdf:public ff_pipeline { task_f_t *task = &(TASKS[ntasks++ % maxMsgs]); task->P = P; task->wtask = wtask; - while(!ff_send_out(task, 1)) ff_relax(1); + while(!ff_send_out(task, -1, 1)) ff_relax(1); } void *svc(void *) { diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 4e48fdb3..c409f5a3 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -180,12 +180,7 @@ class ff_minode: public ff_node { inline pthread_cond_t &get_prod_c() { return gt->get_prod_c(); } inline pthread_mutex_t &get_cons_m() { return gt->get_cons_m(); } inline pthread_cond_t &get_cons_c() { return gt->get_cons_c(); } - - void set_input_channelid(ssize_t id, bool fromin=true) { - gt->set_input_channelid(id, fromin); - } - virtual inline void get_in_nodes(svector&w) { size_t len=w.size(); // it is possible that the multi-input node is register @@ -284,6 +279,10 @@ class ff_minode: public ff_node { return 0; } + void set_input_channelid(ssize_t id, bool fromin=true) { + gt->set_input_channelid(id, fromin); + } + /** * \brief Assembly a input channel * @@ -301,6 +300,11 @@ class ff_minode: public ff_node { virtual bool isMultiInput() const { return true;} + + void set_running(int r) { + gt->running = r; + } + /** * \brief Skip first pop * @@ -639,7 +643,10 @@ class ff_monode: public ff_node { w += outputNodesFeedback; } - + void set_running(int r) { + lb->running = r; + } + /** * \brief Skips the first pop * @@ -655,23 +662,25 @@ class ff_monode: public ff_node { * * \return true if successful, false otherwise */ - inline bool ff_send_out_to(void *task, int id, unsigned long retry=((unsigned long)-1), - unsigned long ticks=(ff_node::TICKS2WAIT)) { + virtual inline bool ff_send_out_to(void *task, int id, unsigned long retry=((unsigned long)-1), + unsigned long ticks=(ff_node::TICKS2WAIT)) { // NOTE: this callback should be set only if the multi-output node is part of // a composition and the node is not the last stage - if (callback) return callback(task,retry,ticks, callback_arg); + if (callback) return callback(task,id, retry,ticks, callback_arg); return lb->ff_send_out_to(task,id,retry,ticks); } - inline bool ff_send_out(void * task, + inline bool ff_send_out(void * task, int id=0, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { // NOTE: this callback should be set only if the multi-output node is part of // a composition and it is not the last stage - if (callback) return callback(task,retry,ticks,callback_arg); + if (callback) return callback(task,id, retry,ticks,callback_arg); return lb->schedule_task(task,retry,ticks); } - + + // TODO: broadcast_task should have callback as in ff_send_out + // inline void broadcast_task(void *task) { lb->broadcast_task(task); } @@ -857,12 +866,10 @@ struct ff_monode_t: ff_monode { struct internal_mo_transformer: ff_monode { internal_mo_transformer(ff_node* n, bool cleanup=false): ff_monode(n),cleanup(cleanup),n(n) { - assert(!n->isMultiOutput()); } template internal_mo_transformer(const T& _n) { - assert(!_n.isMultiOutput()); T *t = new T(_n); assert(t); n = t; @@ -901,14 +908,19 @@ struct internal_mo_transformer: ff_monode { ff_monode::set_id(id); } + void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { + n->registerCallback(cb,arg); + } + int dryrun() { if (prepared) return 0; if (n) { - n->registerCallback(ff_send_out_motransformer, this); + if (!n->callback) + n->registerCallback(ff_send_out_motransformer, this); } return ff_monode::dryrun(); } - + int run(bool skip_init=false) { assert(n); if (!prepared) { @@ -924,13 +936,14 @@ struct internal_mo_transformer: ff_monode { return ff_monode::run(skip_init); } - static inline bool ff_send_out_motransformer(void * task, + + static inline bool ff_send_out_motransformer(void * task, int id, unsigned long retry, unsigned long ticks, void *obj) { - bool r= ((internal_mo_transformer *)obj)->ff_send_out(task, retry, ticks); + bool r= ((internal_mo_transformer *)obj)->ff_send_out_to(task, id, retry, ticks); return r; } - + bool cleanup; ff_node *n=nullptr; }; @@ -968,7 +981,7 @@ struct internal_mi_transformer: ff_minode { if (cleanup && n) delete n; } - inline void* svc(void*task) {return n->svc(task);} + inline void* svc(void*task) { return n->svc(task); } int set_input(const svector & w) { n->neos += w.size(); @@ -991,14 +1004,26 @@ struct internal_mi_transformer: ff_minode { return 0; } - void registerCallback(bool (*cb)(void *,unsigned long,unsigned long,void *), void * arg) { + void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { n->registerCallback(cb,arg); } - + int set_output_buffer(FFBUFFER * const o) { - return ff_minode::getgt()->set_output_buffer(o); + return n->set_output_buffer(o); } + bool init_output_blocking(pthread_mutex_t *&m, + pthread_cond_t *&c, + bool feedback=true) { + return n->init_output_blocking(m,c,feedback); + } + + void set_output_blocking(pthread_mutex_t *&m, + pthread_cond_t *&c, + bool canoverwrite=false) { + n->set_output_blocking(m,c, canoverwrite); + } + inline void eosnotify(ssize_t id) { n->eosnotify(id); } void set_id(ssize_t id) { diff --git a/ff/node.hpp b/ff/node.hpp index 6ddeaee1..cfba74c7 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -511,7 +511,7 @@ class ff_node { bool my_own_thread; ff_thread * thread; /// A \p thWorker object, which extends the \p ff_thread class - bool (*callback)(void *,unsigned long,unsigned long, void *); + bool (*callback)(void *, int, unsigned long,unsigned long, void *); void * callback_arg; BARRIER_T * barrier; /// A \p Barrier object struct timeval tstart; @@ -1125,10 +1125,10 @@ class ff_node { * \param ticks delay between successive retries * */ - virtual bool ff_send_out(void * task, + virtual bool ff_send_out(void * task, int id=0, unsigned long retry=((unsigned long)-1), unsigned long ticks=(TICKS2WAIT)) { - if (callback) return callback(task,retry,ticks,callback_arg); + if (callback) return callback(task,id,retry,ticks,callback_arg); bool r =Push(task,retry,ticks); #if defined(FF_TASK_CALLBACK) if (r) callbackOut(); @@ -1207,7 +1207,7 @@ class ff_node { /** * used for composition (see ff_comb) */ - static inline bool ff_send_out_comp(void * task,unsigned long /*retry*/,unsigned long /*ticks*/, void *obj) { + static inline bool ff_send_out_comp(void * task, int, unsigned long /*retry*/,unsigned long /*ticks*/, void *obj) { return ((ff_node *)obj)->push_comp_local(task); } @@ -1280,7 +1280,7 @@ class ff_node { in_active= onoff; } - virtual void registerCallback(bool (*cb)(void *,unsigned long,unsigned long,void *), void * arg) { + virtual void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { callback=cb; callback_arg=arg; } @@ -1600,8 +1600,9 @@ struct ff_buffernode: ff_node { return 0; } - bool ff_send_out(void *ptr, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { - return ff_node::ff_send_out(ptr,retry,ticks); + bool ff_send_out(void *ptr, int id=0, + unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { + return ff_node::ff_send_out(ptr,id,retry,ticks); } bool gather_task(void **task, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { bool r =ff_node::Pop(task,retry,ticks); diff --git a/ff/selector.hpp b/ff/selector.hpp index 41db2002..e39120a4 100644 --- a/ff/selector.hpp +++ b/ff/selector.hpp @@ -47,10 +47,10 @@ namespace ff { template class ff_nodeSelector: public ff_node_t { protected: - static bool ff_send_out_selector(void * task, + static bool ff_send_out_selector(void * task,int id, unsigned long retry, unsigned long ticks, void *obj) { - return reinterpret_cast(obj)->ff_send_out(task, retry, ticks); + return reinterpret_cast(obj)->ff_send_out(task, id, retry, ticks); } From 269f48c92b3e9f1fd5f1708d7bf88333390f691b Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Fri, 13 Nov 2020 17:52:32 +0100 Subject: [PATCH 011/202] minor fix to the default parameters of the ff_send_out --- ff/combine.hpp | 2 +- ff/multinode.hpp | 2 +- ff/node.hpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ff/combine.hpp b/ff/combine.hpp index 3e57b0ec..e0fa1ef2 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -616,7 +616,7 @@ class ff_comb: public ff_minode { if (getLast()->isMultiOutput()) return nullptr; return comp_nodes[1]->get_out_buffer(); } - inline bool ff_send_out(void * task, int id=0, + inline bool ff_send_out(void * task, int id=-1, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { return comp_nodes[1]->ff_send_out(task,id,retry,ticks); diff --git a/ff/multinode.hpp b/ff/multinode.hpp index c409f5a3..e62e3d93 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -670,7 +670,7 @@ class ff_monode: public ff_node { return lb->ff_send_out_to(task,id,retry,ticks); } - inline bool ff_send_out(void * task, int id=0, + inline bool ff_send_out(void * task, int id=-1, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { // NOTE: this callback should be set only if the multi-output node is part of diff --git a/ff/node.hpp b/ff/node.hpp index cfba74c7..6e660890 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1125,7 +1125,7 @@ class ff_node { * \param ticks delay between successive retries * */ - virtual bool ff_send_out(void * task, int id=0, + virtual bool ff_send_out(void * task, int id=-1, unsigned long retry=((unsigned long)-1), unsigned long ticks=(TICKS2WAIT)) { if (callback) return callback(task,id,retry,ticks,callback_arg); @@ -1600,7 +1600,7 @@ struct ff_buffernode: ff_node { return 0; } - bool ff_send_out(void *ptr, int id=0, + bool ff_send_out(void *ptr, int id=-1, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { return ff_node::ff_send_out(ptr,id,retry,ticks); } From 8b291c069fd8f8eaf1e6d13f6a32d5c9f48b363f Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 9 Dec 2020 09:50:18 +0100 Subject: [PATCH 012/202] New version of the staticallocator (still experimental). Added a new class ff_comb_t. More work for heterogeneous all-to-all. Bug fixes. --- ff/all2all.hpp | 33 +++-- ff/combine.hpp | 93 ++++++++++--- ff/farm.hpp | 10 -- ff/gt.hpp | 28 +--- ff/lb.hpp | 39 ++++-- ff/multinode.hpp | 24 ++-- ff/node.hpp | 24 ++-- ff/pipeline.hpp | 63 +++++---- ff/staticallocator.hpp | 81 ++++++++---- tests/Makefile | 2 +- tests/test_all-to-all18.cpp | 105 +++++++++++++++ tests/test_all-to-all19.cpp | 199 ++++++++++++++++++++++++++++ tests/test_staticallocator.cpp | 16 ++- tests/test_staticallocator2.cpp | 219 +++++++++++++++++++++++++++++++ tests/test_staticallocator3.cpp | 226 ++++++++++++++++++++++++++++++++ 15 files changed, 998 insertions(+), 164 deletions(-) create mode 100644 tests/test_all-to-all18.cpp create mode 100644 tests/test_all-to-all19.cpp create mode 100644 tests/test_staticallocator2.cpp create mode 100644 tests/test_staticallocator3.cpp diff --git a/ff/all2all.hpp b/ff/all2all.hpp index d750f415..5e9aa01e 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -293,21 +293,25 @@ class ff_a2a: public ff_node { * e.g., a composition where the last stage is a multi-output node * */ - int add_firstset(const std::vector & w, int ondemand=0, bool cleanup=false) { + template + int add_firstset(const std::vector & w, int ondemand=0, bool cleanup=false) { if (workers1.size()>0) { error("A2A, add_firstset cannot be called multiple times\n"); return -1; - } + } if (w.size()==0) { error("A2A, try to add zero workers to the first set!\n"); return -1; } - for(size_t i=0;i& w, int ondemand=0, bool cleanup=false) { + template + int change_firstset(const std::vector& w, int ondemand=0, bool cleanup=false) { workers1.clear(); return add_firstset(w, ondemand, cleanup); } @@ -315,7 +319,8 @@ class ff_a2a: public ff_node { * The nodes of the second set must be either standard ff_node or a node that is multi-input. * */ - int add_secondset(const std::vector & w, bool cleanup=false) { + template + int add_secondset(const std::vector & w, bool cleanup=false) { if (workers2.size()>0) { error("A2A, add_secondset cannot be called multiple times\n"); return -1; @@ -324,11 +329,14 @@ class ff_a2a: public ff_node { error("A2A, try to add zero workers to the second set!\n"); return -1; } - for(size_t i=0;i& w, bool cleanup=false) { + template + int change_secondset(const std::vector& w, bool cleanup=false) { workers2.clear(); return add_secondset(w, cleanup); } @@ -678,14 +686,15 @@ class ff_a2a: public ff_node { return 0; } - bool init_input_blocking(pthread_mutex_t *&, - pthread_cond_t *&, + bool init_input_blocking(pthread_mutex_t *&m, + pthread_cond_t *&c, bool /*feedback*/=true) { size_t nworkers1 = workers1.size(); for(size_t i=0;iinit_input_blocking(m,c)) return false; + pthread_mutex_t *m1 = NULL; + pthread_cond_t *c1 = NULL; + if (!workers1[i]->init_input_blocking(m1,c1)) return false; + if (nworkers1==1) { m=m1; c=c1; } } return true; } diff --git a/ff/combine.hpp b/ff/combine.hpp index e0fa1ef2..e2f8f3f4 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -36,6 +36,10 @@ * Author: Massimo Torquati * */ +// This file contains the ff_comb combiner building block class +// the ff_comb_t class, which is the type-preserving version of ff_comb, +// and some helper functions, e.g., combine_nodes, combine_farms, etc. + #include #include @@ -141,7 +145,7 @@ class ff_comb: public ff_minode { if (w.size() == 0) getFirst()->skipfirstpop(true); return ff_minode::run(); } - + if (ff_node::run(true)<0) return -1; return 0; } @@ -324,17 +328,17 @@ class ff_comb: public ff_minode { int prepare() { if (prepared) return 0; connectCallback(); - dryrun(); // checking if the first node is a multi-input node ff_node *n1 = getFirst(); if (n1->isMultiInput()) { // here we substitute the gt ((ff_minode*)n1)->setgt(ff_minode::getgt()); - - if (ff_minode::prepare()<0) return -1; } - + // dryrun should be executed here because the gt of the + // first node might have been substituted + ff_comb::dryrun(); + // registering a special callback if the last stage does // not have an output channel ff_node *n2 = getLast(); @@ -486,7 +490,7 @@ class ff_comb: public ff_minode { } void eosnotify(ssize_t id=-1) { - comp_nodes[0]->eosnotify(); + comp_nodes[0]->eosnotify(id); ++neos; // if the first node is multi-input or is a comp passed as filter to a farm collector, @@ -539,7 +543,10 @@ class ff_comb: public ff_minode { r=ff_node::set_input_buffer(w[0]->get_in_buffer()); return r; } - return ff_node::create_input_buffer(nentries,fixedsize); + int r = ff_node::create_input_buffer(nentries,fixedsize); + if (r<0) return r; + r = getFirst()->set_input_buffer(ff_node::get_in_buffer()); + return r; } int create_output_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { return comp_nodes[1]->create_output_buffer(nentries,fixedsize); @@ -580,7 +587,14 @@ class ff_comb: public ff_minode { w[i]->set_output_blocking(m,c); return true; } - return ff_node::init_input_blocking(m,c); + bool r = ff_node::init_input_blocking(m,c); + if (!r) return false; + // if the first node is a standard node or a multi-output node + // then the comb node and the first node share the same + // cond variable. This is due to the put_done method in the lb + // (i.e. the prev node is a multi-output or an emitter node) + n->cons_c = c; + return true; } // producer bool init_output_blocking(pthread_mutex_t *&m, @@ -598,14 +612,6 @@ class ff_comb: public ff_minode { // uses as output channel(s) the one(s) of the second node. // these functions should not be called if the node is multi-output inline bool get(void **ptr) { return comp_nodes[1]->get(ptr);} - inline pthread_mutex_t &get_prod_m() { return comp_nodes[1]->get_prod_m();} - inline pthread_cond_t &get_prod_c() { return comp_nodes[1]->get_prod_c();} - - inline pthread_mutex_t &get_cons_m() { - ff_node *n = getFirst(); - if (n->isMultiInput()) return ff_minode::get_cons_m(); - return ff_node::get_cons_m(); - } inline pthread_cond_t &get_cons_c() { ff_node *n = getFirst(); if (n->isMultiInput()) return ff_minode::get_cons_c(); @@ -652,6 +658,61 @@ class ff_comb: public ff_minode { ssize_t neos=0; }; +/* + * Type-preserving combiner building block + * + */ +template +struct ff_comb_t: ff_comb { + typedef TIN IN_t; + typedef T T_t; + typedef TOUT OUT_t; + + ff_comb_t(ff_node_t* n1, ff_node_t* n2): + ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_node_t* n1, ff_minode_t* n2): + ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_node_t* n1, ff_monode_t* n2): + ff_comb(n1,n2,false,false) {} + template + ff_comb_t(ff_node_t* n1, ff_comb_t* n2): + ff_comb(n1,n2,false,false) {} + + + ff_comb_t(ff_minode_t* n1, ff_node_t* n2): + ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_minode_t* n1, ff_minode_t* n2): + ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_minode_t* n1, ff_monode_t* n2): + ff_comb(n1,n2,false,false) {} + template + ff_comb_t(ff_minode_t* n1, ff_comb_t* n2): + ff_comb(n1,n2,false,false) {} + + ff_comb_t(ff_monode_t* n1, ff_node_t* n2): + ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_monode_t* n1, ff_minode_t* n2): + ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_monode_t* n1, ff_monode_t* n2): + ff_comb(n1,n2,false,false) {} + template + ff_comb_t(ff_monode_t* n1, ff_comb_t* n2): + ff_comb(n1,n2,false,false) {} + + template + ff_comb_t(ff_comb_t* n1, ff_node_t* n2): + ff_comb(n1,n2,false,false) {} + template + ff_comb_t(ff_comb_t* n1, ff_minode_t* n2): + ff_comb(n1,n2,false,false) {} + template + ff_comb_t(ff_comb_t* n1, ff_monode_t* n2): + ff_comb(n1,n2,false,false) {} + template + ff_comb_t(ff_comb_t* n1, ff_comb_t* n2): + ff_comb(n1,n2,false,false) {} +}; + /* *************************************************************************** * * * diff --git a/ff/farm.hpp b/ff/farm.hpp index c815c3a5..a1508639 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -709,18 +709,8 @@ class ff_farm: public ff_node { } } - virtual inline pthread_mutex_t &get_cons_m() { return *(lb->cons_m);} virtual inline pthread_cond_t &get_cons_c() { return *(lb->cons_c);} - virtual inline pthread_mutex_t &get_prod_m() { - assert(collector && !collector_removed); - return *((collector==(ff_node*)gt) ? gt->prod_m:collector->prod_m); - } - virtual inline pthread_cond_t &get_prod_c() { - assert(collector && !collector_removed); - return *((collector==(ff_node*)gt) ? gt->prod_c:collector->prod_c); - } - public: enum { DEF_IN_BUFF_ENTRIES=DEFAULT_BUFFER_CAPACITY, DEF_IN_OUT_DIFF=DEFAULT_IN_OUT_CAPACITY_DIFFERENCE, diff --git a/ff/gt.hpp b/ff/gt.hpp index 3b8c7aeb..c70b64be 100644 --- a/ff/gt.hpp +++ b/ff/gt.hpp @@ -117,22 +117,6 @@ class ff_gatherer: public ff_thread { filter->set_output_blocking(m,c, canoverwrite); } } - - inline pthread_mutex_t &get_prod_m() { - if (!prod_m && filter && - ( (filter->get_out_buffer()!=nullptr) || filter->isMultiOutput() ) ) { - return filter->get_prod_m(); - } - return *prod_m; - } - inline pthread_cond_t &get_prod_c() { - if (!prod_c && filter && - ( (filter->get_out_buffer()!=nullptr) || filter->isMultiOutput() ) ) { - return filter->get_prod_c(); - } - return *prod_c; - } - inline pthread_mutex_t &get_cons_m() { return *cons_m; } inline pthread_cond_t &get_cons_c() { return *cons_c; } @@ -160,11 +144,11 @@ class ff_gatherer: public ff_thread { virtual inline void notifyeos(int /*id*/) {} /** - * \brief Gets the number of tentatives. + * \brief Gets the number of attempts. * - * The number of tentative before wasting some times and than retry + * The number of attempts before wasting some times and than retry */ - virtual inline size_t ntentative() { return getnworkers();} + virtual inline size_t nattempts() { return getnworkers();} /** * \brief Loses the time out. @@ -218,7 +202,7 @@ class ff_gatherer: public ff_thread { if (workers[nextr]->get(task)) { return nextr; } - else if (++cnt == ntentative()) break; + else if (++cnt == nattempts()) break; } while(1); if (blocking_in) { struct timespec tv; @@ -686,7 +670,7 @@ class ff_gatherer: public ff_thread { int dryrun() { running=workers.size(); if (filter) { - if ((filter->get_out_buffer() == nullptr) && buffer) // TODO WARNING: <----- TO BE CHECKED vedi dryrun di lb.hpp + if ((filter->get_out_buffer() == nullptr) && buffer) filter->registerCallback(ff_send_out_collector, this); // setting the thread for the filter filter->setThread(this); @@ -705,7 +689,7 @@ class ff_gatherer: public ff_thread { * \return 0 if successful, otherwise -1 is returned. */ int run(bool=false) { - dryrun(); + ff_gatherer::dryrun(); if (this->spawn(filter?filter->getCPUId():-1)== -2) { error("GT, spawning GT thread\n"); diff --git a/ff/lb.hpp b/ff/lb.hpp index a8d67db5..2dd318ef 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -72,7 +72,7 @@ class ff_loadbalancer: public ff_thread { // NOTE: // - TICKS2WAIT should be a valued profiled for the application - // Consider to redifine losetime_in and losetime_out for your app. + // Consider to redefine losetime_in and losetime_out for your app. // enum {TICKS2WAIT=1000}; protected: @@ -116,10 +116,6 @@ class ff_loadbalancer: public ff_thread { assert(1==0); } - virtual inline pthread_mutex_t &get_prod_m() { return *prod_m;} - virtual inline pthread_cond_t &get_prod_c() { return *prod_c;} - - virtual inline pthread_mutex_t &get_cons_m() { return *cons_m;} virtual inline pthread_cond_t &get_cons_c() { return *cons_c;} /** @@ -181,13 +177,13 @@ class ff_loadbalancer: public ff_thread { #endif /** - * \brief Gets the number of tentatives before wasting some times + * \brief Gets the number of attempts before wasting some times * - * The number of tentative before wasting some times and than retry. + * The number of attempts before wasting some times and than retry. * * \return The number of workers. */ - virtual inline size_t ntentative() { return running;} + virtual inline size_t nattempts() { return running;} /** * \brief Loses some time before sending the message to output buffer @@ -235,7 +231,7 @@ class ff_loadbalancer: public ff_thread { */ virtual inline bool schedule_task(void * task, unsigned long retry=((unsigned long)-1), - unsigned long /*ticks*/=0) { + unsigned long ticks=TICKS2WAIT) { unsigned long cnt; if (blocking_out) { do { @@ -253,7 +249,7 @@ class ff_loadbalancer: public ff_thread { return true; } ++cnt; - if (cnt == ntentative()) break; + if (cnt == nattempts()) break; } while(1); struct timespec tv; @@ -278,9 +274,9 @@ class ff_loadbalancer: public ff_thread { } ++cnt; if (cnt>=retry) { nextw=-1; return false; } - if (cnt == ntentative()) break; + if (cnt == nattempts()) break; } while(1); - losetime_out(); + losetime_out(ticks); } while(1); return false; } @@ -443,7 +439,22 @@ class ff_loadbalancer: public ff_thread { assert((task == FF_EOS) || (task == FF_EOS_NOFREEZE)); } } - + + int get_next_free_channel(bool forever=true) { + long x=1; + const size_t attempts = (forever ? (size_t)-1 : running); + do { + int nextone = (nextw + x) % running; + FFBUFFER* buf = workers[nextone]->get_in_buffer(); + + if (buf->buffersize()>buf->length()) { + nextw = (nextw + x - 1) % running; + return nextone; + } + if ((x % running) == 0) losetime_out(); + } while((size_t)++x <= attempts); + return -1; + } #if defined(FF_TASK_CALLBACK) virtual void callbackIn(void *t=NULL) { if (filter) filter->callbackIn(t); } @@ -1129,7 +1140,7 @@ class ff_loadbalancer: public ff_thread { * \return 0 if successful, otherwise -1 is returned. */ int runlb(bool=false, ssize_t nw=-1) { - dryrun(); + ff_loadbalancer::dryrun(); running = (nw<=0)?(feedbackid>0?feedbackid:workers.size()):nw; if (this->spawn(filter?filter->getCPUId():-1) == -2) { diff --git a/ff/multinode.hpp b/ff/multinode.hpp index e62e3d93..bc48c174 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -115,7 +115,7 @@ class ff_minode: public ff_node { int prepare() { if (prepared) return 0; - if (dryrun()<0) return -1; + if (ff_minode::dryrun()<0) return -1; prepared=true; return 0; } @@ -176,9 +176,6 @@ class ff_minode: public ff_node { ff_node::set_output_blocking(m,c, canoverwrite); } - inline pthread_mutex_t &get_prod_m() { return gt->get_prod_m(); } - inline pthread_cond_t &get_prod_c() { return gt->get_prod_c(); } - inline pthread_mutex_t &get_cons_m() { return gt->get_cons_m(); } inline pthread_cond_t &get_cons_c() { return gt->get_cons_c(); } virtual inline void get_in_nodes(svector&w) { @@ -487,7 +484,7 @@ class ff_monode: public ff_node { int prepare() { if (prepared) return 0; - if (dryrun()<0) return -1; + if (ff_monode::dryrun()<0) return -1; prepared=true; return 0; } @@ -520,10 +517,6 @@ class ff_monode: public ff_node { ff_node::set_output_blocking(m,c, canoverwrite); } - virtual inline pthread_mutex_t &get_prod_m() { return lb->get_prod_m();} - virtual inline pthread_cond_t &get_prod_c() { return lb->get_prod_c();} - - virtual inline pthread_mutex_t &get_cons_m() { return lb->get_cons_m();} virtual inline pthread_cond_t &get_cons_c() { return lb->get_cons_c();} public: @@ -657,6 +650,14 @@ class ff_monode: public ff_node { ff_node::skipfirstpop(sk); } + /** + * \brief Provides the next channel id that will be selected for sending out the task + * + */ + int get_next_free_channel(bool forever=true) { + return lb->get_next_free_channel(forever); + } + /** * \brief Sends one task to a specific node id. * @@ -667,6 +668,7 @@ class ff_monode: public ff_node { // NOTE: this callback should be set only if the multi-output node is part of // a composition and the node is not the last stage if (callback) return callback(task,id, retry,ticks, callback_arg); + assert(id>=0); return lb->ff_send_out_to(task,id,retry,ticks); } @@ -940,7 +942,7 @@ struct internal_mo_transformer: ff_monode { static inline bool ff_send_out_motransformer(void * task, int id, unsigned long retry, unsigned long ticks, void *obj) { - bool r= ((internal_mo_transformer *)obj)->ff_send_out_to(task, id, retry, ticks); + bool r= ((internal_mo_transformer *)obj)->ff_send_out_to(task, (id<0?0:id), retry, ticks); return r; } @@ -1030,7 +1032,7 @@ struct internal_mi_transformer: ff_minode { if (n) n->set_id(id); ff_minode::set_id(id); } - + int run(bool skip_init=false) { assert(n); if (!prepared) { diff --git a/ff/node.hpp b/ff/node.hpp index 6e660890..224b3d13 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -616,18 +616,14 @@ class ff_node { pthread_cond_t *&c, bool canoverwrite=false) { assert(canoverwrite || - (p_cons_m == nullptr && p_cons_c == nullptr) || - (p_cons_m == m && p_cons_c == c)); + (p_cons_c == nullptr) || + (p_cons_c == c)); FF_IGNORE_UNUSED(canoverwrite); - p_cons_m = m, p_cons_c = c; + FF_IGNORE_UNUSED(m); + p_cons_c = c; } - - virtual inline pthread_mutex_t &get_cons_m() { return *cons_m;} virtual inline pthread_cond_t &get_cons_c() { return *cons_c;} - virtual inline pthread_mutex_t &get_prod_m() { return *prod_m;} - virtual inline pthread_cond_t &get_prod_c() { return *prod_c;} - /** * \brief Set the ff_node to start with no input task * @@ -731,7 +727,9 @@ class ff_node { } virtual inline int set_input(const svector &) { return -1;} - virtual inline int set_input(ff_node *) { return -1;} + virtual inline int set_input(ff_node *n) { + return set_input_buffer(n->get_in_buffer()); + } virtual inline int set_input_feedback(ff_node *) { return -1;} virtual inline int set_output(const svector &) { return -1;} virtual inline int set_output(ff_node *n) { @@ -1143,7 +1141,7 @@ class ff_node { } /** - * checking for multi-input/output, all-to-all, farm + * checking for multi-input/output, all-to-all, farm, pipe * */ virtual inline bool isMultiInput() const { return false; } @@ -1233,7 +1231,7 @@ class ff_node { wttime=0; FFTRACE(taskcnt=0;lostpushticks=0;pushwait=0;lostpopticks=0;popwait=0;ticksmin=(ticks)-1;ticksmax=0;tickstot=0); - p_cons_m = NULL, p_cons_c = NULL; + p_cons_c = NULL; blocking_in = blocking_out = FF_RUNTIME_MODE; }; @@ -1246,7 +1244,7 @@ class ff_node { wtstart = n.wtstart; wtstop = n.wtstop; wttime = n.wttime; - p_cons_m = n.p_cons_m, p_cons_c = n.p_cons_c; + p_cons_c = n.p_cons_c; blocking_in = n.blocking_in; blocking_out = n.blocking_out; default_mapping = n.default_mapping; @@ -1471,7 +1469,6 @@ class ff_node { pthread_cond_t *prod_c = nullptr; // for synchronizing with the next multi-input stage - pthread_mutex_t *p_cons_m = nullptr; pthread_cond_t *p_cons_c = nullptr; bool FF_MEM_ALIGN(blocking_in,32); @@ -1619,7 +1616,6 @@ struct ff_buffernode: ff_node { protected: void* svc(void*){return NULL;} - pthread_mutex_t &get_cons_m() { return *p_cons_m;} pthread_cond_t &get_cons_c() { return *p_cons_c;} diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 05488342..1ff20512 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -102,20 +102,22 @@ class ff_pipeline: public ff_node { const bool curr_single_standard = (!nodes_list[i]->isMultiInput()); // the farm is considered single_multiinput const bool curr_single_multiinput = (!isa2a_curr && nodes_list[i]->isMultiInput()); - const bool curr_multi_standard = [&]() { - if (!isa2a_curr) return false; - const svector& w1=isa2a_getfirstset(get_node(i)); - assert(w1.size()>0); - if (w1[0]->isMultiInput()) return false; // NOTE: we suppose homogeneous first set - return true; - }(); const bool curr_multi_multiinput = [&]() { if (!isa2a_curr) return false; const svector& w1=isa2a_getfirstset(get_node(i)); assert(w1.size()>0); - if (w1[0]->isMultiInput()) return true; // NOTE: we suppose homogeneous first set + for(size_t k=0;k w2(1); + w1[k]->get_in_nodes(w2); + for(size_t j=0;jisMultiInput()) return true; + } return false; } (); + const bool curr_multi_standard = [&]() { + if (!isa2a_curr) return false; + return !curr_multi_multiinput; + }(); const bool isa2a_prev = get_node_last(i-1)->isAll2All(); const bool isfarm_prev = get_node_last(i-1)->isFarm(); const bool prev_isfarm_nocollector = (isfarm_prev && !get_node_last(i-1)->isOFarm() && !(isfarm_withcollector(get_node_last(i-1)))); @@ -248,7 +250,6 @@ class ff_pipeline: public ff_node { error("PIPE, init output blocking mode for node %d\n", i); return -1; } - // ------------------------------------------------------------ } } @@ -267,6 +268,18 @@ class ff_pipeline: public ff_node { nodes_list[i]->get_in_nodes(w); assert(w.size() == 1); if (nodes_list[i-1]->set_output_buffer(w[0]->get_in_buffer())<0) return -1; + + // blocking stuff -------------------------------------------- + if (!nodes_list[i]->init_input_blocking(m,c)) { + error("PIPE, init input blocking mode for node %d\n", i); + return -1; + } + nodes_list[i-1]->set_output_blocking(m,c); + if (!nodes_list[i-1]->init_output_blocking(m,c)) { + error("PIPE, init output blocking mode for node %d\n", i-1); + return -1; + } + // ----------------------------------------------------------- } else { if (prev_single_multioutput) { if (a2a->create_input_buffer(in_buffer_entries, fixedsize)<0) return -1; @@ -275,15 +288,7 @@ class ff_pipeline: public ff_node { if (nodes_list[i-1]->set_output(w)<0) return -1; // blocking stuff -------------------------------------------- - w.clear(); - nodes_list[i]->get_in_nodes(w); - assert(w.size() == W1.size()); - for(size_t j=0;jinit_input_blocking(m,c)) return -1; - w[j]->set_output_blocking(m,c); - - if (!w[j]->init_output_blocking(m,c)) return -1; - } + if (!a2a->init_input_blocking(m,c)) return -1; if (!nodes_list[i-1]->init_output_blocking(m,c)) { error("PIPE, init output blocking mode for node %d\n", i-1); return -1; @@ -343,10 +348,14 @@ class ff_pipeline: public ff_node { } if (prev_single_multioutput) { for(size_t j=0;jset_input(t); - nodes_list[i-1]->set_output(t); + svector w(MAX_NUM_THREADS); + W1[j]->get_in_nodes(w); + for(size_t k=0;kset_input(t); + nodes_list[i-1]->set_output(t); + } } } if (prev_multi_standard) { @@ -1510,18 +1519,8 @@ class ff_pipeline: public ff_node { nodes_list[last]->set_output_blocking(m,c, canoverwrite); } - virtual inline pthread_mutex_t &get_cons_m() { return nodes_list[0]->get_cons_m();} virtual inline pthread_cond_t &get_cons_c() { return nodes_list[0]->get_cons_c();} - virtual inline pthread_mutex_t &get_prod_m() { - const int last = static_cast(nodes_list.size())-1; - return nodes_list[last]->get_prod_m(); - } - virtual inline pthread_cond_t &get_prod_c() { - const int last = static_cast(nodes_list.size())-1; - return nodes_list[last]->get_prod_c(); - } - int create_input_buffer(int nentries, bool fixedsize) { if (in) return -1; diff --git a/ff/staticallocator.hpp b/ff/staticallocator.hpp index a86a2caa..fd880574 100644 --- a/ff/staticallocator.hpp +++ b/ff/staticallocator.hpp @@ -36,59 +36,84 @@ #include #include -/* Author: - * Massimo Torquati - * +/* Author: Massimo Torquati + * December 2020 */ + namespace ff { class StaticAllocator { public: - StaticAllocator(const size_t nslot, const size_t slotsize): - nslot(nslot),slotsize(slotsize), cnt(0), segment(nullptr) { + StaticAllocator(const size_t _nslot, const size_t slotsize, const int nchannels=1): + ssize(slotsize), nchannels(nchannels), cnts(nchannels,0), segment(nullptr) { + + assert(nchannels>0); + assert(slotsize>0); + ssize = slotsize + sizeof(long*); + + // rounding up nslot to be multiple of nchannels + nslot = ((_nslot + nchannels -1) / nchannels) * nchannels; + slotsxchannel = nslot / nchannels; + + void* result = 0; - result = mmap(NULL, nslot*slotsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + result = mmap(NULL, nslot*ssize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if (result == MAP_FAILED) abort(); segment = (char*)result; - printf("allocated %ld bytes\n", nslot*slotsize); + + // initialize the "header" + char* p = segment; + for(size_t i=0; i - void alloc(T*& p) { - size_t m = cnt.fetch_add(1,std::memory_order_relaxed) % nslot; - char *mp = segment + m*slotsize; - new (mp) T(); - p = reinterpret_cast(mp); - } - - template - void realloc(T* in, S*& out) { - dealloc(in); - char *mp = reinterpret_cast(in); - new (mp) S(); - out = reinterpret_cast(mp); + void alloc(T*& p, int channel=0) { + assert(channel>=0 && channel - void dealloc(T* p) { + static inline void dealloc(T* p) { p->~T(); + long** _p = (long**)((char*)p-sizeof(long*)); + _p[0] = (long*)FF_GO_ON; // the slot is free and can be re-used } + template + void realloc(T* in, S*& out) { + in->~T(); + char* mp = reinterpret_cast(in); + out = new (mp) S(); + } + private: - const size_t nslot; - const size_t slotsize; - std::atomic_ullong cnt; - char *segment; + size_t nslot; // total number of slots in the data segment + size_t slotsxchannel; // how many slots for each sub data segment + size_t ssize; // size of a data slot (real size + sizeof(long*)) + int nchannels; // number of sub data segments + std::vector cnts; // counters + char *segment; // data segment }; }; diff --git a/tests/Makefile b/tests/Makefile index a1cfccd3..2698aaf2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator test_staticallocator2 test_staticallocator3 test_staticallocator4 #test_taskf2 test_taskf3 diff --git a/tests/test_all-to-all18.cpp b/tests/test_all-to-all18.cpp new file mode 100644 index 00000000..11ccec35 --- /dev/null +++ b/tests/test_all-to-all18.cpp @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ +/* + * Left -->| + * | + * ... | --> checkEos --> Right + * | + * Left -->| |<--- combine ---> | + * + * |<----------- all-to-all -------->| + */ +/* + * + * Author: Massimo Torquati + */ + +#include +#include +#include + +using namespace ff; + +struct Left: ff_monode_t { + Left(long ntasks): ntasks(ntasks) {} + + long* svc(long*) { + for (long i=1;i<=ntasks;++i) + ff_send_out(&i); + return EOS; + } + long ntasks; +}; +struct CheckEOS:ff_minode_t { + + long* svc(long* in) { + printf("input from %ld\n", get_channel_id()); + return in; + } + void eosnotify(ssize_t id) { + printf("received EOS from Left%ld\n", id); + } +}; + +struct Right: ff_minode_t { + Right(const bool propagate=false):propagate(propagate) {} + long* svc(long* in) { + if (propagate) return in; + return GO_ON; + } + const bool propagate; +}; + + +int main(int argc, char* argv[]) { + long ntasks = 10; + long nlefts = 4; + + std::vector W1; + for(long i=0;i W2; + ff_comb comb(new CheckEOS, new Right(false), true,true); + ff_pipeline pipe1; + pipe1.add_stage(&comb); + W2.push_back(&pipe1); + + ff_a2a a2a; + a2a.add_firstset(W1, 0, true); + a2a.add_secondset(W2); + + if (a2a.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + std::cout << "Done\n"; + return 0; +} + + + diff --git a/tests/test_all-to-all19.cpp b/tests/test_all-to-all19.cpp new file mode 100644 index 00000000..393fdcee --- /dev/null +++ b/tests/test_all-to-all19.cpp @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ +/* + * Example of heterogeneous all-to-all with complex building-blocks nesting. + * + * + * | |----->| WR - WRoutH | ---------> | + * |-> WL -> WLoutH ->| | | | + * | | | | | + * | |->| WL11inH - WL11 |->| | | + * |-> WL -> WLoutH ->| | |-> WR - WRoutH | | + * | | | | | + * Sc->| |->| |-> WR1 -> WR11 ->|-> Sk + * | | |-> WR - WRoutH | | + * | | | | + * | ------> | WL - WL11 | ----------------->| + * + */ +/* + * + * Author: Massimo Torquati + */ + + +#include +#include + +using namespace ff; + +struct S_t { + int t; + float f; +}; + +struct Source: ff_monode_t { + Source(long ntasks): ntasks(ntasks) {} + S_t* svc(S_t*) { + for (long i=1;i<=ntasks;++i){ + S_t* p = new S_t; + p->t = i; + p->f = p->t*1.0; + ff_send_out_to(p,0); + } + return EOS; + } + long ntasks; +}; +struct Sink: ff_minode_t { + S_t* svc(S_t* in) { + printf("in->t=%d from %ld\n", in->t, get_channel_id()); + return GO_ON; + } +}; + +struct WL: ff_node_t { + S_t* svc(S_t* in) { return in; } +}; +struct WLoutH: ff_monode_t { + S_t* svc(S_t* in) { return in; } +}; +struct WL1: ff_monode_t { + S_t* svc(S_t* in) { return in; } +}; +struct WL11inH: ff_minode_t { + S_t* svc(S_t* in) { return in; } +}; +struct WL11: ff_monode_t { + S_t* svc(S_t* in) { return in; } +}; + +struct WR: ff_minode_t { + S_t* svc(S_t* in) { return in; } +}; +struct WRoutH: ff_monode_t { + S_t* svc(S_t* in) { return in; } +}; +struct WR1: ff_minode_t { + S_t* svc(S_t* in) { return in; } +}; +struct WR11: ff_node_t { + S_t* svc(S_t* in) { return in; } +}; + + +int main(int argc, char* argv[]) { + std::vector L; + std::vector R; + + ff_pipeline pipeMain; + Source Sc(100); + ff_a2a a2aCentral; + Sink Sk; + + + // --- these are the edge nodes of the graph ----- + std::vector edgesWL_in; + edgesWL_in.push_back(new WL); + edgesWL_in.push_back(new WL); + auto edgesWL_out = new ff_comb_t(new WL11inH, new WL11);; + + auto edgesWL_inout = new ff_comb_t(new WL, new WL11);; + + auto edgesWR_inout = new ff_comb_t(new WR, new WRoutH); + std::vector*> edgesWR_in; + edgesWR_in.push_back(new ff_comb_t(new WR, new WRoutH)); + edgesWR_in.push_back(new ff_comb_t(new WR, new WRoutH)); + + WR11* edgesWR_out = new WR11; + // ---------------------------------------------- + + ff_pipeline LeftUp; + ff_a2a a2a_LeftUp; + ff_pipeline pipe1; + pipe1.add_stage(edgesWL_in[0]); + pipe1.add_stage(new WLoutH); + + ff_pipeline pipe2; + pipe2.add_stage(edgesWL_in[1]); + pipe2.add_stage(new WLoutH); + + L.push_back(&pipe1); + L.push_back(&pipe2); + + R.push_back(edgesWL_out); + a2a_LeftUp.add_firstset(L); + a2a_LeftUp.add_secondset(R); + LeftUp.add_stage(&a2a_LeftUp); + + ff_pipeline LeftDown; + LeftDown.add_stage(edgesWL_inout); + + // -------------------------- + L.clear(); + R.clear(); + + ff_pipeline RightUp; + RightUp.add_stage(edgesWR_inout); + + + ff_pipeline RightDown; + ff_a2a a2a_RightDown; + L.push_back(edgesWR_in[0]); + L.push_back(edgesWR_in[1]); + ff_pipeline pipe3; + pipe3.add_stage(new WR1); + pipe3.add_stage(edgesWR_out); + a2a_RightDown.add_firstset(L); + R.push_back(&pipe3); + a2a_RightDown.add_secondset(R); + RightDown.add_stage(&a2a_RightDown); + + + // ------------------ + L.clear(); + R.clear(); + L.push_back(&LeftUp); + L.push_back(&LeftDown); + + R.push_back(&RightUp); + R.push_back(&RightDown); + a2aCentral.add_firstset(L); + a2aCentral.add_secondset(R); + + pipeMain.add_stage(&Sc); + pipeMain.add_stage(&a2aCentral); + pipeMain.add_stage(&Sk); + + std::cout << "Num Threads= " << pipeMain.numThreads() << "\n"; + pipeMain.run_and_wait_end(); + + return 0; +} + + + diff --git a/tests/test_staticallocator.cpp b/tests/test_staticallocator.cpp index ba792e9c..b5f0c334 100644 --- a/tests/test_staticallocator.cpp +++ b/tests/test_staticallocator.cpp @@ -25,6 +25,13 @@ **************************************************************************** */ +/* + * + * Source ---> FlatMap ---> Map ---> Sink + * + * + * + */ #include #include @@ -39,7 +46,7 @@ struct S_t { float f; }; -long qlen = 2; +long qlen = 1; long howmany = 10; long ntasks = 1000; @@ -72,6 +79,7 @@ struct FlatMap: ff_monode_t { *p = *in; ff_send_out(p); } + StaticAllocator::dealloc(in); return GO_ON; } StaticAllocator *SAlloc = nullptr; @@ -94,7 +102,8 @@ struct Sink: ff_minode_t { std::lock_guard lck (mtx); std::cout << "Sink ERROR " << in->t << ", " << in->f << " cnt = " << cnt << "\n"; abort(); - } + } + StaticAllocator::dealloc(in); if (cnt2 == howmany) { cnt++; cnt2= 0; @@ -113,13 +122,12 @@ int main(int argc, char* argv[]) { Map M; Sink Sk; - StaticAllocator* SourceAlloc = new StaticAllocator(qlen + 2, sizeof(S_t)); StaticAllocator* FlatMapAlloc = new StaticAllocator(qlen*2 + 3, sizeof(S_t)); Sc.SAlloc = SourceAlloc; FM.SAlloc = FlatMapAlloc; - + ff_pipeline pipeMain(false, qlen, qlen, true); pipeMain.add_stage(&Sc); diff --git a/tests/test_staticallocator2.cpp b/tests/test_staticallocator2.cpp new file mode 100644 index 00000000..463f0b17 --- /dev/null +++ b/tests/test_staticallocator2.cpp @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + +/* + * + * |---> FlatMap-->| + * Source-->| |---> Map-->|---> Sink + * |---> FlatMap-->| | + * Source-->| |---> Map-->| + * |---> FlatMap-->| | + * Source-->| |---> Map-->|---> Sink + * |---> FlatMap-->| + * /<----- a2a ----->/ + * /<--------------- a2a -------------->/ + * /<------------------- a2a ------------------->/ + * + * Source and FlatMap produce more outputs for a single input, + * the data items flowing into the output streams are allocated + * using the StaticAllocator (one for each Source and FlatMap). + * + */ + +#include +#include +#include +#include +#include +#include + +using namespace ff; + +struct S_t { + int t; + float f; +}; + +static long qlen = 1; +static long howmany = 11; +static long ntasks = 100; +static std::mutex mtx; // used only for pretty printing + +struct Source: ff_monode_t { + Source(long ntasks, StaticAllocator* SAlloc): + ntasks(ntasks), SAlloc(SAlloc) {} + + S_t* svc(S_t*) { + long start = get_my_id()*ntasks; + for (long i=1;i<=ntasks;++i){ + S_t* p; + int ch = (i-1) % get_num_outchannels(); + SAlloc->alloc(p, ch); + p->t = start+i; + p->f = p->t*1.0; + ff_send_out_to(p, ch); // NOTE: here we cannot use ff_send_out or return p + } + return EOS; + } + + long ntasks; + StaticAllocator* SAlloc=nullptr; +}; +struct FlatMap: ff_monode_t { + FlatMap(StaticAllocator* SAlloc): SAlloc(SAlloc) { + } + S_t* svc(S_t* in) { + for(int i=0;ialloc(p,ch); + *p = *in; + ff_send_out_to(p,ch); // NOTE: here we cannot use ff_send_out or return p + } + StaticAllocator::dealloc(in); + return GO_ON; + } + StaticAllocator* SAlloc=nullptr; +}; + +struct Map: ff_monode_t { + S_t* svc(S_t* in) { + return in; + } +}; +struct miHelperM: ff_minode_t { + S_t* svc(S_t* in) { return in; } +}; +struct miHelperFM: ff_minode_t { + S_t* svc(S_t* in) { return in; } +}; + +struct Sink: ff_minode_t { + S_t* svc(S_t* in) { + ++M[in->t]; + { + std::lock_guard lck (mtx); + std::cout << "Sink" << get_my_id() << " received " << in->t << ", " << in->f << "\n"; + } + StaticAllocator::dealloc(in); + return GO_ON; + } + std::map M; +}; + + +int main(int argc, char* argv[]) { + const int nSink = 2; + const int nFlatMap = 2; + const int nMap = 1; + const int nSource = 5; + + std::vector Sk(nSink); + std::vector SA(nFlatMap+nSource); + std::vector L; + std::vector R; + + ff_a2a _1(false, qlen, qlen, true); + for(int i=0;iM[i]; + if (sum != howmany) { + std::cerr << "ERROR: i= " << i << " sum= " << sum << " howmany= " << howmany << "\n"; + result_ok = false; + } + } + if (result_ok) + std::cout << "RESULT OK!\n"; + else + std::cout << "WRONG RESULT!\n"; + + for(size_t i=0;i FlatMap-->| + * Source-->| |---> Map-->|---> Sink + * |---> FlatMap-->| | + * Source-->| |---> Map-->| + * |---> FlatMap-->| | + * Source-->| |---> Map-->|---> Sink + * |---> FlatMap-->| + * /<----- a2a ----->/ + * /<--------------- a2a -------------->/ + * /<------------------- a2a ------------------->/ + * + * Source and FlatMap produce more outputs for a single input, + * the data items flowing into the output streams are allocated + * using the StaticAllocator (one for each Source and FlatMap). + * + */ + +#include +#include +#include +#include +#include +#include + +using namespace ff; + +struct S_t { + int t; + float f; +}; + +static long qlen = 2; +static long howmany = 11; +static long ntasks = 100; +static std::mutex mtx; // used only for pretty printing + +struct Source: ff_monode_t { + Source(long ntasks, StaticAllocator* SAlloc): + ntasks(ntasks), SAlloc(SAlloc) {} + + S_t* svc(S_t*) { + long start = get_my_id()*ntasks; + for (long i=1;i<=ntasks;++i){ + S_t* p; + + // NOTE: + // to know which allocator to use, we must use + // the get_next_free_channel to know which will be + // the output channel selected by the ff_send_out + // + int ch = get_next_free_channel(); + + assert(ch>=0 && ch <(int)get_num_outchannels()); + SAlloc->alloc(p, ch); + + p->t = start+i; + p->f = p->t*1.0; + + ff_send_out(p); + } + return EOS; + } + + long ntasks; + StaticAllocator* SAlloc=nullptr; +}; +struct FlatMap: ff_monode_t { + FlatMap(StaticAllocator* SAlloc): SAlloc(SAlloc) { + } + S_t* svc(S_t* in) { + for(int i=0;i=0 && ch <(int)get_num_outchannels()); + SAlloc->alloc(p,ch); + + *p = *in; + + ff_send_out(p); + } + StaticAllocator::dealloc(in); + return GO_ON; + } + StaticAllocator* SAlloc=nullptr; +}; + +struct Map: ff_monode_t { + S_t* svc(S_t* in) { return in; } +}; + + +struct miHelper: ff_minode_t { + S_t* svc(S_t* in) { return in; } +}; + +struct Sink: ff_minode_t { + S_t* svc(S_t* in) { + if ((get_my_id()%2) == 0) usleep(2000); // even Sinks are bottleneck + ++M[in->t]; + { + std::lock_guard lck (mtx); + std::cout << "Sink" << get_my_id() << " received " << in->t << ", " << in->f << "\n"; + } + StaticAllocator::dealloc(in); + return GO_ON; + } + + std::map M; + StaticAllocator* SAlloc=nullptr; +}; + + +int main() { + const int nSink = 3; + const int nFlatMap = 11; + const int nMap = 3; + const int nSource = 5; + + std::vector S(nSink); + + std::vector L; + std::vector R; + + ff_a2a _1(false, qlen, qlen, true); + for(int i=0;iM[i]; + if (sum != howmany) { + std::cerr << "ERROR: wrong result!\n"; + return -1; + } + } + std::cout << "RESULT OK!\n"; + return 0; +} + + + From a1bc9ce440af628388857f978fedd74123d08f6f Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 10 Dec 2020 07:25:53 +0100 Subject: [PATCH 013/202] fixed a problem related to the last commit --- ff/combine.hpp | 3 ++- ff/node.hpp | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ff/combine.hpp b/ff/combine.hpp index e2f8f3f4..d105be83 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -593,7 +593,8 @@ class ff_comb: public ff_minode { // then the comb node and the first node share the same // cond variable. This is due to the put_done method in the lb // (i.e. the prev node is a multi-output or an emitter node) - n->cons_c = c; + assert(n->cons_m == nullptr); + n->cons_c = c; n->cons_m = nullptr; return true; } // producer diff --git a/ff/node.hpp b/ff/node.hpp index 224b3d13..efe26f57 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -893,16 +893,16 @@ class ff_node { if (in && myinbuffer) delete in; if (out && myoutbuffer) delete out; if (thread && my_own_thread) delete reinterpret_cast(thread); + if (cons_c && cons_m) { + pthread_cond_destroy(cons_c); + free(cons_c); + cons_c = nullptr; + } if (cons_m) { pthread_mutex_destroy(cons_m); free(cons_m); cons_m = nullptr; } - if (cons_c) { - pthread_cond_destroy(cons_c); - free(cons_c); - cons_c = nullptr; - } if (prod_m) { pthread_mutex_destroy(prod_m); free(prod_m); From 0c4e25872c0e94dfa7047b332a04023d63d1553a Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sun, 13 Dec 2020 12:18:43 +0100 Subject: [PATCH 014/202] fixed bug in the parallel_for when the 'grain' parameter is less than zero --- ff/parallel_for_internals.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ff/parallel_for_internals.hpp b/ff/parallel_for_internals.hpp index f5bb6034..37b53f04 100644 --- a/ff/parallel_for_internals.hpp +++ b/ff/parallel_for_internals.hpp @@ -507,7 +507,8 @@ class forall_Scheduler: public ff_node { virtual inline size_t init_data_static(long start, long stop) { assert(_chunk <= 0); static_scheduling = true; // this forces static scheduling in the nextTaskConcurrent - + skip1=false,jump=0,maxid=-1; + if (_chunk == 0) { // default static scheduling, i.e. the iteration space is almost equally divided // in contiguous chunks among threads @@ -517,7 +518,6 @@ class forall_Scheduler: public ff_node { _chunk = numtasks / long(_nw); data.resize(_nw); taskv.resize(_nw);eossent.resize(_nw); - skip1=false,jump=0,maxid=-1; long end, e; for(size_t i=0; totalnumtasks>0; ++i,--totalnumtasks) { @@ -810,6 +810,7 @@ class forall_Scheduler: public ff_node { inline void setloop(long start, long stop, long step, long chunk, size_t nw) { _start=start, _stop=stop, _step=step, _chunk=chunk, _nw=nw; + #ifdef FF_PARFOR_PASSIVE_NOSTEALING _nextIteration = _start; #endif From bb1516b0685261f673ed79771dbb53d3cedd64c9 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 2 Feb 2021 08:00:58 +0100 Subject: [PATCH 015/202] Improved tests for the staticallocator, new ff_comb_t interface for the combine building block, normalized input/output buffer capacity (bounded capacity) --- ff/all2all.hpp | 7 +- ff/combine.hpp | 152 +++++++++++++++++------ ff/config.hpp | 3 - ff/farm.hpp | 40 +++--- ff/node.hpp | 2 + ff/pipeline.hpp | 8 +- tests/test_staticallocator.cpp | 5 +- tests/test_staticallocator2.cpp | 15 ++- tests/test_staticallocator3.cpp | 208 ++++++++++++++++++++++++++------ 9 files changed, 325 insertions(+), 115 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 5e9aa01e..a0ff90f8 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -251,9 +251,6 @@ class ff_a2a: public ff_node { public: - enum { DEF_IN_BUFF_ENTRIES=DEFAULT_BUFFER_CAPACITY, - DEF_IN_OUT_DIFF=DEFAULT_IN_OUT_CAPACITY_DIFFERENCE, - DEF_OUT_BUFF_ENTRIES=(DEF_IN_BUFF_ENTRIES+DEF_IN_OUT_DIFF)}; /** * @@ -262,8 +259,8 @@ class ff_a2a: public ff_node { * */ ff_a2a(bool reduce_channels=false, - int in_buffer_entries=DEF_IN_BUFF_ENTRIES, - int out_buffer_entries=DEF_OUT_BUFF_ENTRIES, + int in_buffer_entries=DEFAULT_BUFFER_CAPACITY, + int out_buffer_entries=DEFAULT_BUFFER_CAPACITY, bool fixedsize=FF_FIXED_SIZE):prepared(false),fixedsize(fixedsize), reduce_channels(reduce_channels), in_buffer_entries(in_buffer_entries), diff --git a/ff/combine.hpp b/ff/combine.hpp index d105be83..27a7a6ba 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -202,6 +202,79 @@ class ff_comb: public ff_minode { return ((ff_comb*)comp_nodes[1])->getLast(); return comp_nodes[1]; } + + // returns the pointer to the "replaced" node + ff_node* replace_first(ff_node* n, bool cleanup=false, bool remove_from_cleanup=true) { + if (comp_nodes[0]->isComp()) return nullptr; + ff_node* first = comp_nodes[0]; + comp_nodes[0] = n; + + if (remove_from_cleanup) { + ssize_t pos=-1; + for(size_t i=0;i=0) + cleanup_stages.erase(cleanup_stages.begin()+pos); + } + if (cleanup) + cleanup_stages.push_back(n); + return first; + } + + // returns the pointer to the "replaced" node + ff_node* replace_last(ff_node* n, bool cleanup=false, bool remove_from_cleanup=true) { + if (comp_nodes[1]->isComp()) return nullptr; + ff_node* last = comp_nodes[1]; + comp_nodes[1] = n; + + if (remove_from_cleanup) { + ssize_t pos=-1; + for(size_t i=0;i=0) + cleanup_stages.erase(cleanup_stages.begin()+pos); + } + if (cleanup) + cleanup_stages.push_back(n); + return last; + } + + // returns true if the "replaced" node has been deleted (it was added with cleanup=true) + template + bool changeFirst(T* n, bool cleanup=false) { + bool r=false; + ff_comb* c = getFirstComb(); + ff_node* first = getFirst(); + + ssize_t pos=-1; + for(size_t i=0;i=0) { + cleanup_stages.erase(cleanup_stages.begin()+pos); + r = true; + } + c->replace_first(n, cleanup, false); + if (r) delete first; + return r; + } + // returns true if the "replaced" node has been deleted (it was added with cleanup=true) + template + bool changeLast(T* n, bool cleanup=false) { + bool r=false; + ff_comb* c = getLastComb(); + ff_node* last = getLast(); + + ssize_t pos=-1; + for(size_t i=0;i=0) { + cleanup_stages.erase(cleanup_stages.begin()+pos); + r = true; + } + c->replace_last(n, cleanup, false); + if (r) delete last; + return r; + } double ffTime() { return diffmsec(getstoptime(),getstarttime()); @@ -294,7 +367,18 @@ class ff_comb: public ff_minode { bool put(void * ptr) { return ff_node::put(ptr); } - + + ff_comb* getFirstComb() { + if (comp_nodes[0]->isComp()) + return ((ff_comb*)comp_nodes[0])->getFirstComb(); + return this; + } + ff_comb* getLastComb() { + if (comp_nodes[1]->isComp()) + return ((ff_comb*)comp_nodes[1])->getLastComb(); + return this; + } + void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { comp_nodes[1]->registerCallback(cb,arg); } @@ -669,49 +753,49 @@ struct ff_comb_t: ff_comb { typedef T T_t; typedef TOUT OUT_t; - ff_comb_t(ff_node_t* n1, ff_node_t* n2): - ff_comb(n1,n2,false,false) {} - ff_comb_t(ff_node_t* n1, ff_minode_t* n2): - ff_comb(n1,n2,false,false) {} - ff_comb_t(ff_node_t* n1, ff_monode_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_node_t* n1, ff_node_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} + ff_comb_t(ff_node_t* n1, ff_minode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} + ff_comb_t(ff_node_t* n1, ff_monode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} template - ff_comb_t(ff_node_t* n1, ff_comb_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_node_t* n1, ff_comb_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} - ff_comb_t(ff_minode_t* n1, ff_node_t* n2): - ff_comb(n1,n2,false,false) {} - ff_comb_t(ff_minode_t* n1, ff_minode_t* n2): - ff_comb(n1,n2,false,false) {} - ff_comb_t(ff_minode_t* n1, ff_monode_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_minode_t* n1, ff_node_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} + ff_comb_t(ff_minode_t* n1, ff_minode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} + ff_comb_t(ff_minode_t* n1, ff_monode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} template - ff_comb_t(ff_minode_t* n1, ff_comb_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_minode_t* n1, ff_comb_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} - ff_comb_t(ff_monode_t* n1, ff_node_t* n2): - ff_comb(n1,n2,false,false) {} - ff_comb_t(ff_monode_t* n1, ff_minode_t* n2): - ff_comb(n1,n2,false,false) {} - ff_comb_t(ff_monode_t* n1, ff_monode_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_monode_t* n1, ff_node_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} + ff_comb_t(ff_monode_t* n1, ff_minode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} + ff_comb_t(ff_monode_t* n1, ff_monode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} template - ff_comb_t(ff_monode_t* n1, ff_comb_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_monode_t* n1, ff_comb_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} template - ff_comb_t(ff_comb_t* n1, ff_node_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_comb_t* n1, ff_node_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} template - ff_comb_t(ff_comb_t* n1, ff_minode_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_comb_t* n1, ff_minode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} template - ff_comb_t(ff_comb_t* n1, ff_monode_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_comb_t* n1, ff_monode_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} template - ff_comb_t(ff_comb_t* n1, ff_comb_t* n2): - ff_comb(n1,n2,false,false) {} + ff_comb_t(ff_comb_t* n1, ff_comb_t* n2, bool cleanup1=false, bool cleanup2=false): + ff_comb(n1,n2,cleanup1,cleanup2) {} }; @@ -1075,7 +1159,7 @@ static inline const ff_pipeline combine_ofarm_farm(ff_farm& farm1, ff_farm& farm ordered_lb* _lb= new ordered_lb(farm1.getNWorkers()); assert(_lb); - const size_t memsize = farm1.getNWorkers() * (2*newfarm1.ondemand_buffer()+ff_farm::DEF_IN_OUT_DIFF+3)+ DEF_OFARM_ONDEMAND_MEMORY; + const size_t memsize = farm1.getNWorkers() * (2*newfarm1.ondemand_buffer()+3)+ DEF_OFARM_ONDEMAND_MEMORY; newfarm1.ordered_resize_memory(memsize); _lb->init(newfarm1.ordered_get_memory(), memsize); newfarm1.setlb(_lb, true); diff --git a/ff/config.hpp b/ff/config.hpp index 02e1a361..b558cb07 100644 --- a/ff/config.hpp +++ b/ff/config.hpp @@ -123,9 +123,6 @@ #if !defined(DEFAULT_BUFFER_CAPACITY) #define DEFAULT_BUFFER_CAPACITY 2048 #endif -#if !defined(DEFAULT_IN_OUT_CAPACITY_DIFFERENCE) -#define DEFAULT_IN_OUT_CAPACITY_DIFFERENCE 128 -#endif /* To save energy and improve hyperthreading performance diff --git a/ff/farm.hpp b/ff/farm.hpp index a1508639..16334982 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -245,7 +245,7 @@ class ff_farm: public ff_node { ordered_lb* _lb= new ordered_lb(nworkers); ordered_gt* _gt= new ordered_gt(nworkers); assert(_lb); assert(_gt); - ordering_Memory.resize(nworkers * (2*ff_farm::ondemand_buffer()+DEF_IN_OUT_DIFF+3)+ordering_memsize); + ordering_Memory.resize(nworkers * (2*ff_farm::ondemand_buffer()+3)+ordering_memsize); _lb->init(ordering_Memory.begin(), ordering_Memory.size()); _gt->init(ordering_memsize); setlb(_lb, true); @@ -311,7 +311,7 @@ class ff_farm: public ff_node { - if (a2a_first->create_input_buffer((int) (ondemand ? ondemand: (in_buffer_entries/nworkers + 1)), + if (a2a_first->create_input_buffer((int) (ondemand ? ondemand: in_buffer_entries), (ondemand ? true: fixedsize))<0) return -1; const svector& W1 = a2a_first->getFirstSet(); @@ -319,7 +319,7 @@ class ff_farm: public ff_node { lb->register_worker(W1[i]); } } else { - if (workers[i]->create_input_buffer((int) (ondemand ? ondemand: (in_buffer_entries/nworkers + 1)), + if (workers[i]->create_input_buffer((int) (ondemand ? ondemand: in_buffer_entries), (ondemand ? true: fixedsize))<0) return -1; lb->register_worker(workers[i]); @@ -346,8 +346,7 @@ class ff_farm: public ff_node { // NOTE: the following call might fail because the buffers were already created for example by // the pipeline that contains this stage - a2a_last->create_output_buffer((int) (out_buffer_entries/nworkers + DEF_IN_OUT_DIFF), - (lb->masterworker()?false:fixedsize)); + a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize)); for(size_t i=0;i w(1); @@ -369,8 +368,7 @@ class ff_farm: public ff_node { } else { // NOTE: the following call might fail because the buffers were already created for example by // the pipeline that contains this stage - if (a2a_last->create_output_buffer((int) (out_buffer_entries/nworkers + DEF_IN_OUT_DIFF), - (lb->masterworker()?false:fixedsize))<0) { + if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize))<0) { if (lb->masterworker()) return -1; // something went wrong } if (lb->masterworker()) { @@ -440,8 +438,7 @@ class ff_farm: public ff_node { gt->register_worker(t); } } else { // standard worker or composition where the second stage is not multi-output - if (workers[i]->create_output_buffer((int) (out_buffer_entries/nworkers + DEF_IN_OUT_DIFF), - (lb->masterworker()?false:fixedsize))<0) + if (workers[i]->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize))<0) return -1; assert(!lb->masterworker()); gt->register_worker(workers[i]); @@ -712,9 +709,6 @@ class ff_farm: public ff_node { virtual inline pthread_cond_t &get_cons_c() { return *(lb->cons_c);} public: - enum { DEF_IN_BUFF_ENTRIES=DEFAULT_BUFFER_CAPACITY, - DEF_IN_OUT_DIFF=DEFAULT_IN_OUT_CAPACITY_DIFFERENCE, - DEF_OUT_BUFF_ENTRIES=(DEF_IN_BUFF_ENTRIES+DEF_IN_OUT_DIFF)}; using lb_t = ff_loadbalancer; using gt_t = ff_gatherer; @@ -735,8 +729,8 @@ class ff_farm: public ff_node { has_input_channel(input_ch),collector_removed(false),ordered(false),fixedsize(FF_FIXED_SIZE), myownlb(true),myowngt(true),worker_cleanup(false),emitter_cleanup(false), collector_cleanup(false),ondemand(0), - in_buffer_entries(DEF_IN_BUFF_ENTRIES), - out_buffer_entries(DEF_OUT_BUFF_ENTRIES), + in_buffer_entries(DEFAULT_BUFFER_CAPACITY), + out_buffer_entries(DEFAULT_BUFFER_CAPACITY), max_nworkers(DEF_MAX_NUM_WORKERS),ordering_memsize(0), emitter(NULL),collector(NULL), lb(new lb_t(max_nworkers)),gt(new gt_t(max_nworkers)), @@ -776,8 +770,8 @@ class ff_farm: public ff_node { * \param fixedsize = true uses only fixed size queue */ explicit ff_farm(bool input_ch=false, - int in_buffer_entries=DEF_IN_BUFF_ENTRIES, - int out_buffer_entries=DEF_OUT_BUFF_ENTRIES, + int in_buffer_entries=DEFAULT_BUFFER_CAPACITY, + int out_buffer_entries=DEFAULT_BUFFER_CAPACITY, bool worker_cleanup=false, // NOTE: by default no cleanup at exit is done ! size_t max_num_workers=DEF_MAX_NUM_WORKERS, bool fixedsize=FF_FIXED_SIZE): @@ -1921,7 +1915,7 @@ class ff_farm: public ff_node { // have to be registered as output channels for the worker. for(size_t i=0;icreate_output_buffer((int) (out_buffer_entries/nworkers + DEF_IN_OUT_DIFF),fixedsize)<0) return -1; + if (workers[i]->create_output_buffer(out_buffer_entries,fixedsize)<0) return -1; } } return 0; @@ -2081,7 +2075,7 @@ class ff_Farm: public ff_farm { std::unique_ptr E =std::unique_ptr(nullptr), std::unique_ptr C =std::unique_ptr(nullptr), bool input_ch=false): - ff_farm(input_ch,DEF_IN_BUFF_ENTRIES, DEF_OUT_BUFF_ENTRIES,false), + ff_farm(input_ch,DEFAULT_BUFFER_CAPACITY,DEFAULT_BUFFER_CAPACITY,false), Workers(std::move(W)), Emitter(std::move(E)), Collector(std::move(C)) { const size_t nw = Workers.size(); @@ -2100,7 +2094,7 @@ class ff_Farm: public ff_farm { ff_Farm(std::vector > &&W, ff_node &E, ff_node &C, bool input_ch=false): - ff_farm(input_ch,DEF_IN_BUFF_ENTRIES, DEF_OUT_BUFF_ENTRIES,false), + ff_farm(input_ch,DEFAULT_BUFFER_CAPACITY,DEFAULT_BUFFER_CAPACITY,false), Workers(std::move(W)) { const size_t nw = Workers.size(); @@ -2114,7 +2108,7 @@ class ff_Farm: public ff_farm { } ff_Farm(std::vector > &&W, ff_node &E, bool input_ch=false): - ff_farm(input_ch,DEF_IN_BUFF_ENTRIES, DEF_OUT_BUFF_ENTRIES,false), + ff_farm(input_ch,DEFAULT_BUFFER_CAPACITY, DEFAULT_BUFFER_CAPACITY,false), Workers(std::move(W)) { const size_t nw = Workers.size(); @@ -2154,7 +2148,7 @@ class ff_Farm: public ff_farm { template explicit ff_Farm(FUNC_t F, ssize_t nw, bool input_ch=false): - ff_farm(input_ch,DEF_IN_BUFF_ENTRIES, DEF_OUT_BUFF_ENTRIES, + ff_farm(input_ch,DEFAULT_BUFFER_CAPACITY,DEFAULT_BUFFER_CAPACITY, true, nw) { std::vector w(nw); @@ -2222,7 +2216,7 @@ class ff_OFarm: public ff_farm { typedef OUT_t out_type; ff_OFarm(std::vector > &&W, bool input_ch=false): - ff_farm(input_ch,DEF_IN_BUFF_ENTRIES, DEF_OUT_BUFF_ENTRIES,false,W.size()), + ff_farm(input_ch, DEFAULT_BUFFER_CAPACITY, DEFAULT_BUFFER_CAPACITY,false,W.size()), Workers(std::move(W)) { assert(Workers.size()); const size_t nw = Workers.size(); @@ -2236,7 +2230,7 @@ class ff_OFarm: public ff_farm { template explicit ff_OFarm(FUNC_t F, size_t nw, bool input_ch=false): - ff_farm(input_ch,DEF_IN_BUFF_ENTRIES, DEF_OUT_BUFF_ENTRIES,false,nw) { + ff_farm(input_ch,DEFAULT_BUFFER_CAPACITY,DEFAULT_BUFFER_CAPACITY,false,nw) { if (Workers.size()>0) { error("OFARM: workers already added\n"); return; diff --git a/ff/node.hpp b/ff/node.hpp index efe26f57..9e112a9f 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -660,6 +660,7 @@ class ff_node { */ virtual int create_input_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { if (in) return -1; + if (nentries<=0) return -1; in = new FFBUFFER(nentries,fixedsize); if (!in) return -1; myinbuffer=true; @@ -686,6 +687,7 @@ class ff_node { */ virtual int create_output_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { if (out) return -1; + if (nentries<=0) return -1; out = new FFBUFFER(nentries,fixedsize); if (!out) return -1; myoutbuffer=true; diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 1ff20512..2308253a 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -509,10 +509,6 @@ class ff_pipeline: public ff_node { } public: - - enum { DEF_IN_BUFF_ENTRIES=DEFAULT_BUFFER_CAPACITY, - DEF_IN_OUT_DIFF=DEFAULT_IN_OUT_CAPACITY_DIFFERENCE, - DEF_OUT_BUFF_ENTRIES=(DEF_IN_BUFF_ENTRIES+DEF_IN_OUT_DIFF)}; /** * \brief Constructor @@ -523,8 +519,8 @@ class ff_pipeline: public ff_node { * \param fixedsize \p true uses bound channels (SPSC queue) */ explicit ff_pipeline(bool input_ch=false, - int in_buffer_entries=DEF_IN_BUFF_ENTRIES, - int out_buffer_entries=DEF_OUT_BUFF_ENTRIES, + int in_buffer_entries=DEFAULT_BUFFER_CAPACITY, + int out_buffer_entries=DEFAULT_BUFFER_CAPACITY, bool fixedsize=FF_FIXED_SIZE): has_input_channel(input_ch), node_cleanup(false),fixedsize(fixedsize), diff --git a/tests/test_staticallocator.cpp b/tests/test_staticallocator.cpp index b5f0c334..afdc25f7 100644 --- a/tests/test_staticallocator.cpp +++ b/tests/test_staticallocator.cpp @@ -122,8 +122,9 @@ int main(int argc, char* argv[]) { Map M; Sink Sk; - StaticAllocator* SourceAlloc = new StaticAllocator(qlen + 2, sizeof(S_t)); - StaticAllocator* FlatMapAlloc = new StaticAllocator(qlen*2 + 3, sizeof(S_t)); + // NOTE: for each queue we have +2 slots + StaticAllocator* SourceAlloc = new StaticAllocator(qlen+2, sizeof(S_t)); + StaticAllocator* FlatMapAlloc = new StaticAllocator((qlen+2)*2, sizeof(S_t)); Sc.SAlloc = SourceAlloc; FM.SAlloc = FlatMapAlloc; diff --git a/tests/test_staticallocator2.cpp b/tests/test_staticallocator2.cpp index 463f0b17..c9ab6aec 100644 --- a/tests/test_staticallocator2.cpp +++ b/tests/test_staticallocator2.cpp @@ -59,8 +59,8 @@ struct S_t { }; static long qlen = 1; -static long howmany = 11; -static long ntasks = 100; +static long howmany = 7; +static long ntasks = 1000; static std::mutex mtx; // used only for pretty printing struct Source: ff_monode_t { @@ -75,7 +75,9 @@ struct Source: ff_monode_t { SAlloc->alloc(p, ch); p->t = start+i; p->f = p->t*1.0; - ff_send_out_to(p, ch); // NOTE: here we cannot use ff_send_out or return p + // NOTE: here we cannot use ff_send_out or return p + // see also test_staticallocator3.cpp + ff_send_out_to(p, ch); } return EOS; } @@ -153,7 +155,9 @@ int main(int argc, char* argv[]) { ff_a2a _2(false, qlen, qlen, true); for (int i=0;i { @@ -72,17 +79,19 @@ struct Source: ff_monode_t { long start = get_my_id()*ntasks; for (long i=1;i<=ntasks;++i){ S_t* p; - - // NOTE: - // to know which allocator to use, we must use - // the get_next_free_channel to know which will be - // the output channel selected by the ff_send_out - // - int ch = get_next_free_channel(); - - assert(ch>=0 && ch <(int)get_num_outchannels()); - SAlloc->alloc(p, ch); - + if (enablestd) { + p = new S_t; + assert(p); + } else { + // NOTE: + // to know which allocator to use, we must use + // the get_next_free_channel to know which will be + // the output channel selected by the ff_send_out + // + int ch = get_next_free_channel(); + assert(ch>=0 && ch <(int)get_num_outchannels()); + SAlloc->alloc(p, ch); + } p->t = start+i; p->f = p->t*1.0; @@ -100,15 +109,21 @@ struct FlatMap: ff_monode_t { S_t* svc(S_t* in) { for(int i=0;i=0 && ch <(int)get_num_outchannels()); - SAlloc->alloc(p,ch); - + if (enablestd) { + p = new S_t; + assert(p); + } else { + int ch = get_next_free_channel(); + assert(ch>=0 && ch <(int)get_num_outchannels()); + SAlloc->alloc(p,ch); + } + *p = *in; - ff_send_out(p); } - StaticAllocator::dealloc(in); + if (enablestd) delete in; + else + StaticAllocator::dealloc(in); return GO_ON; } StaticAllocator* SAlloc=nullptr; @@ -125,13 +140,15 @@ struct miHelper: ff_minode_t { struct Sink: ff_minode_t { S_t* svc(S_t* in) { - if ((get_my_id()%2) == 0) usleep(2000); // even Sinks are bottleneck - ++M[in->t]; - { - std::lock_guard lck (mtx); - std::cout << "Sink" << get_my_id() << " received " << in->t << ", " << in->f << "\n"; - } - StaticAllocator::dealloc(in); + if (sinksleep && (get_my_id()%2) == 0) usleep(3000); // Sinks with even id are bottleneck + ++M[in->t]; + // { + // std::lock_guard lck (mtx); + // std::cout << "Sink" << get_my_id() << " received " << in->t << ", " << in->f << "\n"; + // } + if (enablestd) delete in; + else + StaticAllocator::dealloc(in); return GO_ON; } @@ -140,14 +157,121 @@ struct Sink: ff_minode_t { }; -int main() { - const int nSink = 3; - const int nFlatMap = 11; - const int nMap = 3; - const int nSource = 5; +static inline void usage(const char *argv0) { + std::cout << "--------------------\n"; + std::cout << "Usage: " << argv0 << " [options]\n"; + std::cout << "\nOptions:\n"; + std::cout << " -s number of source nodes (default " << nSource << ")\n"; + std::cout << " -f number of flat map nodes (default " << nFlatMap << ")\n"; + std::cout << " -m number of map nodes (default " << nMap << ")\n"; + std::cout << " -k number of sink nodes (default " << nSink << ")\n"; + std::cout << " -n number of tasks generated by each source (default " << ntasks << ")\n"; + std::cout << " -q set the queue length (default " << qlen << ")\n"; + std::cout << " -w set how many tasks generate each flat map for one input (default " << howmany << ")\n"; + std::cout << " -a enable the standard allocator (new/delete) (default disabled)\n"; + std::cout << " -h print this message\n"; + std::cout << " -u enable sleeping for even sinks (default disabled)\n"; + std::cout << "--------------------\n\n"; +} +static bool isNumber(const char* s, long &n) { + try { + size_t e; + n=std::stol(s, &e, 10); + return e == strlen(s); + } catch (const std::invalid_argument&) { + return false; + } catch (const std::out_of_range&) { + return false; + } +} +int parseCommandLine(int argc, char *argv[]) { + extern char *optarg; + const std::string optstr="s:m:f:k:n:q:w:u:ah"; + long opt, _s=nSource, _m=nMap, _f=nFlatMap, _k=nSink, _n=ntasks, _q=qlen, _w=howmany; + while((opt = getopt(argc, argv, optstr.c_str())) != -1) { + switch(opt) { + case 's': { + if (!isNumber(optarg, _s)) { + std::cerr << "Error: wrong '-s' option\n"; + usage(argv[0]); + return -1; + } + } break; + case 'm': { + if (!isNumber(optarg, _m)) { + std::cerr << "Error: wrong '-m' option\n"; + usage(argv[0]); + return -1; + } + } break; + case 'f': { + if (!isNumber(optarg, _f)) { + std::cerr << "Error: wrong '-f' option\n"; + usage(argv[0]); + return -1; + } + } break; + case 'k': { + if (!isNumber(optarg, _k)) { + std::cerr << "Error: wrong '-k' option\n"; + usage(argv[0]); + return -1; + } + } break; + case 'n': { + if (!isNumber(optarg, _n)) { + std::cerr << "Error: wrong '-n' option\n"; + usage(argv[0]); + return -1; + } + } break; + case 'q': { + if (!isNumber(optarg, _q)) { + std::cerr << "Error: wrong '-q' option\n"; + usage(argv[0]); + return -1; + } + } break; + case 'w': { + if (!isNumber(optarg, _w)) { + std::cerr << "Error: wrong '-w' option\n"; + usage(argv[0]); + return -1; + } + } break; + case 'a': { enablestd=true; } break; + case 'u': { sinksleep=true; } break; + case 'h': + default: + usage(argv[0]); + return -1; + } + } + if (optind S(nSink); - +int main(int argc, char* argv[]) { + if (argc!=1) { + if (parseCommandLine(argc, argv)<0) return -1; + } + std::cout << "running with the following settings:\n"; + std::cout << " nSource (-s) = " << nSource << "\n"; + std::cout << " nFlatMap (-f) = " << nFlatMap << "\n"; + std::cout << " nMap (-m) = " << nMap << "\n"; + std::cout << " nSink (-k) = " << nSink << "\n"; + std::cout << " ntasks (-n) = " << ntasks << "\n"; + std::cout << " qlen (-q) = " << qlen << "\n"; + std::cout << " howmany (-w) = " << howmany << "\n"; + std::cout << " standard allocator (-a) = " << (enablestd?"enabled":"disabled") << "\n"; + std::cout << " even sinks sleep (-u) = " << (sinksleep?"enabled":"disabled") << "\n\n"; + + std::vector S(nSink); std::vector L; std::vector R; @@ -166,8 +290,13 @@ int main() { R.clear(); ff_a2a _2(false, qlen, qlen, true); - for (int i=0;i Date: Sat, 6 Feb 2021 11:17:53 +0100 Subject: [PATCH 016/202] enahanced the possibility to replace nodes in the graph of building blocks --- ff/all2all.hpp | 122 +++++++++++++++++++++++++---------- ff/combine.hpp | 35 +++++++--- ff/graph_utils.hpp | 63 ++++++++++++++++++ ff/optimize.hpp | 15 ++--- ff/pipeline.hpp | 22 +++++++ tests/Makefile | 2 +- tests/test_changenode.cpp | 132 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 338 insertions(+), 53 deletions(-) create mode 100644 tests/test_changenode.cpp diff --git a/ff/all2all.hpp b/ff/all2all.hpp index a0ff90f8..0d346555 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -134,7 +134,7 @@ class ff_a2a: public ff_node { } // it is a standard node or a pipeline with a standard node as last stage, so we transform it to a multi-output node for(size_t i=0;iget_barrier(); if (bar) mo->set_barrier(bar); workers1[i] = mo; // replacing old node + internalSupportNodes.push_back(mo); } - workers1_to_free = true; } for(size_t i=0;iondemand_buffer()==0)) @@ -159,7 +159,7 @@ class ff_a2a: public ff_node { // here we have to transform the standard node into a multi-input node for(size_t i=0;iget_barrier(); if (bar) mi->set_barrier(bar); workers2[i] = mi; // replacing old node + internalSupportNodes.push_back(mi); } - workers2_to_free = true; } for(size_t i=0;iset_id(int(i)); @@ -269,20 +269,14 @@ class ff_a2a: public ff_node { virtual ~ff_a2a() { if (barrier) delete barrier; - if (workers1_to_free) { - for(size_t i=0;i - int change_firstset(const std::vector& w, int ondemand=0, bool cleanup=false) { + int change_firstset(const std::vector& w, int ondemand=0, bool cleanup=false, bool remove_from_cleanuplist=false) { + if (remove_from_cleanuplist) { + for(size_t j=0;j=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos); + } + } workers1.clear(); return add_firstset(w, ondemand, cleanup); } @@ -329,14 +336,62 @@ class ff_a2a: public ff_node { for(size_t i=0;i - int change_secondset(const std::vector& w, bool cleanup=false) { + int change_secondset(const std::vector& w, bool cleanup=false, bool remove_from_cleanuplist=false) { + if (remove_from_cleanuplist) { + for(size_t j=0;j=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos); + } + } workers2.clear(); return add_secondset(w, cleanup); } + + bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) { + if (prepared) { + error("A2A, change_node cannot be called because the A2A has already been prepared\n"); + return false; + } + for(size_t i=0; i=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos); + } + workers1[i] = n; + if (cleanup) internalSupportNodes.push_back(n); + return true; + } + } + for(size_t i=0; i=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos); + } + workers2[i] = n; + if (cleanup) internalSupportNodes.push_back(n); + return true; + } + } + + return false; + } bool reduceChannel() const { return reduce_channels;} @@ -447,21 +502,20 @@ class ff_a2a: public ff_node { return true; } - const svector& getFirstSet() const { return workers1; } + void remove_from_cleanuplist(const svector& w) { + for(size_t j=0;j=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos); + } + } + + const svector& getFirstSet() const { return workers1; } const svector& getSecondSet() const { return workers2; } - bool isset_cleanup_firstset() const { return workers1_to_free; } - bool isset_cleanup_secondset() const { return workers2_to_free;} - int ondemand_buffer() const { return ondemand_chunk; } - void cleanup_firstset(bool onoff=true) { - workers1_to_free = onoff; - } - void cleanup_secondset(bool onoff=true) { - workers2_to_free = onoff; - } - int numThreads() const { return cardinality(); } int set_output(const svector & w) { @@ -716,8 +770,8 @@ class ff_a2a: public ff_node { } protected: - bool workers1_to_free=false; - bool workers2_to_free=false; + bool workers1_cleanup=false; + bool workers2_cleanup=false; bool prepared, fixedsize,reduce_channels; int in_buffer_entries, out_buffer_entries; int ondemand_chunk=0; diff --git a/ff/combine.hpp b/ff/combine.hpp index 27a7a6ba..5dea0665 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -190,26 +190,32 @@ class ff_comb: public ff_minode { } inline bool isComp() const { return true; } - // returns the first node on the left-hand side + // returns the first sequential node (not comb) on the left-hand side ff_node* getFirst() const { if (comp_nodes[0]->isComp()) return ((ff_comb*)comp_nodes[0])->getFirst(); return comp_nodes[0]; } - // returns the last node on the right-hand side + // returns the last sequential node (not comb) on the right-hand side ff_node* getLast() const { if (comp_nodes[1]->isComp()) return ((ff_comb*)comp_nodes[1])->getLast(); return comp_nodes[1]; } - + ff_node* getLeft() const { + return comp_nodes[0]; + } + ff_node* getRight() const { + return comp_nodes[1]; + } + // returns the pointer to the "replaced" node - ff_node* replace_first(ff_node* n, bool cleanup=false, bool remove_from_cleanup=true) { + ff_node* replace_first(ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=true) { if (comp_nodes[0]->isComp()) return nullptr; ff_node* first = comp_nodes[0]; comp_nodes[0] = n; - if (remove_from_cleanup) { + if (remove_from_cleanuplist) { ssize_t pos=-1; for(size_t i=0;iisComp()) return nullptr; ff_node* last = comp_nodes[1]; comp_nodes[1] = n; - if (remove_from_cleanup) { + if (remove_from_cleanuplist) { ssize_t pos=-1; for(size_t i=0;i @@ -367,12 +381,13 @@ class ff_comb: public ff_minode { bool put(void * ptr) { return ff_node::put(ptr); } - + // returns the innermost combine on the left-hand side ff_comb* getFirstComb() { if (comp_nodes[0]->isComp()) return ((ff_comb*)comp_nodes[0])->getFirstComb(); return this; } + // returns the innermost combine on the right-hand side ff_comb* getLastComb() { if (comp_nodes[1]->isComp()) return ((ff_comb*)comp_nodes[1])->getLastComb(); @@ -429,10 +444,10 @@ class ff_comb: public ff_minode { if (n2->isMultiOutput()) { svector w(1); n2->get_out_nodes(w); - if (w.size()==0) + if ((w.size()==0) && (n2->callback == nullptr)) n2->registerCallback(devnull, nullptr); // devnull callback } else - if (n2->get_out_buffer() == nullptr) + if ((n2->get_out_buffer() == nullptr) && (n2->callback == nullptr)) n2->registerCallback(devnull, nullptr); // devnull callback diff --git a/ff/graph_utils.hpp b/ff/graph_utils.hpp index 15bffd31..362089e2 100644 --- a/ff/graph_utils.hpp +++ b/ff/graph_utils.hpp @@ -95,6 +95,69 @@ static inline ff_node* ispipe_getlast(ff_node* node) { } return nullptr; } +// returns: +// - the innermost BB that contains the node 'n' passed as argument +// - null if the node 'n' is not found +// - the node 'n' if the starting node is 'n' +static inline ff_node* getBB(ff_node* startnode, ff_node* n) { + if (startnode == n) return n; + + if (startnode->isPipe()) { + ff_pipeline* pipe = reinterpret_cast(startnode); + svector Vn = pipe->getStages(); + for(size_t i=0;iisAll2All()) { + ff_a2a* a2a = reinterpret_cast(startnode); + svector L = a2a->getFirstSet(); + svector R = a2a->getSecondSet(); + for(size_t i=0;iisFarm()) { + ff_farm* farm = reinterpret_cast(startnode); + if (farm->getEmitter() == n) return farm; + if (farm->getCollector() == n) return farm; + svector W = farm->getWorkers(); + for(size_t i=0;iisComp()) { + ff_comb* comb = reinterpret_cast(startnode); + ff_node* cl = comb->getLeft(); + ff_node* cr = comb->getRight(); + if (cl->isComp()) { + ff_node* r = getBB(cl,n); + if (r) return ((r==n)?cl:r); + } else { + if (comb->getFirst() == n) return comb; + } + if (cr->isComp()) { + ff_node* r = getBB(cr,n); + if (r) return ((r==n)?cr:r); + } else { + if (comb->getLast() == n) return comb; + } + return nullptr; + } + return nullptr; +} + } // namespace #endif /* FF_GRAPH_UTILS_HPP */ diff --git a/ff/optimize.hpp b/ff/optimize.hpp index 519771a5..4c5f48a2 100644 --- a/ff/optimize.hpp +++ b/ff/optimize.hpp @@ -251,23 +251,23 @@ static inline int combine_right_with_a2a(ff_a2a& a2a, T* node, bool cleanup_node if (combine_with_laststage(*reinterpret_cast(w[0]), node, cleanup_node)<0) return -1; int r=0; for(size_t i=1;iisPipe()); ff_pipeline* pipe = reinterpret_cast(w[i]); r+=combine_with_laststage(*pipe, new T(*node), true); } return (r>0?-1:0); } - bool rset_cleanup = a2a.isset_cleanup_secondset(); std::vector new_secondset; - ff_comb* comb = new ff_comb(w[0], node, rset_cleanup, cleanup_node); + ff_comb* comb = new ff_comb(w[0], node, false, cleanup_node); assert(comb); new_secondset.push_back(comb); for(size_t i=1;i @@ -279,19 +279,18 @@ static inline int combine_left_with_a2a(ff_a2a& a2a, T* node, bool cleanup_node) return -1; int r=0; for(size_t i=1;iisPipe()); ff_pipeline* pipe = reinterpret_cast(w[i]); r+=combine_with_firststage(*pipe, new T(*node), true); } return (r>0?-1:0); } - bool lset_cleanup = a2a.isset_cleanup_firstset(); std::vector new_firstset; - - ff_comb* comb = new ff_comb(node, w[0], cleanup_node, lset_cleanup); + ff_comb* comb = new ff_comb(node, w[0], cleanup_node, false); assert(comb); new_firstset.push_back(comb); for(size_t i=1;i(nodes_list.size())-1; return nodes_list[last]->set_output(node); } + inline int set_output(const svector &w) { + int last = static_cast(nodes_list.size())-1; + return nodes_list[last]->set_output(w); + } + inline int set_output_feedback(ff_node *node) { int last = static_cast(nodes_list.size())-1; diff --git a/tests/Makefile b/tests/Makefile index 2698aaf2..ecf844d0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator test_staticallocator2 test_staticallocator3 test_staticallocator4 +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator test_staticallocator2 test_staticallocator3 test_staticallocator4 test_changenode #test_taskf2 test_taskf3 diff --git a/tests/test_changenode.cpp b/tests/test_changenode.cpp new file mode 100644 index 00000000..7be68287 --- /dev/null +++ b/tests/test_changenode.cpp @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + +/* + * Testing the change_node method for the pipeline, all-to-all and combine. + * + */ + +/* + * Author: Massimo Torquati + */ + +#include + +using namespace ff; + +struct Source: ff_monode_t { + Source(long ntasks): ntasks(ntasks) {} + long* svc(long*) { + for (long i=1;i<=ntasks;++i){ + ff_send_out((long*)i); + } + return EOS; + } + long ntasks; +}; +struct miNode:ff_minode_t { + long* svc(long* in) { + return in; + } +}; +struct moNode:ff_monode_t { + long* svc(long* in) { + return in; + } +}; +struct Sink: ff_minode_t { + long* svc(long* in) { + printf("in=%ld\n", (long)in); + return GO_ON; + } +}; + + + +int main() { + Source source(10); + Sink sink; + + miNode* uno = new miNode; + moNode* due = new moNode; + ff_pipeline pipe0; + pipe0.add_stage(uno, true); + pipe0.add_stage(due, true); + + ff_a2a a2a; + std::vector W1, W2; + miNode* tre = new miNode; + moNode* quattro = new moNode; + ff_comb *c1 = new ff_comb(tre, quattro, true, true); + W1.push_back(c1); + miNode* cinque = new miNode; + moNode* sei = new moNode; + ff_comb *c2 = new ff_comb(cinque, sei, true, true); + W1.push_back(c2); + a2a.add_firstset(W1, 0, true); + + miNode* sette = new miNode; + moNode* otto = new moNode; + ff_comb *c3 = new ff_comb(sette, otto, true, true); + W2.push_back(c3); + a2a.add_secondset(W2,true); + + + ff_pipeline pipeMain; + pipeMain.add_stage(&source); + pipeMain.add_stage(&pipe0); + pipeMain.add_stage(&a2a); + pipeMain.add_stage(&sink); + + // changing some nodes + ff_node* c = getBB(&pipeMain, otto); + assert(c==c3); + (reinterpret_cast(c))->change_node(otto, new miNode, true); + + ff_node* t1 = getBB(&pipeMain, tre); + assert(t1 == c1); + (reinterpret_cast(t1))->change_node(tre, new moNode, true); + + ff_node* t2 = getBB(&pipeMain, cinque); + assert(t2 == c2); + (reinterpret_cast(t2))->change_node(cinque, new moNode, true); + + ff_node* t3 = getBB(&pipeMain, c2); + assert(t3 == &a2a); + (reinterpret_cast(t3))->change_node(c2, new ff_comb(new moNode, new moNode, true, true), true, true); + delete c2; + + pipeMain.change_node(&source, new Source(100), true); + + // now run the modified pipeline + if (pipeMain.run_and_wait_end()<0) { + error("running pipe\n"); + return -1; + } + + return 0; +} From 8ae52ed63a0821944318ebf0a0d2d8a7bcd7bf56 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 9 Feb 2021 15:31:16 +0100 Subject: [PATCH 017/202] added a missing method in the A2A, moved from protected to public the methods get_in/out_nodes in the ff_pipeline class. --- ff/all2all.hpp | 6 ++++++ ff/pipeline.hpp | 38 +++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 0d346555..7603a0b5 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -537,6 +537,12 @@ class ff_a2a: public ff_node { if (w.size() == 0) w += getSecondSet(); } + + void get_out_nodes_feedback(svector& w) { + for(size_t i=0;iget_out_nodes_feedback(w); + } + void get_in_nodes(svector&w) { size_t len=w.size(); for(size_t i=0;i&w) { + assert(nodes_list.size()>0); + int last = static_cast(nodes_list.size())-1; + nodes_list[last]->get_out_nodes(w); + } + + inline void get_out_nodes_feedback(svector&w) { + assert(nodes_list.size()>0); + int last = static_cast(nodes_list.size())-1; + nodes_list[last]->get_out_nodes_feedback(w); + } + + + inline void get_in_nodes(svector&w) { + assert(nodes_list.size()>0); + nodes_list[0]->get_in_nodes(w); + } + void skipfirstpop(bool sk) { get_node(0)->skipfirstpop(sk); for(size_t i=1;i&w) { - assert(nodes_list.size()>0); - int last = static_cast(nodes_list.size())-1; - nodes_list[last]->get_out_nodes(w); - } - - inline void get_out_nodes_feedback(svector&w) { - assert(nodes_list.size()>0); - int last = static_cast(nodes_list.size())-1; - nodes_list[last]->get_out_nodes_feedback(w); - } - - - inline void get_in_nodes(svector&w) { - assert(nodes_list.size()>0); - nodes_list[0]->get_in_nodes(w); - } - - /* The pipeline has not been flattened and its first stage is a multi-input node used as * a standard node. */ From c59073e6411aef6cfe8983677d1387763819506d Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 10 Feb 2021 12:14:56 +0100 Subject: [PATCH 018/202] moved get_in/out_nodes public --- ff/node.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ff/node.hpp b/ff/node.hpp index 9e112a9f..cad4c8f5 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -739,12 +739,7 @@ class ff_node { } virtual inline int set_output_feedback(ff_node *) { return -1;} virtual inline void set_input_channelid(ssize_t, bool=true) {} - - virtual inline void get_out_nodes(svector&w) { w.push_back(this); } - virtual inline void get_out_nodes_feedback(svector&) {} - virtual inline void get_in_nodes(svector&w) { w.push_back(this); } - virtual inline void get_in_nodes_feedback(svector&) {} - + virtual int prepare() { prepared=true; return 0; } virtual int dryrun() { if (!prepared) return prepare(); return 0; } @@ -999,6 +994,11 @@ class ff_node { virtual void callbackOut(void * =NULL) { } #endif + virtual inline void get_out_nodes(svector&w) { w.push_back(this); } + virtual inline void get_out_nodes_feedback(svector&) {} + virtual inline void get_in_nodes(svector&w) { w.push_back(this); } + virtual inline void get_in_nodes_feedback(svector&) {} + /** * \brief Force ff_node-to-core pinning * From 68df1b12569b7511eb081ece96b7cd7c2ba7d580 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 20 Feb 2021 15:47:00 +0100 Subject: [PATCH 019/202] minor improvements to the staticallocator. --- ff/lb.hpp | 2 ++ ff/mpmc/MPMCqueues.hpp | 2 +- ff/staticallocator.hpp | 27 ++++++++++++------------ tests/test_staticallocator.cpp | 8 ++++++- tests/test_staticallocator2.cpp | 20 ++++++++++++++---- tests/test_staticallocator3.cpp | 37 ++++++++++++++++++++++----------- 6 files changed, 64 insertions(+), 32 deletions(-) diff --git a/ff/lb.hpp b/ff/lb.hpp index 2dd318ef..ebc63162 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -440,6 +440,8 @@ class ff_loadbalancer: public ff_thread { } } + // FIX: this function is too costly, it should be re-implemented! + // int get_next_free_channel(bool forever=true) { long x=1; const size_t attempts = (forever ? (size_t)-1 : running); diff --git a/ff/mpmc/MPMCqueues.hpp b/ff/mpmc/MPMCqueues.hpp index 8f5c1015..0cef78bf 100644 --- a/ff/mpmc/MPMCqueues.hpp +++ b/ff/mpmc/MPMCqueues.hpp @@ -52,7 +52,7 @@ #pragma message ("Define -DNO_STD_C0X to use a non c++0x/c++11 compiler") #endif -#define NO_STD_C0X +//#define NO_STD_C0X // // Check for g++ version >= 4.5 diff --git a/ff/staticallocator.hpp b/ff/staticallocator.hpp index fd880574..f8dcb081 100644 --- a/ff/staticallocator.hpp +++ b/ff/staticallocator.hpp @@ -33,7 +33,6 @@ #include #include #include -#include #include /* Author: Massimo Torquati @@ -41,7 +40,6 @@ */ - namespace ff { class StaticAllocator { @@ -52,18 +50,23 @@ class StaticAllocator { assert(nchannels>0); assert(slotsize>0); ssize = slotsize + sizeof(long*); - // rounding up nslot to be multiple of nchannels nslot = ((_nslot + nchannels -1) / nchannels) * nchannels; slotsxchannel = nslot / nchannels; - - + } + + ~StaticAllocator() { + if (segment) munmap(segment, nslot*ssize); + segment=nullptr; + } + + int init() { void* result = 0; result = mmap(NULL, nslot*ssize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (result == MAP_FAILED) abort(); + if (result == MAP_FAILED) return -1; segment = (char*)result; - + // initialize the "header" char* p = segment; for(size_t i=0; i void alloc(T*& p, int channel=0) { assert(channel>=0 && channel { Source(long ntasks): ntasks(ntasks) {} - + + int svc_init() { + return SAlloc->init(); + } S_t* svc(S_t*) { long start = get_my_id()*ntasks; for (long i=1;i<=ntasks;++i){ @@ -72,6 +75,9 @@ struct Source: ff_monode_t { }; struct FlatMap: ff_monode_t { + int svc_init() { + return SAlloc->init(); + } S_t* svc(S_t* in) { for(int i=0;i @@ -66,7 +72,11 @@ static std::mutex mtx; // used only for pretty printing struct Source: ff_monode_t { Source(long ntasks, StaticAllocator* SAlloc): ntasks(ntasks), SAlloc(SAlloc) {} - + + int svc_init() { + return SAlloc->init(); + } + S_t* svc(S_t*) { long start = get_my_id()*ntasks; for (long i=1;i<=ntasks;++i){ @@ -88,6 +98,9 @@ struct Source: ff_monode_t { struct FlatMap: ff_monode_t { FlatMap(StaticAllocator* SAlloc): SAlloc(SAlloc) { } + int svc_init() { + return SAlloc->init(); + } S_t* svc(S_t* in) { for(int i=0;i @@ -74,15 +79,15 @@ static std::mutex mtx; // used only for pretty printing struct Source: ff_monode_t { Source(long ntasks, StaticAllocator* SAlloc): ntasks(ntasks), SAlloc(SAlloc) {} - + + int svc_init() { + return (!enablestd?SAlloc->init():0); + } S_t* svc(S_t*) { long start = get_my_id()*ntasks; for (long i=1;i<=ntasks;++i){ S_t* p; - if (enablestd) { - p = new S_t; - assert(p); - } else { + if (!enablestd) { // NOTE: // to know which allocator to use, we must use // the get_next_free_channel to know which will be @@ -91,7 +96,11 @@ struct Source: ff_monode_t { int ch = get_next_free_channel(); assert(ch>=0 && ch <(int)get_num_outchannels()); SAlloc->alloc(p, ch); + } else { + p = new S_t; + assert(p); } + p->t = start+i; p->f = p->t*1.0; @@ -106,16 +115,21 @@ struct Source: ff_monode_t { struct FlatMap: ff_monode_t { FlatMap(StaticAllocator* SAlloc): SAlloc(SAlloc) { } + + int svc_init() { + return (!enablestd?SAlloc->init():0); + } + S_t* svc(S_t* in) { for(int i=0;i=0 && ch <(int)get_num_outchannels()); SAlloc->alloc(p,ch); + } else { + p = new S_t; + assert(p); } *p = *in; @@ -153,7 +167,6 @@ struct Sink: ff_minode_t { } std::map M; - StaticAllocator* SAlloc=nullptr; }; @@ -294,7 +307,7 @@ int main(int argc, char* argv[]) { StaticAllocator* FlatMapAlloc = nullptr; if (!enablestd){ // NOTE: for each queue we have +2 slots - FlatMapAlloc = new StaticAllocator((nMap*nSink +1)*(qlen+2), sizeof(S_t), nMap); + FlatMapAlloc = new StaticAllocator((nSink +1)*(qlen+2), sizeof(S_t), nMap); assert(FlatMapAlloc); } L.push_back(new ff_comb(new miHelper, new FlatMap(FlatMapAlloc), true, true)); @@ -314,7 +327,7 @@ int main(int argc, char* argv[]) { StaticAllocator* SourceAlloc = nullptr; if (!enablestd) { // NOTE: for each queue we have +2 slots - SourceAlloc = new StaticAllocator( nFlatMap*(qlen+2), sizeof(S_t), nFlatMap); + SourceAlloc = new StaticAllocator( 1*(qlen+2), sizeof(S_t), nFlatMap); assert(SourceAlloc); } L.push_back(new Source(ntasks, SourceAlloc)); From bf453a5cd1a86659de2b225e8b527a10583d0bb5 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 22 Feb 2021 17:30:20 +0100 Subject: [PATCH 020/202] moved into the run method the assignment of the workers' id --- ff/farm.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ff/farm.hpp b/ff/farm.hpp index 16334982..210089dc 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -216,6 +216,9 @@ class ff_farm: public ff_node { error("FARM: wrong number of workers\n"); return -1; } + for(size_t i=0;iset_id(int(i)); + } // NOTE: if the farm is in a master-worker configuration, all workers must be either // sequential or parallel building block @@ -1074,8 +1077,7 @@ class ff_farm: public ff_node { return -1; } for(size_t i=0;iset_id(int(i)); + workers.push_back(w[i]); } return 0; From 80c779bf181a4751350b77144b62d5bf79ff2635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Tue, 9 Mar 2021 10:14:51 +0100 Subject: [PATCH 021/202] First commit of the distributed support --- ff/all2all.hpp | 4 + ff/dff.hpp | 32 ++ ff/distributed/ff_dgroup.hpp | 670 ++++++++++++++++++++++++++++ ff/distributed/ff_dreceiver.hpp | 233 ++++++++++ ff/distributed/ff_dsender.hpp | 213 +++++++++ ff/distributed/ff_network.hpp | 148 ++++++ ff/distributed/ff_wrappers.hpp | 335 ++++++++++++++ ff/distributed/loader/loader_v1.cpp | 144 ++++++ ff/pipeline.hpp | 13 +- 9 files changed, 1791 insertions(+), 1 deletion(-) create mode 100644 ff/dff.hpp create mode 100644 ff/distributed/ff_dgroup.hpp create mode 100644 ff/distributed/ff_dreceiver.hpp create mode 100644 ff/distributed/ff_dsender.hpp create mode 100644 ff/distributed/ff_network.hpp create mode 100644 ff/distributed/ff_wrappers.hpp create mode 100644 ff/distributed/loader/loader_v1.cpp diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 7603a0b5..a845be40 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -702,6 +702,10 @@ class ff_a2a: public ff_node { } #endif +#ifdef DFF_ENABLED + dGroup& createGroup(std::string); +#endif + protected: diff --git a/ff/dff.hpp b/ff/dff.hpp new file mode 100644 index 00000000..e478cdfd --- /dev/null +++ b/ff/dff.hpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ + + +/* *************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ + +#ifndef FF_DFF_HPP +#define FF_DFF_HPP + +#define DFF_ENABLED + +#include + +#include +#include + +#endif /* FF_DFF_HPP */ diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp new file mode 100644 index 00000000..f8c8c1d5 --- /dev/null +++ b/ff/distributed/ff_dgroup.hpp @@ -0,0 +1,670 @@ +#ifndef FF_DGROUP_H +#define FF_DGROUP_H + +#include +#include +#include +#include +#include +#include +#include "ff_wrappers.hpp" +#include "ff_dreceiver.hpp" +#include "ff_dsender.hpp" + +#include +#include +#include +#include + +using namespace ff; + +class dGroup; + +class dGroups { +public: + + static dGroups* Instance(){ + if (i == nullptr) + i = new dGroups(); + return i; + } + + void parseConfig(); + + void addGroup(std::string label, dGroup* g){ groups.insert(make_pair(label, g));} + + int size(){ return groups.size();} + + void setConfigFile(std::string f){this->configFilePath = f;} + + void setRunningGroup(std::string g){this->runningGroup = g;} + + dGroup* getRunningGroup(){return this->groups[this->runningGroup];} + +protected: + dGroups() : groups(), configFilePath(), runningGroup() { + // costruttore + } + +private: + inline static dGroups* i = nullptr; + std::map groups; + std::string configFilePath; + std::string runningGroup; + + // helper class to parse config file Json + struct G { + std::string name; + std::string address; + int port; + std::vector Oconn; + + template + void load( Archive & ar ){ + ar(cereal::make_nvp("name", name)); + + try { + std::string endpoint; + ar(cereal::make_nvp("endpoint", endpoint)); std::vector endp(split(endpoint, ':')); + address = endp[0]; port = std::stoi(endp[1]); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + + try { + ar(cereal::make_nvp("OConn", Oconn)); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + } + }; + + static inline std::vector split (const std::string &s, char delim) { + std::vector result; + std::stringstream ss (s); + std::string item; + + while (getline (ss, item, delim)) + result.push_back (item); + + return result; + } + + + static int expectedInputConnections(std::string groupName, std::vector& groups){ + int result = 0; + for (const G& g : groups) + if (g.name != groupName) + for (const std::string& conn : g.Oconn) + if (conn == groupName) result++; + return result; + } +}; + +enum IOTypes { IN, OUT }; + +template +class MySet { +private: + dGroup* group; + + struct ForwarderNode : ff_node{ + void* svc(void* input){return input;} + }; +public: + MySet(dGroup* group): group(group){ } + + template + MySet& operator<<(ff_node_t*) {} + + template + MySet& operator<<(ff_minode_t*) {} + + template + MySet& operator<<(ff_monode_t*) {} + + template + MySet& operator<<=(ff_node_t*) {} + + bool check_inout(ff_node* node); +}; + +class dGroups; + +class dGroup : public ff_farm { + + friend class MySet; + friend class MySet; +private: + ff_node * parentStructure; + + ff_endpoint endpoint; + std::vector destinations; + int expectedInputConnections; + + /** + * Key: reference to original node + * Value: pair of [reference to wrapper, serialization_required] + **/ + std::map> in_, out_; + std::map inout_; + + bool isSource(){return in_.empty() && inout_.empty();} + bool isSink(){return out_.empty() && inout_.empty();} + +public: + dGroup(ff_node* parent, std::string label): parentStructure(parent), endpoint(), destinations(), expectedInputConnections(0), in(this), out(this){ + dGroups::Instance()->addGroup(label, this); + } + + static bool isIncludedIn(const ff::svector& firstSet, std::vector& secondSet){ + for (const ff_node* n : firstSet) + if (std::find(secondSet.begin(), secondSet.end(), n) == secondSet.end()) + return false; + return true; + } + + bool replaceWrapper(const ff::svector& list, std::map>& wrappers_){ + for (ff_node* node : list){ + ff_node* parentBB = getBB(this->parentStructure, node); + if (parentBB != nullptr){ + + ff_node* wrapped = wrappers_[node].first; + + if (parentBB->isPipe()){ + reinterpret_cast(parentBB)->change_node(node, wrapped, true); + continue; + } + + if (parentBB->isAll2All()){ + reinterpret_cast(parentBB)->change_node(node, wrapped, true); + continue; + } + + if (parentBB->isComp()){ + reinterpret_cast(parentBB)->change_node(node, wrapped, true); + continue; + } + + return false; + } + return false; + } + return true; + } + + ff_node* getOriginal(ff_node* wrapper){ + auto resultI = std::find_if(this->in_.begin(), this->in_.end(), [&](const std::pair> &pair){return pair.second.first == wrapper;}); + if (resultI != this->in_.end()) return resultI->first; + auto resultII = std::find_if(this->inout_.begin(), this->inout_.end(), [&](const std::pair &pair){return pair.second == wrapper;}); + if (resultII != this->inout_.end()) return resultII->first; + + return nullptr; + } + + static inline bool isSeq(ff_node* n){return (!n->isAll2All() && !n->isComp() && !n->isFarm() && !n->isOFarm() && !n->isPipe());} + + bool processBB(ff_node* bb, std::vector in_C, std::vector out_C){ + if (isSeq(bb)){ + ff::svector svectBB(1); svectBB.push_back(bb); + if (isSource() && this->out_.find(bb) != this->out_.end() && replaceWrapper(svectBB, this->out_)){ + this->add_workers({this->out_[bb].first}); + return true; + } + + if (isSink() && this->in_.find(bb) != this->in_.end() && replaceWrapper(svectBB, this->in_)){ + this->add_workers({this->in_[bb].first}); + return true; + } + + return false; + } + + ff::svector in_nodes, out_nodes; + bb->get_in_nodes(in_nodes); + + if (!isSource() && !isIncludedIn(in_nodes, in_C)) + return false; + + bb->get_out_nodes(out_nodes); + + if (!isSink() && !isIncludedIn(out_nodes, out_C)) + return false; + + if ((isSource() || replaceWrapper(in_nodes, this->in_)) && (isSink() || replaceWrapper(out_nodes, this->out_))){ + this->add_workers({bb}); // here the bb is already modified with the wrapper + return true; + } + + return false; + } + + static bool isStageOf(ff_node* n, ff_pipeline* p){ + for (const ff_node* s : p->getStages()) + if (s == n) return true; + + return false; + } + + static int getInputIndexOfNode(ff_node* bb, ff_node* wrapper, ff_node* original){ + if (bb->isAll2All()){ + ff_a2a* a2a = (ff_a2a*) bb; + int index = 0; + for (ff_node* n : a2a->getFirstSet()){ + ff::svector inputs; n->get_in_nodes(inputs); + for (const ff_node* input : inputs){ + if (input == wrapper || input == original) + return index; + index++; + } + } + + index = 0; + for (ff_node* n : a2a->getSecondSet()){ + ff::svector inputs; n->get_in_nodes(inputs); + for (ff_node* input : inputs) + if (input == wrapper || input == original) + return index; + else index++; + } + } + + int index = 0; + ff::svector inputs; bb->get_in_nodes(inputs); + for (ff_node* input : inputs) + if (input == wrapper || input == original) + return index; + else index++; + + return 0; + } + + std::map buildRoutingTable(ff_node* level1BB){ + std::map routingTable; + int localIndex = 0; + for (ff_node* inputBB : this->getWorkers()){ + ff::svector inputs; inputBB->get_in_nodes(inputs); + for (ff_node* input : inputs){ + routingTable[getInputIndexOfNode(level1BB, input, getOriginal(input))] = localIndex; + localIndex++; + } + //routingTable[getInputIndexOfNode(level1BB, reinterpret_cast(input)->getOriginal())] = localIndex++; + } + return routingTable; + } + + + + int buildFarm(ff_pipeline* basePipe = nullptr){ // chimato dalla run & wait della main pipe + + // find the 1 level builiding block which containes the group (level 1 BB means a BB whoch is a stage in the main piepline) + ff_node* level1BB = this->parentStructure; + while(!isStageOf(level1BB, basePipe)){ + level1BB = getBB(basePipe, level1BB); + if (!level1BB || level1BB == basePipe) throw FF_Exception("A group were created from a builiding block not included in the Main Pipe! :("); + } + + + std::vector in_C, out_C; + for (const auto& pair : this->in_) in_C.push_back(pair.first); + for (const auto& pair : this->out_) out_C.push_back(pair.first); + + + if (this->parentStructure->isPipe()) + processBB(this->parentStructure, in_C, out_C); + + + if (this->parentStructure->isAll2All()){ + ff_a2a * a2a = (ff_a2a*) this->parentStructure; + + if (!processBB(a2a, in_C, out_C)){ // if the user has not wrapped the whole a2a, expan its sets + + for(ff_node* bb : a2a->getFirstSet()) + processBB(bb, in_C, out_C); + + for(ff_node* bb : a2a->getSecondSet()) + processBB(bb, in_C, out_C); + } + + } + + // in/out nodes left to be added to the farm. The next lines does it + for (const auto& pair : this->inout_){ + this->add_workers({pair.second}); + } + + if (this->getNWorkers() == 0) + return -1; + + + // create receiver + if (!isSource()){ + std::cout << "Creating the receiver!" << std::endl; + this->add_emitter(new ff_dreceiver(0 , this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! + } + // create sender + if (!isSink()){ + std::cout << "Creating the sender!" << std::endl; + this->add_collector(new ff_dsender(0, this->destinations), true); + } + + + std::cout << "Built a farm of " << this->getNWorkers() << " workers!" << std::endl; + // call the base class (ff_farm)'s prepare + return 0; + } + + void print(){ + for(auto it = inout_.cbegin(); it != inout_.cend(); ++it) + { + std::cout << it->first << " " << it->second << std::endl; + } + } + + ff_node* getWrapper(ff_node* n){ + return this->inout_[n]; + } + + int run(bool skip_init=false){ + buildFarm(); + return ff_farm::run(skip_init); + } + + int run(ff_pipeline* baseBB, bool skip_init=false) { + // parsare il config File + dGroups* groups_ = dGroups::Instance(); + groups_->parseConfig(); + + buildFarm(baseBB); + + //std::cout << "Called run of the group!" << std::endl; + return ff_farm::run(skip_init); + } + + int wait(){return ff_farm::wait();} + + int run_and_wait_end() { + if (run()<0) return -1; + if (ff_farm::wait()<0) return -1; + return 0; + } + + + void setEndpoint(const std::string address, const int port){ + this->endpoint.address = address; + this->endpoint.port = port; + } + + ff_endpoint getEndpoint(){return this->endpoint;} + + void setDestination(ff_endpoint e){ this->destinations.push_back(std::move(e));} + + void setExpectedInputConnections(int connections){this->expectedInputConnections = connections;} + + MySet in; + MySet out; +}; + + +/** + * If the user uses << operator -> serialization is used + * If the user uses <<= operator -> NO serialization is used + **/ + +template<> +template +MySet& MySet::operator<<(ff_node_t* node){ + /*if (condizione){ + error("Errore!"); + throw + }*/ + + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(node); + if (!handle.empty()) // the node is edge also in its output + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + else + this->group->in_.insert({node, {new WrapperIN(node, true), true}}); + + return *this; +} + +template<> +template +MySet& MySet::operator<<(ff_minode_t* node){ + /*if (condizione){ + error("Errore!"); + throw + }*/ + + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(node); + if (!handle.empty()) // the node is edge also in its output + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + else + this->group->in_.insert({node, {new WrapperIN(node, true), true}}); + + return *this; +} + +template<> +template +MySet& MySet::operator<<(ff_monode_t* node){ + /*if (condizione){ + error("Errore!"); + throw + }*/ + + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(node); + if (!handle.empty()) // the node is edge also in its output + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + else { + ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true); + this->group->in_.insert({node, {combine, true}}); + } + + return *this; +} + +template<> +template +MySet& MySet::operator<<(ff_node_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(node); + if (!handle.empty()) // the node is edge also in its input + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + else + this->group->out_.insert({node, {new WrapperOUT(node, true), true}}); + + return *this; +} + +template<> +template +MySet& MySet::operator<<(ff_minode_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(node); + if (!handle.empty()) // the node is edge also in its input + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + else { + ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, true), false, true); + this->group->out_.insert({node, {combine, true}}); + } + + return *this; +} + +template<> +template +MySet& MySet::operator<<(ff_monode_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(node); + if (!handle.empty()) // the node is edge also in its input + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); + else + this->group->out_.insert({node, {new WrapperOUT(node, true), true}}); + + return *this; +} + + +template<> +template +MySet& MySet::operator<<=(ff_node_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(node); + if (!handle.empty()) // the node is edge also in its output + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + else + this->group->in_.insert({node, {new WrapperIN(node, true), false}}); + + return *this; +} + +template<> +template +MySet& MySet::operator<<=(ff_node_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(node); + if (!handle.empty()) // the node is edge also in its input + this->group->inout_.insert({node, handle.mapped().second ? (ff_node*) new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + else + this->group->out_.insert({node, {new WrapperOUT(node, true), false}}); + + return *this; +} + +template +bool MySet::check_inout(ff_node* node){ + return this->group->inout_.find(node) != this->group->inout_.end(); + } + + +void dGroups::parseConfig(){ + if (this->configFilePath.empty()) throw FF_Exception("Config file not defined!"); + + std::ifstream is(this->configFilePath); + + if (!is) throw FF_Exception("Unable to open configuration file for the program!"); + + cereal::JSONInputArchive ari(is); + + std::vector parsedGroups; + + try { + ari(cereal::make_nvp("groups", parsedGroups)); + } catch (const cereal::Exception& e){ + std::cerr << e.what(); + exit(EXIT_FAILURE); + } + + for(G& g : parsedGroups) + if (groups.find(g.name) != groups.end()) + groups[g.name]->setEndpoint(g.address, g.port); + else { + std::cout << "Cannot find group: " << g.name << std::endl; + throw FF_Exception("A specified group in the configuration file has not been implemented! :("); + } + + for(G& g : parsedGroups){ + dGroup* groupRef = groups[g.name]; + for(std::string& conn : g.Oconn) + if (groups.find(conn) != groups.end()) + groupRef->setDestination(groups[conn]->getEndpoint()); + else throw FF_Exception("A specified destination has a wrong name! :("); + + groupRef->setExpectedInputConnections(expectedInputConnections(g.name, parsedGroups)); + } + + } + + void DFF_Init(int &argc, char **&argv){ + int c; + std::string groupName, configFile; + + while (1){ + static struct option long_options[] = + { + {"DFF_GName", required_argument, 0, 'n'}, + {"DFF_Config", required_argument, 0, 'c'}, + {0, 0, 0, 0} + }; + + /* getopt_long stores the option index here. */ + int option_index = 0; + c = getopt_long(argc, argv, "", long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) break; + + switch (c){ + case 0: + /* If this option set a flag, do nothing else now. */ + if (long_options[option_index].flag != 0) + break; + printf("option %s", long_options[option_index].name); + if (optarg) + printf(" with arg %s", optarg); + printf("\n"); + break; + + case 'c': + configFile = std::string(optarg); + break; + + case 'n': + groupName = std::string(optarg); + break; + + case '?': + /* getopt_long already printed an error message. */ + break; + + default: + abort(); + } + } + + if (configFile.empty()){ + std::cerr << "Config file not passed as argument!" << std::endl; + abort(); + } + + if (groupName.empty()){ + std::cerr << "Group not passed as argument!" << std::endl; + abort(); + } + + dGroups::Instance()->setConfigFile(configFile); + dGroups::Instance()->setRunningGroup(groupName); + + // if other arguments are passed, they are preserved! + if (optind <= argc){ + optind--; + char *exeName = argv[0]; + argc -= optind; + argv += optind; + argv[0] = exeName; + } + } + + + + +// redefinition of createGroup methods for ff_a2a and ff_pipeline +dGroup& ff::ff_a2a::createGroup(std::string name){ + dGroup * g = new dGroup(this, std::move(name)); + return *g; +} + +dGroup& ff::ff_pipeline::createGroup(std::string name){ + dGroup * g = new dGroup(this, std::move(name)); + return *g; +} + +#endif \ No newline at end of file diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp new file mode 100644 index 00000000..1f6c6417 --- /dev/null +++ b/ff/distributed/ff_dreceiver.hpp @@ -0,0 +1,233 @@ +#include +#include +#include +#include "ff_network.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ff; + +/* + The distributed sender is a multi output node +*/ +#ifndef FF_DRECEIVER_H +#define FF_DRECEIVER_H + +class ff_dreceiver: public ff_monode_t { +private: + + int sendRoutingTable(int sck){ + dataBuffer buff; std::ostream oss(&buff); + cereal::PortableBinaryOutputArchive oarchive(oss); + std::vector reachableDestinations; + + for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); + + oarchive << reachableDestinations; + + std::cout << "Sending routing table (" << buff.getLen() << " bytes)" << std::endl; + + size_t sz = htobe64(buff.getLen()); + struct iovec iov[1]; + iov[0].iov_base = &sz; + iov[0].iov_len = sizeof(sz); + + if (writevn(sck, iov, 1) < 0 || writen(sck, buff.getPtr(), buff.getLen()) < 0){ + error("Error writing on socket the routing Table"); + return -1; + } + + return 0; + } + + int handleRequest(int sck){ + int sender; + int chid; + size_t sz; + struct iovec iov[3]; + iov[0].iov_base = &sender; + iov[0].iov_len = sizeof(sender); + iov[1].iov_base = &chid; + iov[1].iov_len = sizeof(chid); + iov[2].iov_base = &sz; + iov[2].iov_len = sizeof(sz); + + switch (readvn(sck, iov, 3)) { + case -1: error("Error reading from socket"); // fatal error + case 0: return -1; // connection close + } + + // convert values to host byte order + sender = ntohl(sender); + chid = ntohl(chid); + sz = be64toh(sz); + + if (sz > 0){ + char* buff = new char [sz]; + assert(buff); + if(readn(sck, buff, sz) < 0){ + error("Error reading from socket"); + delete [] buff; + return -1; + } + message_t* out = new message_t(buff, sz, true); + assert(out); + out->sender = sender; + out->chid = chid; + + //std::cout << "received something from " << sender << " directed to " << chid << std::endl; + + ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! + return 0; + } + + neos++; // increment the eos received + return -1; + } + +public: + ff_dreceiver(const int dGroup_id, ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : input_channels(input_channels), acceptAddr(acceptAddr), routingTable(routingTable), + distributedGroupId(dGroup_id), coreid(coreid) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + #ifdef LOCAL + if ((listen_sck=socket(AF_LOCAL, SOCK_STREAM, 0)) < 0){ + error("Error creating the socket"); + return -1; + } + + struct sockaddr_un serv_addr; + memset(&serv_addr, '0', sizeof(serv_addr)); + serv_addr.sun_family = AF_LOCAL; + strncpy(serv_addr.sun_path, acceptAddr.address.c_str(), acceptAddr.address.size()+1); + #endif + + #ifdef REMOTE + if ((listen_sck=socket(AF_INET, SOCK_STREAM, 0)) < 0){ + error("Error creating the socket"); + return -1; + } + + int enable = 1; + // enable the reuse of the address + if (setsockopt(listen_sck, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + error("setsockopt(SO_REUSEADDR) failed"); + + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; // still listening from any interface + serv_addr.sin_port = htons( acceptAddr.port ); + + #endif + + if (bind(listen_sck, (struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0){ + error("Error binding"); + return -1; + } + + if (listen(listen_sck, MAXBACKLOG) < 0){ + error("Error listening"); + return -1; + } + + for (const auto& e : routingTable) + std::cout << "Entry: " << e.first << " -> " << e.second << std::endl; + + return 0; + } + void svc_end() { + close(this->listen_sck); + + #ifdef LOCAL + unlink(this->acceptAddr.address.c_str()); + #endif + std::cout << "Receiver of distribute group " << distributedGroupId << " shutdown " << std::endl; + } + /* + Here i should not care of input type nor input data since they come from a socket listener. + Everything will be handled inside a while true in the body of this node where data is pulled from network + */ + message_t *svc(message_t* task) { + /* here i should receive the task via socket */ + + std::cout << "Receiver correctly started... start to listening" << std::endl; + fd_set set, tmpset; + // intialize both sets (master, temp) + FD_ZERO(&set); + FD_ZERO(&tmpset); + + // add the listen socket to the master set + FD_SET(this->listen_sck, &set); + + // hold the greater descriptor + int fdmax = this->listen_sck; + + while(neos < input_channels){ + // copy the master set to the temporary + tmpset = set; + + switch(select(fdmax+1, &tmpset, NULL, NULL, NULL)){ + case -1: error("Error on selecting socket"); return EOS; + case 0: continue; + } + + // iterate over the file descriptor to see which one is active + for(int i=0; i <= fdmax; i++) + if (FD_ISSET(i, &tmpset)){ + if (i == this->listen_sck) { + int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); + if (connfd == -1){ + error("Error accepting client"); + } else { + FD_SET(connfd, &set); + if(connfd > fdmax) fdmax = connfd; + + this->sendRoutingTable(connfd); // here i should check the result of the call! and handle possible errors! + } + continue; + } + + // it is not a new connection, call receive and handle possible errors + if (this->handleRequest(i) < 0){ + close(i); + FD_CLR(i, &set); + + // update the maximum file descriptor + if (i == fdmax) + for(int i=(fdmax-1);i>=0;--i) + if (FD_ISSET(i, &set)){ + fdmax = i; + break; + } + + } + } + + } + + /* In theory i should never return because of the while true. In our first example this is necessary */ + return this->EOS; + } + +private: + size_t neos = 0; + size_t input_channels; + int listen_sck; + ff_endpoint acceptAddr; + std::map routingTable; + int distributedGroupId; + int coreid; +}; + +#endif diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp new file mode 100644 index 00000000..4eec93f1 --- /dev/null +++ b/ff/distributed/ff_dsender.hpp @@ -0,0 +1,213 @@ +#ifndef FF_DSENDER_H +#define FF_DSENDER_H + +#include +#include +#include +#include "ff_network.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +using namespace ff; + +class ff_dsender: public ff_minode_t { +private: + size_t neos=0; + int distibutedGroupId; + int next_rr_destination = 0; //next destiation to send for round robin policy + std::vector dest_endpoints; + std::map dest2Socket; + std::map sockets; + int coreid; + + int receiveReachableDestinations(int sck){ + size_t sz; + + //while (readvn(sck, iov, 1) != sizeof(sz)) // wait untill a size is received! + recv(sck, &sz, sizeof(sz), MSG_WAITALL); + + sz = be64toh(sz); + + std::cout << "Receiving routing table (" << sz << " bytes)" << std::endl; + char* buff = new char [sz]; + assert(buff); + + if(readn(sck, buff, sz) < 0){ + error("Error reading from socket"); + delete [] buff; + return -1; + } + + dataBuffer dbuff(buff, sz, true); + std::istream iss(&dbuff); + cereal::PortableBinaryInputArchive iarchive(iss); + std::vector destinationsList; + + iarchive >> destinationsList; + + for (int d : destinationsList) dest2Socket[d] = sck; + + return 0; + } + + int create_connect(const ff_endpoint& destination){ + int socketFD; + + #ifdef LOCAL + socketFD = socket(AF_LOCAL, SOCK_STREAM, 0); + if (socketFD < 0){ + error("\nError creating socket \n"); + return socketFD; + } + struct sockaddr_un serv_addr; + memset(&serv_addr, '0', sizeof(serv_addr)); + serv_addr.sun_family = AF_LOCAL; + + strncpy(serv_addr.sun_path, destination.address.c_str(), destination.address.size()+1); + + if (connect(socketFD, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){ + close(socketFD); + return -1; + } + #endif + + #ifdef REMOTE + struct addrinfo hints; + struct addrinfo *result, *rp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ + hints.ai_flags = 0; + hints.ai_protocol = IPPROTO_TCP; /* Allow only TCP */ + + // resolve the address + if (getaddrinfo(destination.address.c_str() , std::to_string(destination.port).c_str() , &hints, &result) != 0) + return -1; + + // try to connect to a possible one of the resolution results + for (rp = result; rp != NULL; rp = rp->ai_next) { + socketFD = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (socketFD == -1) + continue; + + if (connect(socketFD, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + + close(socketFD); + } + + if (rp == NULL) /* No address succeeded */ + return -1; + #endif + + // receive the reachable destination from this sockets + receiveReachableDestinations(socketFD); + + + return socketFD; + } + + int tryConnect(const ff_endpoint &destination){ + int fd, retries = 0; + + while((fd = this->create_connect(destination)) < 0 && ++retries < MAX_RETRIES) + std::this_thread::sleep_for(std::chrono::milliseconds((long)std::pow(2, retries))); + + return fd; + } + + int sendToSck(int sck, message_t* task){ + //std::cout << "received something from " << task->sender << " directed to " << task->chid << std::endl; + task->sender = htonl(task->sender); + task->chid = htonl(task->chid); + + size_t sz = htobe64(task->data.getLen()); + struct iovec iov[3]; + iov[0].iov_base = &task->sender; + iov[0].iov_len = sizeof(task->sender); + iov[1].iov_base = &task->chid; + iov[1].iov_len = sizeof(task->chid); + iov[2].iov_base = &sz; + iov[2].iov_len = sizeof(sz); + + if (writevn(sck, iov, 3) < 0){ + error("Error writing on socket header"); + return -1; + } + + if (writen(sck, task->data.getPtr(), task->data.getLen()) < 0){ + error("Error writing on socket data"); + return -1; + } + + return 0; + } + + +public: + ff_dsender(const int dGroup_id, ff_endpoint dest_endpoint, int coreid=-1) + : distibutedGroupId(dGroup_id),coreid(coreid) { + this->dest_endpoints.push_back(std::move(dest_endpoint)); + } + + ff_dsender(const int dGroup_id, std::vector dest_endpoints_, int coreid=-1) + : distibutedGroupId(dGroup_id), dest_endpoints(std::move(dest_endpoints_)),coreid(coreid) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + for(size_t i=0; i < this->dest_endpoints.size(); i++) + sockets[i] = tryConnect(this->dest_endpoints[i]); + + return 0; + } + + void svc_end() { + // close the socket not matter if local or remote + for(size_t i=0; i < this->sockets.size(); i++) + close(sockets[i]); + } + message_t *svc(message_t* task) { + /* here i should send the task via socket */ + if (task->chid == -1){ // roundrobin over the destinationss + task->chid = next_rr_destination; + next_rr_destination = (next_rr_destination + 1) % dest2Socket.size(); + } + + sendToSck(dest2Socket[task->chid], task); + + delete task; + return this->GO_ON; + } + + void eosnotify(ssize_t) { + if (++neos >= this->get_num_inchannels()){ + message_t * E_O_S = new message_t; + E_O_S->chid = 0; + E_O_S->sender = 0; + for(const auto& pair : sockets) + sendToSck(pair.second, E_O_S); + + delete E_O_S; + } + } + + +}; + +#endif \ No newline at end of file diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp new file mode 100644 index 00000000..2853eeb4 --- /dev/null +++ b/ff/distributed/ff_network.hpp @@ -0,0 +1,148 @@ +#ifndef FF_NETWORK +#define FF_NETWORK + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//#define LOCAL +#define REMOTE + +#ifdef LOCAL + #define SOCKET_PATH "./socket_fd" +#endif + +#define MAXBACKLOG 32 +#define MAX_RETRIES 15 + + +#ifdef __APPLE__ + #include + #define htobe16(x) OSSwapHostToBigInt16(x) + #define htole16(x) OSSwapHostToLittleInt16(x) + #define be16toh(x) OSSwapBigToHostInt16(x) + #define le16toh(x) OSSwapLittleToHostInt16(x) + + #define htobe32(x) OSSwapHostToBigInt32(x) + #define htole32(x) OSSwapHostToLittleInt32(x) + #define be32toh(x) OSSwapBigToHostInt32(x) + #define le32toh(x) OSSwapLittleToHostInt32(x) + + #define htobe64(x) OSSwapHostToBigInt64(x) + #define htole64(x) OSSwapHostToLittleInt64(x) + #define be64toh(x) OSSwapBigToHostInt64(x) + #define le64toh(x) OSSwapLittleToHostInt64(x) +#endif + +class dataBuffer: public std::stringbuf { +public: + dataBuffer() + : std::stringbuf(std::ios::in | std::ios::out | std::ios::binary) { + } + dataBuffer(char p[], size_t len, bool cleanup=false) + : std::stringbuf(std::ios::in | std::ios::out | std::ios::binary), + len(len),cleanup(cleanup) { + setg(p, p, p + len); + } + ~dataBuffer() { + if (cleanup) delete [] getPtr(); + } + size_t getLen() const { + if (len>=0) return len; + return str().length(); + } + char* getPtr() const { + return eback(); + } + + void doNotCleanup(){ + cleanup = false; + } + +protected: + ssize_t len=-1; + bool cleanup = false; +}; + +struct message_t { + message_t(){} + message_t(char *rd, size_t size, bool cleanup=true) : data(rd,size,cleanup){} + + int sender; + int chid; + dataBuffer data; +}; + + +struct ff_endpoint { + std::string address; + int port; +}; + + +struct FF_Exception: public std::runtime_error {FF_Exception(const char* err) throw() : std::runtime_error(err) {}}; + +ssize_t readn(int fd, char *ptr, size_t n) { + size_t nleft = n; + ssize_t nread; + + while (nleft > 0) { + if((nread = read(fd, ptr, nleft)) < 0) { + if (nleft == n) return -1; /* error, return -1 */ + else break; /* error, return amount read so far */ + } else if (nread == 0) break; /* EOF */ + nleft -= nread; + ptr += nread; + } + return(n - nleft); /* return >= 0 */ +} + +ssize_t readvn(int fd, struct iovec *v, int count){ + ssize_t rread; + for (int cur = 0;;) { + rread = readv(fd, v+cur, count-cur); + if (rread <= 0) return rread; // error or closed connection + while (cur < count && rread >= (ssize_t)v[cur].iov_len) + rread -= v[cur++].iov_len; + if (cur == count) return 1; // success!! + v[cur].iov_base = (char *)v[cur].iov_base + rread; + v[cur].iov_len -= rread; + } +} + +ssize_t writen(int fd, const char *ptr, size_t n) { + size_t nleft = n; + ssize_t nwritten; + + while (nleft > 0) { + if((nwritten = write(fd, ptr, nleft)) < 0) { + if (nleft == n) return -1; /* error, return -1 */ + else break; /* error, return amount written so far */ + } else if (nwritten == 0) break; + nleft -= nwritten; + ptr += nwritten; + } + return(n - nleft); /* return >= 0 */ +} + +ssize_t writevn(int fd, struct iovec *v, int count){ + ssize_t written; + for (int cur = 0;;) { + written = writev(fd, v+cur, count-cur); + if (written < 0) return -1; + while (cur < count && written >= (ssize_t)v[cur].iov_len) + written -= v[cur++].iov_len; + if (cur == count) return 1; // success!! + v[cur].iov_base = (char *)v[cur].iov_base + written; + v[cur].iov_len -= written; + } +} + +#endif diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp new file mode 100644 index 00000000..433a9403 --- /dev/null +++ b/ff/distributed/ff_wrappers.hpp @@ -0,0 +1,335 @@ +#ifndef WRAPPER_H +#define WRAPPER_H + +#include +#include +#include +#include +#include "ff_network.hpp" +#include +#include +#include + +using namespace ff; + +/* + Wrapper IN class +*/ +template +class WrapperIN: public internal_mi_transformer { + +private: + int inchannels; // number of input channels the wrapped node is supposed to have +public: + + WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } + + WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } + + template + WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } + + void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { + ff_node::registerCallback(cb,arg); + } + + Tin* deserialize(void* buffer) { + message_t* msg = (message_t*)buffer; + Tin* data = nullptr; + if constexpr (Serialization) { + // deserialize the buffer into a heap allocated buffer + + std::istream iss(&msg->data); + cereal::PortableBinaryInputArchive iarchive(iss); + + data = new Tin; + iarchive >> *data; + + } else { + // deserialize the buffer into a heap allocated buffer + + msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper + data = reinterpret_cast(msg->data.getPtr()); + + if (finalizer_) finalizer_(data); + } + + delete msg; + return data; + } + + int svc_init() { + if (this->n->isMultiOutput()) { + ff_minode* mi = reinterpret_cast(this->n); + mi->set_running(inchannels); + } + return n->svc_init(); + } + + void svc_end(){this->n->svc_end();} + + void * svc(void* in) { + message_t* msg = (message_t*)in; + int channelid = msg->sender; + if (this->n->isMultiInput()) { + ff_minode* mi = reinterpret_cast(this->n); + mi->set_input_channelid(channelid, true); + } + return n->svc(deserialize(in)); + } + + ff_node* getOriginal(){ return this->n; } + + std::function finalizer_; + + std::function getFinalizer() {return this->finalizer_;} +}; + + +/* + Wrapper OUT class +*/ +template +class WrapperOUT: public internal_mo_transformer { +private: + int outchannels; // number of output channels the wrapped node is supposed to have +public: + + typedef Tout T_out; + + WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + template + WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + bool serialize(Tout* in, int id) { + if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); + + message_t* msg = nullptr; + + if constexpr (Serialization){ + msg = new message_t; + assert(msg); + msg->sender = this->n->get_my_id(); + msg->chid = id; + std::ostream oss(&msg->data); + cereal::PortableBinaryOutputArchive oarchive(oss); + oarchive << *in; + delete in; + } else { + std::pair raw_data = transform_(in); + + msg = new message_t(raw_data.first, raw_data.second, true); + assert(msg); + msg->sender = this->n->get_my_id(); + msg->chid = id; + } + + return this->ff_send_out(msg); + + } + + void * svc(void* in) { + void* out = n->svc(in); + if (out > FF_TAG_MIN) return out; + serialize(reinterpret_cast(out), -1); + return GO_ON; + } + + int svc_init() { + if (this->n->isMultiOutput()) { + ff_monode* mo = reinterpret_cast(this->n); + mo->set_running(outchannels); + } + return n->svc_init(); + } + + void svc_end(){n->svc_end();} + + int run(bool skip_init=false) { + return internal_mo_transformer::run(skip_init); + } + + std::function(Tout*)> getTransform(){return this->transform_;} + + std::function(Tout*)> transform_; + + ff::ff_node* getOriginal(){return this->n;} + + static inline bool ff_send_out_to_cbk(void* task, int id, + unsigned long retry, + unsigned long ticks, void* obj) { + return ((WrapperOUT*)obj)->serialize(reinterpret_cast(task), id); + } +}; + + +/* + Wrapper INOUT class +*/ +template +class WrapperINOUT: public internal_mi_transformer { + +private: + int inchannels; // number of input channels the wrapped node is supposed to have + int outchannels; // number of output channels the wrapped node is supposed to have +public: + + typedef Tout T_out; + + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + template + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + + Tin* deserialize(void* buffer) { + message_t* msg = (message_t*)buffer; + Tin* data = nullptr; + if constexpr (SerializationIN) { + // deserialize the buffer into a heap allocated buffer + + std::istream iss(&msg->data); + cereal::PortableBinaryInputArchive iarchive(iss); + + data = new Tin; + iarchive >> *data; + + } else { + // deserialize the buffer into a heap allocated buffer + + msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper + data = reinterpret_cast(msg->data.getPtr()); + + if (finalizer_) finalizer_(data); + } + + delete msg; + return data; + } + + bool serialize(Tout* in, int id) { + if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); + + message_t* msg = nullptr; + + if constexpr (SerializationOUT){ + msg = new message_t; + assert(msg); + msg->sender = this->get_my_id(); + msg->chid = id; + std::ostream oss(&msg->data); + cereal::PortableBinaryOutputArchive oarchive(oss); + oarchive << *in; + delete in; + } else { + std::pair raw_data = transform_(in); + + msg = new message_t(raw_data.first, raw_data.second, true); + assert(msg); + msg->sender = this->get_my_id(); + msg->chid = id; + } + + return this->ff_send_out(msg); + } + + int svc_init() { + if (this->n->isMultiOutput()) { + ff_minode* mi = reinterpret_cast(this->n); + mi->set_running(inchannels); + } + return n->svc_init(); + } + + void svc_end(){this->n->svc_end();} + + void * svc(void* in) { + message_t* msg = (message_t*)in; + int channelid = msg->sender; + if (this->n->isMultiInput()) { + ff_minode* mi = reinterpret_cast(this->n); + mi->set_input_channelid(channelid, true); + } + + void* out = n->svc(deserialize(in)); + if (out > FF_TAG_MIN) return out; + serialize(reinterpret_cast(out), -1); + return GO_ON; + } + + ff_node* getOriginal(){ return this->n; } + + std::function finalizer_; + std::function(Tout*)> transform_; + + static inline bool ff_send_out_to_cbk(void* task, int id, + unsigned long retry, + unsigned long ticks, void* obj) { + return ((WrapperINOUT*)obj)->serialize(reinterpret_cast(task), id); + } +}; + +#endif \ No newline at end of file diff --git a/ff/distributed/loader/loader_v1.cpp b/ff/distributed/loader/loader_v1.cpp new file mode 100644 index 00000000..5ee5eb3b --- /dev/null +++ b/ff/distributed/loader/loader_v1.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include <../external/cereal/cereal.hpp> +#include <../external/cereal/archives/json.hpp> +#include <../external/cereal/types/string.hpp> +#include <../external/cereal/types/vector.hpp> + +std::string configFile; +std::string executable; + +inline std::vector split (const std::string &s, char delim) { + std::vector result; + std::stringstream ss (s); + std::string item; + + while (getline (ss, item, delim)) + result.push_back (item); + + return result; +} + +struct G { + std::string name, host; + FILE* fd = nullptr; + + template + void load( Archive & ar ){ + ar(cereal::make_nvp("name", name)); + + try { + std::string endpoint; + ar(cereal::make_nvp("endpoint", endpoint)); std::vector endp(split(endpoint, ':')); + host = endp[0]; //port = std::stoi(endp[1]); + } catch (cereal::Exception&) { + host = "127.0.0.1"; // set the host to localhost if not found in config file! + ar.setNextName(nullptr); + } + + std::cout << "Host: " << host << std::endl; + } + + void run(){ + char b[100]; // ssh -t + sprintf(b, " %s %s %s --DFF_Config=%s --DFF_GName=%s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : "") , executable.c_str(), configFile.c_str(), this->name.c_str()); + std::cout << "Executing the following string: " << b << std::endl; + fd = popen(b, "r"); + + if (fd == NULL) { + printf("Failed to run command\n" ); + exit(1); + } + } + + bool isRemote(){return !(!host.compare("127.0.0.1") || !host.compare("localhost"));} + + +}; + +bool allTerminated(std::vector& groups){ + for (G& g: groups) + if (g.fd != nullptr) + return false; + return true; +} + +int main(int argc, char** argv) { + + std::vector viewGroups; + + int c; + while ((c = getopt (argc, argv, ":f:v:")) != -1) + switch (c){ + case 'f': + configFile = std::string(optarg); + break; + case 'v': + viewGroups = split(optarg, ','); + break; + case ':': + if (optopt == 'v'){ + // see all + } + break; + case '?': + if (optopt == 'f') + fprintf (stderr, "Option -%c requires an argument.\n", optopt); + else if (isprint (optopt)) + fprintf (stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); + return 1; + default: + abort(); + } + + for (int index = optind; index < argc; index++) + executable += std::string(argv[index]) + " "; + + std::ifstream is(configFile); + + if (!is) throw std::runtime_error("Unable to open configuration file for the program!"); + + cereal::JSONInputArchive ar(is); + + std::vector parsedGroups; + + try { + ar(cereal::make_nvp("groups", parsedGroups)); + } catch (const cereal::Exception& e){ + std::cerr << e.what(); + exit(EXIT_FAILURE); + } + + #ifdef DEBUG + for(auto& g : parsedGroups) + std::cout << "Group: " << g.name << " on host " << g.host << std::endl; + #endif + + for (G& g : parsedGroups) + g.run(); + + + while(!allTerminated(parsedGroups)){ + for(G& g : parsedGroups){ + if (g.fd != nullptr){ + char buff[1024]; + char* result = fgets(buff, sizeof(buff), g.fd); + if (result == NULL){ + pclose(g.fd); g.fd = nullptr; + } else { + if (find(viewGroups.begin(), viewGroups.end(), g.name) != viewGroups.end()) + std::cout << "[" << g.name << "]" << buff; + } + } + } + + sleep(0.20); + } + + std::cout << "Everything terminated correctly" << std::endl; + + return 0; +} \ No newline at end of file diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 45c154ff..eb002852 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -41,6 +41,10 @@ #include #endif +#ifdef DFF_ENABLED + class dGroup; +#endif + namespace ff { @@ -1192,6 +1196,9 @@ class ff_pipeline: public ff_node { * Blocking behaviour w.r.t. main thread to be clarified */ int run_and_wait_end() { + + + if (isfrozen()) { // TODO error("PIPE: Error: feature not yet supported\n"); return -1; @@ -1201,7 +1208,7 @@ class ff_pipeline: public ff_node { if (wait()<0) return -1; return 0; } - + /** * \related ff_pipe * \brief run the pipeline, waits that all stages received the End-Of-Stream (EOS), @@ -1465,6 +1472,10 @@ class ff_pipeline: public ff_node { out << "FastFlow trace not enabled\n"; } #endif + +#ifdef DFF_ENABLED + dGroup& createGroup(std::string); +#endif protected: From 83e7880ed8f77a661353e12464e1fc575cf11ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Tue, 9 Mar 2021 16:13:59 +0100 Subject: [PATCH 022/202] Refactoring on distributed support. Added first test --- ff/all2all.hpp | 5 +- ff/dff.hpp | 2 +- ff/distributed/ff_dgroup.hpp | 104 +++------------------------ ff/distributed/ff_dgroups.hpp | 100 ++++++++++++++++++++++++++ ff/node.hpp | 5 ++ ff/pipeline.hpp | 11 ++- tests/distributed/Makefile | 78 ++++++++++++++++++++ tests/distributed/test_complete.cpp | 95 ++++++++++++++++++++++++ tests/distributed/test_complete.json | 13 ++++ 9 files changed, 315 insertions(+), 98 deletions(-) create mode 100644 ff/distributed/ff_dgroups.hpp create mode 100644 tests/distributed/Makefile create mode 100644 tests/distributed/test_complete.cpp create mode 100644 tests/distributed/test_complete.json diff --git a/ff/all2all.hpp b/ff/all2all.hpp index a845be40..667dae5f 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -39,6 +39,9 @@ namespace ff { // forward declarations static ff_node* ispipe_getlast(ff_node*); +#ifdef DFF_ENABLED +class dGroup; +#endif class ff_a2a: public ff_node { friend class ff_farm; @@ -703,7 +706,7 @@ class ff_a2a: public ff_node { #endif #ifdef DFF_ENABLED - dGroup& createGroup(std::string); + ff::dGroup& createGroup(std::string); #endif diff --git a/ff/dff.hpp b/ff/dff.hpp index e478cdfd..422733f2 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -25,7 +25,7 @@ #define DFF_ENABLED #include - +#include #include #include diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f8c8c1d5..6f614da0 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -10,93 +10,12 @@ #include "ff_wrappers.hpp" #include "ff_dreceiver.hpp" #include "ff_dsender.hpp" +#include "ff_dgroups.hpp" -#include -#include -#include -#include - -using namespace ff; +namespace ff{ class dGroup; -class dGroups { -public: - - static dGroups* Instance(){ - if (i == nullptr) - i = new dGroups(); - return i; - } - - void parseConfig(); - - void addGroup(std::string label, dGroup* g){ groups.insert(make_pair(label, g));} - - int size(){ return groups.size();} - - void setConfigFile(std::string f){this->configFilePath = f;} - - void setRunningGroup(std::string g){this->runningGroup = g;} - - dGroup* getRunningGroup(){return this->groups[this->runningGroup];} - -protected: - dGroups() : groups(), configFilePath(), runningGroup() { - // costruttore - } - -private: - inline static dGroups* i = nullptr; - std::map groups; - std::string configFilePath; - std::string runningGroup; - - // helper class to parse config file Json - struct G { - std::string name; - std::string address; - int port; - std::vector Oconn; - - template - void load( Archive & ar ){ - ar(cereal::make_nvp("name", name)); - - try { - std::string endpoint; - ar(cereal::make_nvp("endpoint", endpoint)); std::vector endp(split(endpoint, ':')); - address = endp[0]; port = std::stoi(endp[1]); - } catch (cereal::Exception&) {ar.setNextName(nullptr);} - - try { - ar(cereal::make_nvp("OConn", Oconn)); - } catch (cereal::Exception&) {ar.setNextName(nullptr);} - } - }; - - static inline std::vector split (const std::string &s, char delim) { - std::vector result; - std::stringstream ss (s); - std::string item; - - while (getline (ss, item, delim)) - result.push_back (item); - - return result; - } - - - static int expectedInputConnections(std::string groupName, std::vector& groups){ - int result = 0; - for (const G& g : groups) - if (g.name != groupName) - for (const std::string& conn : g.Oconn) - if (conn == groupName) result++; - return result; - } -}; - enum IOTypes { IN, OUT }; template @@ -366,18 +285,18 @@ class dGroup : public ff_farm { return ff_farm::run(skip_init); } - int run(ff_pipeline* baseBB, bool skip_init=false) { + int run(ff_node* baseBB, bool skip_init=false) override { // parsare il config File dGroups* groups_ = dGroups::Instance(); groups_->parseConfig(); - buildFarm(baseBB); + buildFarm(reinterpret_cast(baseBB)); //std::cout << "Called run of the group!" << std::endl; return ff_farm::run(skip_init); } - int wait(){return ff_farm::wait();} + int wait() override{return ff_farm::wait();} int run_and_wait_end() { if (run()<0) return -1; @@ -565,17 +484,17 @@ void dGroups::parseConfig(){ for(G& g : parsedGroups) if (groups.find(g.name) != groups.end()) - groups[g.name]->setEndpoint(g.address, g.port); + reinterpret_cast(groups[g.name])->setEndpoint(g.address, g.port); else { std::cout << "Cannot find group: " << g.name << std::endl; throw FF_Exception("A specified group in the configuration file has not been implemented! :("); } for(G& g : parsedGroups){ - dGroup* groupRef = groups[g.name]; + dGroup* groupRef = reinterpret_cast(groups[g.name]); for(std::string& conn : g.Oconn) if (groups.find(conn) != groups.end()) - groupRef->setDestination(groups[conn]->getEndpoint()); + groupRef->setDestination(reinterpret_cast(groups[conn])->getEndpoint()); else throw FF_Exception("A specified destination has a wrong name! :("); groupRef->setExpectedInputConnections(expectedInputConnections(g.name, parsedGroups)); @@ -653,16 +572,15 @@ void dGroups::parseConfig(){ } } - - +} // redefinition of createGroup methods for ff_a2a and ff_pipeline -dGroup& ff::ff_a2a::createGroup(std::string name){ +ff::dGroup& ff_a2a::createGroup(std::string name){ dGroup * g = new dGroup(this, std::move(name)); return *g; } -dGroup& ff::ff_pipeline::createGroup(std::string name){ +ff::dGroup& ff_pipeline::createGroup(std::string name){ dGroup * g = new dGroup(this, std::move(name)); return *g; } diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp new file mode 100644 index 00000000..ec743d83 --- /dev/null +++ b/ff/distributed/ff_dgroups.hpp @@ -0,0 +1,100 @@ +#ifndef FF_DGROUPS_H +#define FF_DGROUPS_H + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +namespace ff { + +class dGroups { +public: + + static dGroups* Instance(){ + if (i == nullptr) + i = new dGroups(); + return i; + } + + void parseConfig(); + + void addGroup(std::string label, ff_node* g){ groups.insert(make_pair(label, g));} + + int size(){ return groups.size();} + + void setConfigFile(std::string f){this->configFilePath = f;} + + void setRunningGroup(std::string g){this->runningGroup = g;} + + int run_and_wait_end(ff_node* parent){ + ff_node* runningGroup = this->groups[this->runningGroup]; + runningGroup->run(parent); + runningGroup->wait(); + return 0; + } +protected: + dGroups() : groups(), configFilePath(), runningGroup() { + // costruttore + } + +private: + inline static dGroups* i = nullptr; + std::map groups; + std::string configFilePath; + std::string runningGroup; + + // helper class to parse config file Json + struct G { + std::string name; + std::string address; + int port; + std::vector Oconn; + + template + void load( Archive & ar ){ + ar(cereal::make_nvp("name", name)); + + try { + std::string endpoint; + ar(cereal::make_nvp("endpoint", endpoint)); std::vector endp(split(endpoint, ':')); + address = endp[0]; port = std::stoi(endp[1]); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + + try { + ar(cereal::make_nvp("OConn", Oconn)); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + } + }; + + static inline std::vector split (const std::string &s, char delim) { + std::vector result; + std::stringstream ss (s); + std::string item; + + while (getline (ss, item, delim)) + result.push_back (item); + + return result; + } + + + static int expectedInputConnections(std::string groupName, std::vector& groups){ + int result = 0; + for (const G& g : groups) + if (g.name != groupName) + for (const std::string& conn : g.Oconn) + if (conn == groupName) result++; + return result; + } +}; + +} + +#endif \ No newline at end of file diff --git a/ff/node.hpp b/ff/node.hpp index cad4c8f5..1bebfd2d 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -495,6 +495,7 @@ class ff_node { friend class ff_comb; friend struct internal_mo_transformer; friend struct internal_mi_transformer; + friend class dGroups; private: FFBUFFER * in; ///< Input buffer, built upon SWSR lock-free (wait-free) @@ -758,6 +759,10 @@ class ff_node { if (!thread) return -1; return thread->run(); } + + #ifdef DFF_ENABLED + virtual int run(ff_node*, bool=false) {return 0;} + #endif /** * \brief Suspend (freeze) the ff_node and run it diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index eb002852..fad46c13 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -42,7 +42,8 @@ #endif #ifdef DFF_ENABLED - class dGroup; +#include + #endif @@ -50,6 +51,7 @@ namespace ff { // forward declarations class ff_pipeline; +class dGroup; static inline int optimize_static(ff_pipeline&, const OptLevel&); template static inline int combine_with_firststage(ff_pipeline&,T*,bool=false); @@ -1196,7 +1198,10 @@ class ff_pipeline: public ff_node { * Blocking behaviour w.r.t. main thread to be clarified */ int run_and_wait_end() { - + #ifdef DFF_ENABLED + dGroups::Instance()->run_and_wait_end(this); + return 0; + #endif if (isfrozen()) { // TODO @@ -1474,7 +1479,7 @@ class ff_pipeline: public ff_node { #endif #ifdef DFF_ENABLED - dGroup& createGroup(std::string); + ff::dGroup& createGroup(std::string); #endif protected: diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile new file mode 100644 index 00000000..04106851 --- /dev/null +++ b/tests/distributed/Makefile @@ -0,0 +1,78 @@ +# --------------------------------------------------------------------------- +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception, you may use this file as part of a free software +# library without restriction. Specifically, if other files instantiate +# templates or use macros or inline functions from this file, or you compile +# this file and link it with other files to produce an executable, this +# file does not by itself cause the resulting executable to be covered by +# the GNU General Public License. This exception does not however +# invalidate any other reasons why the executable file might be covered by +# the GNU General Public License. +# +# --------------------------------------------------------------------------- + +CXXFLAGS += -std=c++17 +ifdef DEBUG + OPTIMIZE_FLAGS += -g -fno-inline-functions +else + OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG +endif +ifdef BLOCKING_MODE + CXXFLAGS += -DBLOCKING_MODE +endif +ifdef FF_HOME + INCS += -I$(FF_HOME) +else + INCS += -I ~/SPM/fastflow +endif +ifdef CEREAL_HOME + INCS += -I$(CEREAL_HOME) +else + INCS += -I ~/SPM/cereal +endif + + +CXXFLAGS += -Wall +INCS += -I../src +LIBS = -pthread +INCLUDES = $(INCS) + +SOURCES = $(wildcard *.cpp) +TARGET = $(SOURCES:.cpp=) + +.PHONY: all clean cleanall +.SUFFIXES: .c .cpp .o + +%.d: %.cpp + set -e; $(CXX) -MM $(INCLUDES) $(CXXFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.d: %.c + set -e; $(CC) -MM $(INCLUDES) $(CFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< +%: %.cpp + $(CXX) $(INCLUDES) $(CXXFLAGS) $(OPTIMIZE_FLAGS) -o $@ $< $(LDFLAGS) $(LIBS) + +all: $(TARGET) + +clean: + -rm -fr *.o *~ +cleanall: clean + -rm -fr $(TARGET) *.d ./socket* + +include $(OBJS:.o=.d) diff --git a/tests/distributed/test_complete.cpp b/tests/distributed/test_complete.cpp new file mode 100644 index 00000000..ba673297 --- /dev/null +++ b/tests/distributed/test_complete.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + + + +#define ITEMS 100 +std::mutex mtx; // used only for pretty printing + +using namespace ff; + +struct Source : ff::ff_monode_t{ + int* svc(int* i){ + for(int i=0; i< ITEMS; i++) + ff_send_out(new int(i)); + + return this->EOS; + } +}; + +struct MoNode : ff::ff_monode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct MiNode : ff::ff_minode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct Sink : ff::ff_minode_t{ + int sum = 0; + int* svc(int* i){ + sum += *i; + delete i; + return this->GO_ON; + } + + void svc_end() { + int local_sum = 0; + for(int i = 0; i < ITEMS; i++) local_sum += i; + const std::lock_guard lock(mtx); + std::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; + } +}; + + +int main(int argc, char*argv[]){ + + DFF_Init(argc, argv); + + ff_pipeline mainPipe; + ff::ff_a2a a2a; + Source s; + Sink sink; + ff_Pipe sp(s, a2a); + ff_Pipe sinkp(sink); + mainPipe.add_stage(&sp); + mainPipe.add_stage(&sinkp); + + MoNode sx1, sx2, sx3; + MiNode dx1, dx2, dx3; + + a2a.add_firstset({&sx1, &sx2, &sx3}); + a2a.add_secondset({&dx1, &dx2, &dx3}); + + + + dGroup g1 = sp.createGroup("G1"); + dGroup g3 = sinkp.createGroup("G3"); + + + g1.out << &dx1 << &dx2 << &dx3; + g3.in << &sink; + + mainPipe.run_and_wait_end(); + + return 0; +} \ No newline at end of file diff --git a/tests/distributed/test_complete.json b/tests/distributed/test_complete.json new file mode 100644 index 00000000..f1e030f9 --- /dev/null +++ b/tests/distributed/test_complete.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G3"] + }, + { + "name" : "G3", + "endpoint": "localhost:8005" + } + ] +} From 01e7e9090625f46d137bde2140b70a4c627882f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 10 Mar 2021 16:41:50 +0100 Subject: [PATCH 023/202] Bug fixes and Makefile refactoring --- ff/distributed/ff_dgroup.hpp | 97 +++---------------- ff/distributed/ff_dgroups.hpp | 85 +++++++++++++++- ff/distributed/ff_dreceiver.hpp | 12 +-- ff/distributed/ff_dsender.hpp | 11 +-- ff/distributed/ff_wrappers.hpp | 2 +- ff/distributed/loader/Makefile | 72 ++++++++++++++ .../loader/{loader_v1.cpp => dff_run.cpp} | 35 +++++-- tests/distributed/Makefile | 7 +- tests/distributed/test_complete.cpp | 2 - 9 files changed, 206 insertions(+), 117 deletions(-) create mode 100644 ff/distributed/loader/Makefile rename ff/distributed/loader/{loader_v1.cpp => dff_run.cpp} (83%) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 6f614da0..5f442e93 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -7,10 +7,12 @@ #include #include #include -#include "ff_wrappers.hpp" -#include "ff_dreceiver.hpp" -#include "ff_dsender.hpp" -#include "ff_dgroups.hpp" +#include +#include +#include +#include +#include + namespace ff{ @@ -260,7 +262,7 @@ class dGroup : public ff_farm { // create sender if (!isSink()){ std::cout << "Creating the sender!" << std::endl; - this->add_collector(new ff_dsender(0, this->destinations), true); + this->add_collector(new ff_dsender(this->destinations), true); } @@ -280,30 +282,23 @@ class dGroup : public ff_farm { return this->inout_[n]; } - int run(bool skip_init=false){ - buildFarm(); - return ff_farm::run(skip_init); + int run(bool skip_init=false) override { + // nothing to do + return 0; } int run(ff_node* baseBB, bool skip_init=false) override { - // parsare il config File + dGroups* groups_ = dGroups::Instance(); groups_->parseConfig(); buildFarm(reinterpret_cast(baseBB)); - //std::cout << "Called run of the group!" << std::endl; return ff_farm::run(skip_init); } int wait() override{return ff_farm::wait();} - int run_and_wait_end() { - if (run()<0) return -1; - if (ff_farm::wait()<0) return -1; - return 0; - } - void setEndpoint(const std::string address, const int port){ this->endpoint.address = address; @@ -502,76 +497,6 @@ void dGroups::parseConfig(){ } - void DFF_Init(int &argc, char **&argv){ - int c; - std::string groupName, configFile; - - while (1){ - static struct option long_options[] = - { - {"DFF_GName", required_argument, 0, 'n'}, - {"DFF_Config", required_argument, 0, 'c'}, - {0, 0, 0, 0} - }; - - /* getopt_long stores the option index here. */ - int option_index = 0; - c = getopt_long(argc, argv, "", long_options, &option_index); - - /* Detect the end of the options. */ - if (c == -1) break; - - switch (c){ - case 0: - /* If this option set a flag, do nothing else now. */ - if (long_options[option_index].flag != 0) - break; - printf("option %s", long_options[option_index].name); - if (optarg) - printf(" with arg %s", optarg); - printf("\n"); - break; - - case 'c': - configFile = std::string(optarg); - break; - - case 'n': - groupName = std::string(optarg); - break; - - case '?': - /* getopt_long already printed an error message. */ - break; - - default: - abort(); - } - } - - if (configFile.empty()){ - std::cerr << "Config file not passed as argument!" << std::endl; - abort(); - } - - if (groupName.empty()){ - std::cerr << "Group not passed as argument!" << std::endl; - abort(); - } - - dGroups::Instance()->setConfigFile(configFile); - dGroups::Instance()->setRunningGroup(groupName); - - // if other arguments are passed, they are preserved! - if (optind <= argc){ - optind--; - char *exeName = argv[0]; - argc -= optind; - argv += optind; - argv[0] = exeName; - } - } - } // redefinition of createGroup methods for ff_a2a and ff_pipeline diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index ec743d83..2db1a461 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -6,7 +6,10 @@ #include #include +#include + #include +#include #include #include @@ -34,9 +37,15 @@ class dGroups { void setRunningGroup(std::string g){this->runningGroup = g;} int run_and_wait_end(ff_node* parent){ + if (groups.find(runningGroup) == groups.end()){ + ff::error("The group specified is not found nor implemented!"); + return -1; + } + ff_node* runningGroup = this->groups[this->runningGroup]; - runningGroup->run(parent); - runningGroup->wait(); + + if (runningGroup->run(parent) < 0) return -1; + if (runningGroup->wait() < 0) return -1; return 0; } protected: @@ -95,6 +104,78 @@ class dGroups { } }; +int DFF_Init(int &argc, char **&argv){ + int c; + std::string groupName, configFile; + + while (1){ + static struct option long_options[] = + { + {"DFF_GName", required_argument, 0, 'n'}, + {"DFF_Config", required_argument, 0, 'c'}, + {0, 0, 0, 0} + }; + + /* getopt_long stores the option index here. */ + int option_index = 0; + c = getopt_long(argc, argv, "", long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) break; + + switch (c){ + case 0: + /* If this option set a flag, do nothing else now. */ + if (long_options[option_index].flag != 0) + break; + printf("option %s", long_options[option_index].name); + if (optarg) + printf(" with arg %s", optarg); + printf("\n"); + break; + + case 'c': + configFile = std::string(optarg); + break; + + case 'n': + groupName = std::string(optarg); + break; + + case '?': + /* getopt_long already printed an error message. */ + break; + + default: + return -1; + } + } + + if (configFile.empty()){ + ff::error("Config file not passed as argument!"); + return -1; + } + + if (groupName.empty()){ + ff::error("Group not passed as argument!"); + return -1; + } + + dGroups::Instance()->setRunningGroup(groupName); + dGroups::Instance()->setConfigFile(configFile); + + // if other arguments are passed, they are preserved! + if (optind <= argc){ + optind--; + char *exeName = argv[0]; + argc -= optind; + argv += optind; + argv[0] = exeName; + } + + return 0; + } + } #endif \ No newline at end of file diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 1f6c6417..d67cf869 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -1,7 +1,11 @@ +#ifndef FF_DRECEIVER_H +#define FF_DRECEIVER_H + + #include #include #include -#include "ff_network.hpp" +#include #include #include #include @@ -14,12 +18,6 @@ using namespace ff; -/* - The distributed sender is a multi output node -*/ -#ifndef FF_DRECEIVER_H -#define FF_DRECEIVER_H - class ff_dreceiver: public ff_monode_t { private: diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 4eec93f1..94658252 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -4,7 +4,7 @@ #include #include #include -#include "ff_network.hpp" +#include #include #include #include @@ -25,7 +25,6 @@ using namespace ff; class ff_dsender: public ff_minode_t { private: size_t neos=0; - int distibutedGroupId; int next_rr_destination = 0; //next destiation to send for round robin policy std::vector dest_endpoints; std::map dest2Socket; @@ -159,13 +158,13 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(const int dGroup_id, ff_endpoint dest_endpoint, int coreid=-1) - : distibutedGroupId(dGroup_id),coreid(coreid) { + ff_dsender(ff_endpoint dest_endpoint, int coreid=-1) + : coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender(const int dGroup_id, std::vector dest_endpoints_, int coreid=-1) - : distibutedGroupId(dGroup_id), dest_endpoints(std::move(dest_endpoints_)),coreid(coreid) {} + ff_dsender( std::vector dest_endpoints_, int coreid=-1) + : dest_endpoints(std::move(dest_endpoints_)),coreid(coreid) {} int svc_init() { if (coreid!=-1) diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 433a9403..7231a5f5 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -5,7 +5,7 @@ #include #include #include -#include "ff_network.hpp" +#include #include #include #include diff --git a/ff/distributed/loader/Makefile b/ff/distributed/loader/Makefile new file mode 100644 index 00000000..508f842b --- /dev/null +++ b/ff/distributed/loader/Makefile @@ -0,0 +1,72 @@ +# --------------------------------------------------------------------------- +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception, you may use this file as part of a free software +# library without restriction. Specifically, if other files instantiate +# templates or use macros or inline functions from this file, or you compile +# this file and link it with other files to produce an executable, this +# file does not by itself cause the resulting executable to be covered by +# the GNU General Public License. This exception does not however +# invalidate any other reasons why the executable file might be covered by +# the GNU General Public License. +# +# --------------------------------------------------------------------------- + +CXXFLAGS += -std=c++17 +ifdef DEBUG + OPTIMIZE_FLAGS += -g -fno-inline-functions +else + OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG +endif + +ifdef CEREAL_HOME + INCS += -I$(CEREAL_HOME) +else + INCS += -I ~/cereal/include +endif + + +CXXFLAGS += -Wall +LIBS = -pthread +INCLUDES = $(INCS) + +SOURCES = $(wildcard *.cpp) +TARGET = $(SOURCES:.cpp=) + +.DEFAULT_GOAL := dff_run +.PHONY: all clean cleanall +.SUFFIXES: .c .cpp .o + + +%.d: %.cpp + set -e; $(CXX) -MM $(INCLUDES) $(CXXFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.d: %.c + set -e; $(CC) -MM $(INCLUDES) $(CFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< +%: %.cpp + $(CXX) $(INCLUDES) $(CXXFLAGS) $(OPTIMIZE_FLAGS) -o $@ $< $(LDFLAGS) $(LIBS) + +all: $(TARGET) + +clean: + -rm -fr *.o *~ +cleanall: clean + -rm -fr $(TARGET) *.d ./socket* + +include $(OBJS:.o=.d) diff --git a/ff/distributed/loader/loader_v1.cpp b/ff/distributed/loader/dff_run.cpp similarity index 83% rename from ff/distributed/loader/loader_v1.cpp rename to ff/distributed/loader/dff_run.cpp index 5ee5eb3b..512f2b61 100644 --- a/ff/distributed/loader/loader_v1.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -1,10 +1,21 @@ #include #include +#include +#include #include -#include <../external/cereal/cereal.hpp> -#include <../external/cereal/archives/json.hpp> -#include <../external/cereal/types/string.hpp> -#include <../external/cereal/types/vector.hpp> +#include + +#include +#include +#include +#include + + +static inline unsigned long getusec() { + struct timeval tv; + gettimeofday(&tv,NULL); + return (unsigned long)(tv.tv_sec*1e6+tv.tv_usec); +} std::string configFile; std::string executable; @@ -99,8 +110,10 @@ int main(int argc, char** argv) { std::ifstream is(configFile); - if (!is) throw std::runtime_error("Unable to open configuration file for the program!"); - + if (!is){ + std::cerr << "Unable to open configuration file for the program!" << std::endl; + return -1; + } cereal::JSONInputArchive ar(is); std::vector parsedGroups; @@ -117,9 +130,10 @@ int main(int argc, char** argv) { std::cout << "Group: " << g.name << " on host " << g.host << std::endl; #endif + auto Tstart = getusec(); + for (G& g : parsedGroups) g.run(); - while(!allTerminated(parsedGroups)){ for(G& g : parsedGroups){ @@ -135,10 +149,13 @@ int main(int argc, char** argv) { } } - sleep(0.20); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } - std::cout << "Everything terminated correctly" << std::endl; + std::cout << "Everything terminated correctly!" << std::endl; + std::cout << "Elapsed time: " << (getusec()-(Tstart))/1000 << " ms" << std::endl; + return 0; } \ No newline at end of file diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index 04106851..4e0f56ec 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -23,7 +23,7 @@ # # --------------------------------------------------------------------------- -CXXFLAGS += -std=c++17 +CXXFLAGS += -std=c++17 ifdef DEBUG OPTIMIZE_FLAGS += -g -fno-inline-functions else @@ -35,17 +35,16 @@ endif ifdef FF_HOME INCS += -I$(FF_HOME) else - INCS += -I ~/SPM/fastflow + INCS += -I ~/fastflow endif ifdef CEREAL_HOME INCS += -I$(CEREAL_HOME) else - INCS += -I ~/SPM/cereal + INCS += -I ~/cereal endif CXXFLAGS += -Wall -INCS += -I../src LIBS = -pthread INCLUDES = $(INCS) diff --git a/tests/distributed/test_complete.cpp b/tests/distributed/test_complete.cpp index ba673297..1ffa8c03 100644 --- a/tests/distributed/test_complete.cpp +++ b/tests/distributed/test_complete.cpp @@ -2,8 +2,6 @@ #include #include - - #define ITEMS 100 std::mutex mtx; // used only for pretty printing From e8b75da07d81cc22c4603f53a0123801ca2f2373 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 15 Mar 2021 08:59:43 +0100 Subject: [PATCH 024/202] minor fixes, mainly about error messages --- ff/distributed/ff_dgroups.hpp | 8 ++-- ff/distributed/ff_dreceiver.hpp | 20 ++++---- ff/distributed/ff_dsender.hpp | 8 ++-- tests/distributed/test_complete.cpp | 71 +++++++++++++++++++++------- tests/distributed/test_complete.json | 4 +- 5 files changed, 73 insertions(+), 38 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 2db1a461..6ad50eb3 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -38,7 +38,7 @@ class dGroups { int run_and_wait_end(ff_node* parent){ if (groups.find(runningGroup) == groups.end()){ - ff::error("The group specified is not found nor implemented!"); + ff::error("The group specified is not found nor implemented!\n"); return -1; } @@ -152,12 +152,12 @@ int DFF_Init(int &argc, char **&argv){ } if (configFile.empty()){ - ff::error("Config file not passed as argument!"); + ff::error("Config file not passed as argument!\nUse option --DFF_Config=\"config-file-name\"\n"); return -1; } if (groupName.empty()){ - ff::error("Group not passed as argument!"); + ff::error("Group not passed as argument!\nUse option --DFF_GName=\"group-name\"\n"); return -1; } @@ -178,4 +178,4 @@ int DFF_Init(int &argc, char **&argv){ } -#endif \ No newline at end of file +#endif diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index d67cf869..ff9c6cc6 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -38,7 +38,7 @@ class ff_dreceiver: public ff_monode_t { iov[0].iov_len = sizeof(sz); if (writevn(sck, iov, 1) < 0 || writen(sck, buff.getPtr(), buff.getLen()) < 0){ - error("Error writing on socket the routing Table"); + error("Error writing on socket the routing Table\n"); return -1; } @@ -58,7 +58,7 @@ class ff_dreceiver: public ff_monode_t { iov[2].iov_len = sizeof(sz); switch (readvn(sck, iov, 3)) { - case -1: error("Error reading from socket"); // fatal error + case -1: error("Error reading from socket\n"); // fatal error case 0: return -1; // connection close } @@ -71,7 +71,7 @@ class ff_dreceiver: public ff_monode_t { char* buff = new char [sz]; assert(buff); if(readn(sck, buff, sz) < 0){ - error("Error reading from socket"); + error("Error reading from socket\n"); delete [] buff; return -1; } @@ -101,7 +101,7 @@ class ff_dreceiver: public ff_monode_t { #ifdef LOCAL if ((listen_sck=socket(AF_LOCAL, SOCK_STREAM, 0)) < 0){ - error("Error creating the socket"); + error("Error creating the socket\n"); return -1; } @@ -113,14 +113,14 @@ class ff_dreceiver: public ff_monode_t { #ifdef REMOTE if ((listen_sck=socket(AF_INET, SOCK_STREAM, 0)) < 0){ - error("Error creating the socket"); + error("Error creating the socket\n"); return -1; } int enable = 1; // enable the reuse of the address if (setsockopt(listen_sck, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - error("setsockopt(SO_REUSEADDR) failed"); + error("setsockopt(SO_REUSEADDR) failed\n"); struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; @@ -130,12 +130,12 @@ class ff_dreceiver: public ff_monode_t { #endif if (bind(listen_sck, (struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0){ - error("Error binding"); + error("Error binding\n"); return -1; } if (listen(listen_sck, MAXBACKLOG) < 0){ - error("Error listening"); + error("Error listening\n"); return -1; } @@ -176,7 +176,7 @@ class ff_dreceiver: public ff_monode_t { tmpset = set; switch(select(fdmax+1, &tmpset, NULL, NULL, NULL)){ - case -1: error("Error on selecting socket"); return EOS; + case -1: error("Error on selecting socket\n"); return EOS; case 0: continue; } @@ -186,7 +186,7 @@ class ff_dreceiver: public ff_monode_t { if (i == this->listen_sck) { int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); if (connfd == -1){ - error("Error accepting client"); + error("Error accepting client\n"); } else { FD_SET(connfd, &set); if(connfd > fdmax) fdmax = connfd; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 94658252..86fd9e31 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -44,7 +44,7 @@ class ff_dsender: public ff_minode_t { assert(buff); if(readn(sck, buff, sz) < 0){ - error("Error reading from socket"); + error("Error reading from socket\n"); delete [] buff; return -1; } @@ -144,12 +144,12 @@ class ff_dsender: public ff_minode_t { iov[2].iov_len = sizeof(sz); if (writevn(sck, iov, 3) < 0){ - error("Error writing on socket header"); + error("Error writing on socket header\n"); return -1; } if (writen(sck, task->data.getPtr(), task->data.getLen()) < 0){ - error("Error writing on socket data"); + error("Error writing on socket data\n"); return -1; } @@ -209,4 +209,4 @@ class ff_dsender: public ff_minode_t { }; -#endif \ No newline at end of file +#endif diff --git a/tests/distributed/test_complete.cpp b/tests/distributed/test_complete.cpp index 1ffa8c03..6b66d401 100644 --- a/tests/distributed/test_complete.cpp +++ b/tests/distributed/test_complete.cpp @@ -1,3 +1,27 @@ +/* + * FastFlow concurrent network: + * + * |--> MiNode -->| + * MoNode-->| | + * Source --> |--> MiNode -->|----> Sink + * MoNode-->| | + * |--> MiNode -->| + * + * /<--------- a2a -------->/ + * /<------------ pipe1 -------------->/ /<- pipe2 ->/ + * /<-------------------- pipeMain ------------------>/ + * + * + * distributed version: + * + * -------- -------- + * | pipe1 | ----> | pipe2 | + * | | | | + * -------- -------- + * G1 G2 + * + */ + #include #include #include @@ -7,16 +31,16 @@ std::mutex mtx; // used only for pretty printing using namespace ff; -struct Source : ff::ff_monode_t{ +struct Source : ff_monode_t{ int* svc(int* i){ for(int i=0; i< ITEMS; i++) ff_send_out(new int(i)); - return this->EOS; + return EOS; } }; -struct MoNode : ff::ff_monode_t{ +struct MoNode : ff_monode_t{ int processedItems = 0; int* svc(int* i){ ++processedItems; @@ -29,7 +53,7 @@ struct MoNode : ff::ff_monode_t{ } }; -struct MiNode : ff::ff_minode_t{ +struct MiNode : ff_minode_t{ int processedItems = 0; int* svc(int* i){ ++processedItems; @@ -42,12 +66,12 @@ struct MiNode : ff::ff_minode_t{ } }; -struct Sink : ff::ff_minode_t{ +struct Sink : ff_minode_t{ int sum = 0; int* svc(int* i){ sum += *i; delete i; - return this->GO_ON; + return GO_ON; } void svc_end() { @@ -61,14 +85,21 @@ struct Sink : ff::ff_minode_t{ int main(int argc, char*argv[]){ - DFF_Init(argc, argv); + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + // defining the concurrent network ff_pipeline mainPipe; - ff::ff_a2a a2a; + ff_a2a a2a; Source s; - Sink sink; - ff_Pipe sp(s, a2a); - ff_Pipe sinkp(sink); + ff_pipeline sp; + sp.add_stage(&s); + sp.add_stage(&a2a); + ff_pipeline sinkp; + Sink sink; + sinkp.add_stage(&sink); mainPipe.add_stage(&sp); mainPipe.add_stage(&sinkp); @@ -77,17 +108,21 @@ int main(int argc, char*argv[]){ a2a.add_firstset({&sx1, &sx2, &sx3}); a2a.add_secondset({&dx1, &dx2, &dx3}); + // ----------------------- - - + // defining the distributed groups dGroup g1 = sp.createGroup("G1"); - dGroup g3 = sinkp.createGroup("G3"); - + dGroup g3 = sinkp.createGroup("G2"); g1.out << &dx1 << &dx2 << &dx3; g3.in << &sink; - - mainPipe.run_and_wait_end(); + // ---------------------- + + // running the distributed groups + if (mainPipe.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } return 0; -} \ No newline at end of file +} diff --git a/tests/distributed/test_complete.json b/tests/distributed/test_complete.json index f1e030f9..fab97f4a 100644 --- a/tests/distributed/test_complete.json +++ b/tests/distributed/test_complete.json @@ -3,10 +3,10 @@ { "endpoint" : "localhost:8004", "name" : "G1", - "OConn" : ["G3"] + "OConn" : ["G2"] }, { - "name" : "G3", + "name" : "G2", "endpoint": "localhost:8005" } ] From f2c6b0c1933872074bd7ff74a1908ad13eab2b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 15 Mar 2021 19:12:32 +0100 Subject: [PATCH 025/202] Minor fixes --- ff/distributed/ff_dgroup.hpp | 20 +++++--------------- ff/distributed/ff_dsender.hpp | 7 +++++-- ff/distributed/ff_network.hpp | 4 ---- ff/distributed/loader/dff_run.cpp | 9 +++++++++ 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 5f442e93..25a4bf9e 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -69,11 +69,6 @@ class dGroup : public ff_farm { bool isSource(){return in_.empty() && inout_.empty();} bool isSink(){return out_.empty() && inout_.empty();} -public: - dGroup(ff_node* parent, std::string label): parentStructure(parent), endpoint(), destinations(), expectedInputConnections(0), in(this), out(this){ - dGroups::Instance()->addGroup(label, this); - } - static bool isIncludedIn(const ff::svector& firstSet, std::vector& secondSet){ for (const ff_node* n : firstSet) if (std::find(secondSet.begin(), secondSet.end(), n) == secondSet.end()) @@ -271,22 +266,17 @@ class dGroup : public ff_farm { return 0; } - void print(){ - for(auto it = inout_.cbegin(); it != inout_.cend(); ++it) - { - std::cout << it->first << " " << it->second << std::endl; - } - } - ff_node* getWrapper(ff_node* n){ return this->inout_[n]; } - int run(bool skip_init=false) override { - // nothing to do - return 0; +public: + dGroup(ff_node* parent, std::string label): parentStructure(parent), endpoint(), destinations(), expectedInputConnections(0), in(this), out(this){ + dGroups::Instance()->addGroup(label, this); } + int run(bool skip_init=false) override {return 0;} + int run(ff_node* baseBB, bool skip_init=false) override { dGroups* groups_ = dGroups::Instance(); diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 86fd9e31..440f7422 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -124,7 +124,10 @@ class ff_dsender: public ff_minode_t { int fd, retries = 0; while((fd = this->create_connect(destination)) < 0 && ++retries < MAX_RETRIES) - std::this_thread::sleep_for(std::chrono::milliseconds((long)std::pow(2, retries))); + if (retries < 9) + std::this_thread::sleep_for(std::chrono::milliseconds((long)std::pow(2, retries))); + else + std::this_thread::sleep_for(std::chrono::milliseconds(200)); return fd; } @@ -171,7 +174,7 @@ class ff_dsender: public ff_minode_t { ff_mapThreadToCpu(coreid); for(size_t i=0; i < this->dest_endpoints.size(); i++) - sockets[i] = tryConnect(this->dest_endpoints[i]); + if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; return 0; } diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 2853eeb4..ffc10a89 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -15,10 +15,6 @@ //#define LOCAL #define REMOTE -#ifdef LOCAL - #define SOCKET_PATH "./socket_fd" -#endif - #define MAXBACKLOG 32 #define MAX_RETRIES 15 diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 512f2b61..66efe70f 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -77,6 +77,15 @@ bool allTerminated(std::vector& groups){ int main(int argc, char** argv) { + if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-help") == 0 || strcmp(argv[0], "-h")){ + std::cout << "USAGE: " << argv[0] << " [options] -f \n\n" + << "OPTIONS: \n" + << "\t -v ,..., \t Print the ouput of the g1 and g2 processes. If no groups are specified all are printed\n"; + + exit(EXIT_SUCCESS); + } + + std::vector viewGroups; int c; From 208674d8c5aa04a0edd3c36f32a6fa6e64bf5185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Tue, 23 Mar 2021 17:26:41 +0100 Subject: [PATCH 026/202] Fixed cereal types error in phase of annotation --- ff/distributed/ff_dgroup.hpp | 72 ++++++++++++++++++++++++----------- ff/distributed/ff_dsender.hpp | 2 +- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 25a4bf9e..f215ba38 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -13,6 +13,8 @@ #include #include +#include // used for operators constexpr evaulations + namespace ff{ @@ -322,9 +324,14 @@ MySet& MySet::operator<<(ff_node_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(node); - if (!handle.empty()) // the node is edge also in its output - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - else + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + } else this->group->in_.insert({node, {new WrapperIN(node, true), true}}); return *this; @@ -341,9 +348,14 @@ MySet& MySet::operator<<(ff_minode_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(node); - if (!handle.empty()) // the node is edge also in its output - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); - else + if (!handle.empty()){// the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + } else this->group->in_.insert({node, {new WrapperIN(node, true), true}}); return *this; @@ -352,17 +364,18 @@ MySet& MySet::operator<<(ff_minode_t* node){ template<> template MySet& MySet::operator<<(ff_monode_t* node){ - /*if (condizione){ - error("Errore!"); - throw - }*/ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(node); - if (!handle.empty()) // the node is edge also in its output - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - else { + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + } else { ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true); this->group->in_.insert({node, {combine, true}}); } @@ -376,9 +389,14 @@ MySet& MySet::operator<<(ff_node_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->in_.extract(node); - if (!handle.empty()) // the node is edge also in its input - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); - else + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + } else this->group->out_.insert({node, {new WrapperOUT(node, true), true}}); return *this; @@ -390,9 +408,14 @@ MySet& MySet::operator<<(ff_minode_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->in_.extract(node); - if (!handle.empty()) // the node is edge also in its input - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); - else { + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + } else { ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, true), false, true); this->group->out_.insert({node, {combine, true}}); } @@ -406,9 +429,14 @@ MySet& MySet::operator<<(ff_monode_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->in_.extract(node); - if (!handle.empty()) // the node is edge also in its input - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); - else + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); + } else this->group->out_.insert({node, {new WrapperOUT(node, true), true}}); return *this; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 440f7422..dec235ad 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -186,7 +186,7 @@ class ff_dsender: public ff_minode_t { } message_t *svc(message_t* task) { /* here i should send the task via socket */ - if (task->chid == -1){ // roundrobin over the destinationss + if (task->chid == -1){ // roundrobin over the destinations task->chid = next_rr_destination; next_rr_destination = (next_rr_destination + 1) % dest2Socket.size(); } From fbd377e8bab34de81f78863f0d1b395ee061c488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 24 Mar 2021 11:43:43 +0100 Subject: [PATCH 027/202] Rewritten DFF_Init to preserve all other short and long options not consumed by the function --- ff/distributed/ff_dgroups.hpp | 120 ++++++++++++++++------------------ 1 file changed, 55 insertions(+), 65 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 6ad50eb3..09b6501d 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -104,77 +104,67 @@ class dGroups { } }; -int DFF_Init(int &argc, char **&argv){ - int c; - std::string groupName, configFile; - - while (1){ - static struct option long_options[] = - { - {"DFF_GName", required_argument, 0, 'n'}, - {"DFF_Config", required_argument, 0, 'c'}, - {0, 0, 0, 0} - }; - - /* getopt_long stores the option index here. */ - int option_index = 0; - c = getopt_long(argc, argv, "", long_options, &option_index); - - /* Detect the end of the options. */ - if (c == -1) break; - - switch (c){ - case 0: - /* If this option set a flag, do nothing else now. */ - if (long_options[option_index].flag != 0) - break; - printf("option %s", long_options[option_index].name); - if (optarg) - printf(" with arg %s", optarg); - printf("\n"); - break; - - case 'c': - configFile = std::string(optarg); - break; - - case 'n': - groupName = std::string(optarg); - break; - - case '?': - /* getopt_long already printed an error message. */ - break; - - default: - return -1; - } +int DFF_Init(int& argc, char**& argv){ + + std::string configFile, groupName; + + for(int i = 0; i < argc; i++){ + if (strstr(argv[i], "--DFF_Config") != NULL){ + char * equalPosition = strchr(argv[i], '='); + if (equalPosition == NULL){ + // the option is in the next argument array position + configFile = std::string(argv[i+1]); + argv[i] = argv[i+1] = NULL; + i++; + } else { + // the option is in the next position of this string + configFile = std::string(++equalPosition); + argv[i] = NULL; } - - if (configFile.empty()){ - ff::error("Config file not passed as argument!\nUse option --DFF_Config=\"config-file-name\"\n"); - return -1; + continue; + } + + if (strstr(argv[i], "--DFF_GName") != NULL){ + char * equalPosition = strchr(argv[i], '='); + if (equalPosition == NULL){ + // the option is in the next argument array position + groupName = std::string(argv[i+1]); + argv[i] = argv[i+1] = NULL; + i++; + } else { + // the option is in the next position of this string + groupName = std::string(++equalPosition); + argv[i] = NULL; } + continue; + } + } - if (groupName.empty()){ - ff::error("Group not passed as argument!\nUse option --DFF_GName=\"group-name\"\n"); - return -1; - } + if (configFile.empty()){ + ff::error("Config file not passed as argument!\nUse option --DFF_Config=\"config-file-name\"\n"); + return -1; + } - dGroups::Instance()->setRunningGroup(groupName); - dGroups::Instance()->setConfigFile(configFile); + if (groupName.empty()){ + ff::error("Group not passed as argument!\nUse option --DFF_GName=\"group-name\"\n"); + return -1; + } - // if other arguments are passed, they are preserved! - if (optind <= argc){ - optind--; - char *exeName = argv[0]; - argc -= optind; - argv += optind; - argv[0] = exeName; - } + dGroups::Instance()->setRunningGroup(groupName); + dGroups::Instance()->setConfigFile(configFile); - return 0; - } + + // recompact the argv array + int j = 0; + for(int i = 0; i < argc; i++) + if (argv[i] != NULL) + argv[j++] = argv[i]; + + // update the argc value + argc = j; + + return 0; +} } From 046db71edce455a6ef878b5cf8cafdf318871349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 24 Mar 2021 18:43:58 +0100 Subject: [PATCH 028/202] Fixed bugs on dff_run, added -V to print outputs of all the processes, -v now require groupnames separated by comma. Added test_complete1. Changed the tryconnect policy, regulated by two macros on config.hpp: MAX_RETRIES and AGGRESSIVE_THRESHOLD. Implemented operators <<= for annoting mi/mo-node. --- ff/config.hpp | 13 +++ ff/distributed/ff_dgroup.hpp | 113 ++++++++++++++++++++++++-- ff/distributed/ff_dsender.hpp | 5 +- ff/distributed/ff_network.hpp | 4 - ff/distributed/loader/dff_run.cpp | 32 ++++---- tests/distributed/test_complete1.cpp | 110 +++++++++++++++++++++++++ tests/distributed/test_complete1.json | 18 ++++ 7 files changed, 267 insertions(+), 28 deletions(-) create mode 100644 tests/distributed/test_complete1.cpp create mode 100644 tests/distributed/test_complete1.json diff --git a/ff/config.hpp b/ff/config.hpp index b558cb07..e2c6c3cc 100644 --- a/ff/config.hpp +++ b/ff/config.hpp @@ -144,6 +144,19 @@ */ //#define FF_TASK_CALLBACK 1 +/* + ****** DISTRIBUTED SUPPORT PARAMETERS + */ +#define MAX_RETRIES 1500 +#define AGGRESSIVE_TRESHOLD 1000 + +#define MAXBACKLOG 32 + +/* + ****** END DISTRIBUTED VERSION PARAMETERS + */ + + #if defined(TRACE_FASTFLOW) #define FFTRACE(x) x #else diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f215ba38..8cadb2bd 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -45,6 +45,12 @@ class MySet { template MySet& operator<<=(ff_node_t*) {} + template + MySet& operator<<=(ff_minode_t*) {} + + template + MySet& operator<<=(ff_monode_t*) {} + bool check_inout(ff_node* node); }; @@ -344,7 +350,7 @@ MySet& MySet::operator<<(ff_minode_t* node){ error("Errore!"); throw }*/ - + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(node); @@ -449,23 +455,116 @@ MySet& MySet::operator<<=(ff_node_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(node); - if (!handle.empty()) // the node is edge also in its output - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*)new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - else + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + } else this->group->in_.insert({node, {new WrapperIN(node, true), false}}); return *this; } + +template<> +template +MySet& MySet::operator<<=(ff_minode_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(node); + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + + } else + this->group->in_.insert({node, {new WrapperIN(node, true), false}}); + + return *this; +} + +template<> +template +MySet& MySet::operator<<=(ff_monode_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(node); + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + + } else { + ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true); + this->group->in_.insert({node, {combine, false}}); + } + + return *this; +} + template<> template MySet& MySet::operator<<=(ff_node_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->in_.extract(node); - if (!handle.empty()) // the node is edge also in its input - this->group->inout_.insert({node, handle.mapped().second ? (ff_node*) new WrapperINOUT(node, true) : (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); - else + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + } else + this->group->out_.insert({node, {new WrapperOUT(node, true), false}}); + + return *this; +} + +template<> +template +MySet& MySet::operator<<=(ff_minode_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(node); + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + } else { + ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, true), false, true); + this->group->out_.insert({node, {combine, false}}); + } + + return *this; +} + +template<> +template +MySet& MySet::operator<<=(ff_monode_t* node){ + if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(node); + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, true)}); + } + } else + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); + } else this->group->out_.insert({node, {new WrapperOUT(node, true), false}}); return *this; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index dec235ad..66149481 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -124,10 +124,11 @@ class ff_dsender: public ff_minode_t { int fd, retries = 0; while((fd = this->create_connect(destination)) < 0 && ++retries < MAX_RETRIES) - if (retries < 9) - std::this_thread::sleep_for(std::chrono::milliseconds((long)std::pow(2, retries))); + if (retries < AGGRESSIVE_TRESHOLD) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); else std::this_thread::sleep_for(std::chrono::milliseconds(200)); + //std::this_thread::sleep_for(std::chrono::milliseconds((long)std::pow(2, retries - AGGRESSIVE_TRESHOLD))); return fd; } diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index ffc10a89..e2dc7482 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -15,10 +15,6 @@ //#define LOCAL #define REMOTE -#define MAXBACKLOG 32 -#define MAX_RETRIES 15 - - #ifdef __APPLE__ #include #define htobe16(x) OSSwapHostToBigInt16(x) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 66efe70f..e6179966 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -4,6 +4,8 @@ #include #include #include +#include + #include #include @@ -47,12 +49,10 @@ struct G { host = "127.0.0.1"; // set the host to localhost if not found in config file! ar.setNextName(nullptr); } - - std::cout << "Host: " << host << std::endl; } void run(){ - char b[100]; // ssh -t + char b[150]; // ssh -t sprintf(b, " %s %s %s --DFF_Config=%s --DFF_GName=%s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : "") , executable.c_str(), configFile.c_str(), this->name.c_str()); std::cout << "Executing the following string: " << b << std::endl; fd = popen(b, "r"); @@ -77,7 +77,7 @@ bool allTerminated(std::vector& groups){ int main(int argc, char** argv) { - if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-help") == 0 || strcmp(argv[0], "-h")){ + if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-help") == 0 || strcmp(argv[0], "-h") == 0){ std::cout << "USAGE: " << argv[0] << " [options] -f \n\n" << "OPTIONS: \n" << "\t -v ,..., \t Print the ouput of the g1 and g2 processes. If no groups are specified all are printed\n"; @@ -87,32 +87,34 @@ int main(int argc, char** argv) { std::vector viewGroups; + bool seeAll = false; int c; - while ((c = getopt (argc, argv, ":f:v:")) != -1) + while ((c = getopt(argc, argv, "Vv:f:")) != -1){ + std::cout << (char)c << std::endl; switch (c){ case 'f': configFile = std::string(optarg); + std::cout << configFile << std::endl; break; - case 'v': - viewGroups = split(optarg, ','); + case 'V': + seeAll = true; break; - case ':': - if (optopt == 'v'){ - // see all - } + case 'v': + viewGroups = split(optarg, ','); break; case '?': if (optopt == 'f') - fprintf (stderr, "Option -%c requires an argument.\n", optopt); + printf ("Option -%c requires an argument.\n", optopt); else if (isprint (optopt)) - fprintf (stderr, "Unknown option `-%c'.\n", optopt); + printf ("Unknown option `-%c'.\n", optopt); else - fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); + printf ("Unknown option character `\\x%x'.\n", optopt); return 1; default: abort(); } + } for (int index = optind; index < argc; index++) executable += std::string(argv[index]) + " "; @@ -152,7 +154,7 @@ int main(int argc, char** argv) { if (result == NULL){ pclose(g.fd); g.fd = nullptr; } else { - if (find(viewGroups.begin(), viewGroups.end(), g.name) != viewGroups.end()) + if (seeAll || find(viewGroups.begin(), viewGroups.end(), g.name) != viewGroups.end()) std::cout << "[" << g.name << "]" << buff; } } diff --git a/tests/distributed/test_complete1.cpp b/tests/distributed/test_complete1.cpp new file mode 100644 index 00000000..5e49da1c --- /dev/null +++ b/tests/distributed/test_complete1.cpp @@ -0,0 +1,110 @@ +/* + * FastFlow concurrent network: + * + * |--> MiNode -->| + * MoNode-->| | + * Source --> |--> MiNode -->|----> Sink + * MoNode-->| | + * |--> MiNode -->| + * + * /<--------- a2a -------->/ + * /<-------------------- pipeMain ------------------>/ + */ + + +#include +#include +#include + +#define ITEMS 100 +std::mutex mtx; // used only for pretty printing + +struct Source : ff::ff_monode_t{ + int* svc(int* i){ + for(int i=0; i< ITEMS; i++) + ff_send_out(new int(i)); + + return this->EOS; + } +}; + +struct MoNode : ff::ff_monode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct MiNode : ff::ff_minode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct Sink : ff::ff_minode_t{ + int sum = 0; + int* svc(int* i){ + sum += *i; + delete i; + return this->GO_ON; + } + + void svc_end() { + int local_sum = 0; + for(int i = 0; i < ITEMS; i++) local_sum += i; + const std::lock_guard lock(mtx); + std::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; + } +}; + + +int main(int argc, char*argv[]){ + + DFF_Init(argc, argv); + + ff_pipeline mainPipe; + ff::ff_a2a a2a; + Source s; + Sink sink; + ff_Pipe sp(s); + ff_Pipe sinkp(sink); + mainPipe.add_stage(&sp); + mainPipe.add_stage(&a2a); + mainPipe.add_stage(&sinkp); + + MoNode sx1, sx2, sx3; + MiNode dx1, dx2, dx3; + + a2a.add_firstset({&sx1, &sx2, &sx3}); + a2a.add_secondset({&dx1, &dx2, &dx3}); + + //mainPipe.run_and_wait_end(); + + auto g1 = sp.createGroup("G1"); + auto g2 = a2a.createGroup("G2"); + auto g3 = sinkp.createGroup("G3"); + + + g1.out << &s; + g2.in << &sx1 << &sx2 << &sx3; g2.out << &dx1 << &dx2 << &dx3; + g3.in << &sink; + + if (mainPipe.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } + +} \ No newline at end of file diff --git a/tests/distributed/test_complete1.json b/tests/distributed/test_complete1.json new file mode 100644 index 00000000..beae1ddf --- /dev/null +++ b/tests/distributed/test_complete1.json @@ -0,0 +1,18 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G3"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006" + } + ] +} From dbc375cf24a19b8a5d969351a71d082bd0dff3fd Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 25 Mar 2021 07:19:26 +0100 Subject: [PATCH 029/202] new test --- tests/distributed/test_complete2.cpp | 107 ++++++++++++++++++++++++++ tests/distributed/test_complete2.json | 13 ++++ 2 files changed, 120 insertions(+) create mode 100644 tests/distributed/test_complete2.cpp create mode 100644 tests/distributed/test_complete2.json diff --git a/tests/distributed/test_complete2.cpp b/tests/distributed/test_complete2.cpp new file mode 100644 index 00000000..64d88a47 --- /dev/null +++ b/tests/distributed/test_complete2.cpp @@ -0,0 +1,107 @@ +/* + * + * + * Node1-->Node2 --> Node3 --> Node4 + * + * /<-- pipe0-->/ /<---pipe1--->/ + * /<----------- pipe ------------>/ + * + * + */ + + +#include +#include + +using namespace ff; + +struct task_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + task_t* svc(task_t*){ + for(long i=0; i< ntasks; i++) { + task_t* task = new task_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + task_t* svc(task_t* t){ + t->str += std::string(" World"); + std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + + return t; + } +}; + +struct Node3: ff_minode_t{ + task_t* svc(task_t* t){ + t->S.t += 1; + t->S.f += 1.0; + return t; + } +}; + +struct Node4: ff_node_t{ + task_t* svc(task_t* t){ + std::cout << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + delete t; + return GO_ON; + } +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + + long ntasks = 100; + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2; + Node3 n3; + Node4 n4; + ff_pipeline pipe0; + pipe0.add_stage(&n1); + pipe0.add_stage(&n2); + ff_pipeline pipe1; + pipe1.add_stage(&n3); + pipe1.add_stage(&n4); + pipe.add_stage(&pipe0); + pipe.add_stage(&pipe1); + + auto G1 = pipe0.createGroup("G1"); + auto G2 = pipe1.createGroup("G2"); + + G1.out << &n2; + G2.in << &n3; + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete2.json b/tests/distributed/test_complete2.json new file mode 100644 index 00000000..fab97f4a --- /dev/null +++ b/tests/distributed/test_complete2.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} From cf76289e5861721e30010c1d971883c5041c59f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 25 Mar 2021 10:13:05 +0100 Subject: [PATCH 030/202] Fixed parsing of invalid json file, now the thrown exception is handled --- ff/distributed/ff_dgroup.hpp | 5 ++--- ff/distributed/loader/dff_run.cpp | 9 ++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 8cadb2bd..e747063b 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -582,15 +582,14 @@ void dGroups::parseConfig(){ std::ifstream is(this->configFilePath); if (!is) throw FF_Exception("Unable to open configuration file for the program!"); - - cereal::JSONInputArchive ari(is); std::vector parsedGroups; try { + cereal::JSONInputArchive ari(is); ari(cereal::make_nvp("groups", parsedGroups)); } catch (const cereal::Exception& e){ - std::cerr << e.what(); + std::cerr << "Error parsing the JSON config file. Check syntax and structure of the file and retry!" << std::endl; exit(EXIT_FAILURE); } diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index e6179966..ea30af89 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -91,11 +91,10 @@ int main(int argc, char** argv) { int c; while ((c = getopt(argc, argv, "Vv:f:")) != -1){ - std::cout << (char)c << std::endl; + switch (c){ case 'f': configFile = std::string(optarg); - std::cout << configFile << std::endl; break; case 'V': seeAll = true; @@ -124,15 +123,15 @@ int main(int argc, char** argv) { if (!is){ std::cerr << "Unable to open configuration file for the program!" << std::endl; return -1; - } - cereal::JSONInputArchive ar(is); + } std::vector parsedGroups; try { + cereal::JSONInputArchive ar(is); ar(cereal::make_nvp("groups", parsedGroups)); } catch (const cereal::Exception& e){ - std::cerr << e.what(); + std::cerr << "Error parsing the JSON config file. Check syntax and structure of the file and retry!" << std::endl; exit(EXIT_FAILURE); } From f12bec0dc4033d7816d206ba21b6ddd80d1dcb7e Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 25 Mar 2021 14:59:26 +0100 Subject: [PATCH 031/202] new test --- tests/distributed/test_complete3.cpp | 105 ++++++++++++++++++++++++++ tests/distributed/test_complete3.json | 13 ++++ 2 files changed, 118 insertions(+) create mode 100644 tests/distributed/test_complete3.cpp create mode 100644 tests/distributed/test_complete3.json diff --git a/tests/distributed/test_complete3.cpp b/tests/distributed/test_complete3.cpp new file mode 100644 index 00000000..814accd6 --- /dev/null +++ b/tests/distributed/test_complete3.cpp @@ -0,0 +1,105 @@ +/* + * + * | -> Node3 -->| + * Node1-->Node2 -->| -> Node3 -->| -> Node4 + * | -> Node3 -->| + * /<-- pipe0-->/ /<--------- a2a -------->/ + * G1 G2 + * /<----------------- pipe ---------------->/ + * + * + */ + + +#include +#include + +using namespace ff; + +struct task_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + task_t* svc(task_t*){ + for(long i=0; i< ntasks; i++) { + task_t* task = new task_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + task_t* svc(task_t* t){ + t->str += std::string(" World"); + return t; + } +}; + +struct Node3: ff_monode_t{ + task_t* svc(task_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + task_t* svc(task_t* t){ + std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + delete t; + return GO_ON; + } +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + + long ntasks = 100; + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2; + Node3 n31, n32, n33; + Node4 n4; + ff_pipeline pipe0; + pipe0.add_stage(&n1); + pipe0.add_stage(&n2); + ff_a2a a2a; + a2a.add_firstset({&n31, &n32, &n33}); + a2a.add_secondset({&n4}); + pipe.add_stage(&pipe0); + pipe.add_stage(&a2a); + + auto G1 = pipe0.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + + G1.out << &n2; + G2.in << &n31 << &n32 << &n33; + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete3.json b/tests/distributed/test_complete3.json new file mode 100644 index 00000000..fab97f4a --- /dev/null +++ b/tests/distributed/test_complete3.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} From 83352a018a3db4ea98cca6cb6884f1bbec729f2b Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 25 Mar 2021 15:55:59 +0100 Subject: [PATCH 032/202] added test --- tests/distributed/test_complete2.cpp | 20 ++--- tests/distributed/test_complete3.cpp | 39 +++++--- tests/distributed/test_complete4.cpp | 125 ++++++++++++++++++++++++++ tests/distributed/test_complete4.json | 13 +++ 4 files changed, 173 insertions(+), 24 deletions(-) create mode 100644 tests/distributed/test_complete4.cpp create mode 100644 tests/distributed/test_complete4.json diff --git a/tests/distributed/test_complete2.cpp b/tests/distributed/test_complete2.cpp index 64d88a47..3de01609 100644 --- a/tests/distributed/test_complete2.cpp +++ b/tests/distributed/test_complete2.cpp @@ -15,7 +15,7 @@ using namespace ff; -struct task_t { +struct myTask_t { std::string str; struct S_t { long t; @@ -30,11 +30,11 @@ struct task_t { }; -struct Node1: ff_monode_t{ +struct Node1: ff_monode_t{ Node1(long ntasks):ntasks(ntasks) {} - task_t* svc(task_t*){ + myTask_t* svc(myTask_t*){ for(long i=0; i< ntasks; i++) { - task_t* task = new task_t; + myTask_t* task = new myTask_t; task->str="Hello"; task->S.t = i; task->S.f = i*1.0; @@ -45,8 +45,8 @@ struct Node1: ff_monode_t{ const long ntasks; }; -struct Node2: ff_node_t{ - task_t* svc(task_t* t){ +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ t->str += std::string(" World"); std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; @@ -54,16 +54,16 @@ struct Node2: ff_node_t{ } }; -struct Node3: ff_minode_t{ - task_t* svc(task_t* t){ +struct Node3: ff_minode_t{ + myTask_t* svc(myTask_t* t){ t->S.t += 1; t->S.f += 1.0; return t; } }; -struct Node4: ff_node_t{ - task_t* svc(task_t* t){ +struct Node4: ff_node_t{ + myTask_t* svc(myTask_t* t){ std::cout << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; delete t; return GO_ON; diff --git a/tests/distributed/test_complete3.cpp b/tests/distributed/test_complete3.cpp index 814accd6..285fa916 100644 --- a/tests/distributed/test_complete3.cpp +++ b/tests/distributed/test_complete3.cpp @@ -16,7 +16,7 @@ using namespace ff; -struct task_t { +struct myTask_t { std::string str; struct S_t { long t; @@ -30,11 +30,11 @@ struct task_t { }; -struct Node1: ff_monode_t{ +struct Node1: ff_monode_t{ Node1(long ntasks):ntasks(ntasks) {} - task_t* svc(task_t*){ + myTask_t* svc(myTask_t*){ for(long i=0; i< ntasks; i++) { - task_t* task = new task_t; + myTask_t* task = new myTask_t; task->str="Hello"; task->S.t = i; task->S.f = i*1.0; @@ -45,27 +45,36 @@ struct Node1: ff_monode_t{ const long ntasks; }; -struct Node2: ff_node_t{ - task_t* svc(task_t* t){ +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ t->str += std::string(" World"); return t; } }; -struct Node3: ff_monode_t{ - task_t* svc(task_t* t){ +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ t->S.t += get_my_id(); t->S.f += get_my_id()*1.0; return t; } }; -struct Node4: ff_minode_t{ - task_t* svc(task_t* t){ - std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; delete t; return GO_ON; } + void svc_end() { + if (processed != ntasks) { + abort(); + } + } + long ntasks; + long processed=0; }; @@ -73,15 +82,17 @@ int main(int argc, char*argv[]){ if (DFF_Init(argc, argv)<0 ) { error("DFF_Init\n"); return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); } - - long ntasks = 100; ff_pipeline pipe; Node1 n1(ntasks); Node2 n2; Node3 n31, n32, n33; - Node4 n4; + Node4 n4(ntasks); ff_pipeline pipe0; pipe0.add_stage(&n1); pipe0.add_stage(&n2); diff --git a/tests/distributed/test_complete4.cpp b/tests/distributed/test_complete4.cpp new file mode 100644 index 00000000..eea3da17 --- /dev/null +++ b/tests/distributed/test_complete4.cpp @@ -0,0 +1,125 @@ +/* + * |-> Helper->Node2 -->| |-> Helper->Node3 -->| + * | | | | + * Node1-->| | -> |-> Helper->Node3 -->| -> Node4 + * |-> Helper->Node2 -->| | | + * |-> Helper->Node3 -->| + * + * /<---------- a2a0 ---------->/ /<------------ a2a ------------>/ + * G1 G2 + * /<---------------------------- pipe ---------------------------->/ + * + * + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World") + std::to_string(get_my_id()); + return t; + } +}; + +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + } + long ntasks; + long processed=0; +}; + +struct Helper: ff_minode_t { + myTask_t* svc(myTask_t* t) { return t; } +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Helper h21, h22; + Node2 n21, n22; + Helper h31, h32, h33; + Node3 n31, n32, n33; + Node4 n4(ntasks); + ff_a2a a2a0; + a2a0.add_firstset({&n1}); + a2a0.add_secondset({new ff_comb(&h21, &n21), new ff_comb(&h22, &n22)}); + ff_a2a a2a1; + a2a1.add_firstset({new ff_comb(&h31, &n31), new ff_comb(&h32, &n32), new ff_comb(&h33, &n33)}); + a2a1.add_secondset({&n4}); + pipe.add_stage(&a2a0); + pipe.add_stage(&a2a1); + + auto G1 = a2a0.createGroup("G1"); + auto G2 = a2a1 + .createGroup("G2"); + + G1.out << &n21 << &n22; + G2.in << &h31 << &h32 << &h33; + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete4.json b/tests/distributed/test_complete4.json new file mode 100644 index 00000000..fab97f4a --- /dev/null +++ b/tests/distributed/test_complete4.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} From ee983375e0c606e38877ca8fc859b1f31297a7ae Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 26 Mar 2021 08:58:23 +0100 Subject: [PATCH 033/202] new test --- tests/distributed/test_complete4.cpp | 7 +- tests/distributed/test_complete5.cpp | 137 ++++++++++++++++++++++++++ tests/distributed/test_complete5.json | 33 +++++++ 3 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 tests/distributed/test_complete5.cpp create mode 100644 tests/distributed/test_complete5.json diff --git a/tests/distributed/test_complete4.cpp b/tests/distributed/test_complete4.cpp index eea3da17..7ef77c8e 100644 --- a/tests/distributed/test_complete4.cpp +++ b/tests/distributed/test_complete4.cpp @@ -103,16 +103,15 @@ int main(int argc, char*argv[]){ Node4 n4(ntasks); ff_a2a a2a0; a2a0.add_firstset({&n1}); - a2a0.add_secondset({new ff_comb(&h21, &n21), new ff_comb(&h22, &n22)}); + a2a0.add_secondset({new ff_comb(&h21, &n21), new ff_comb(&h22, &n22)}, true); ff_a2a a2a1; - a2a1.add_firstset({new ff_comb(&h31, &n31), new ff_comb(&h32, &n32), new ff_comb(&h33, &n33)}); + a2a1.add_firstset({new ff_comb(&h31, &n31), new ff_comb(&h32, &n32), new ff_comb(&h33, &n33)}, 0, true); a2a1.add_secondset({&n4}); pipe.add_stage(&a2a0); pipe.add_stage(&a2a1); auto G1 = a2a0.createGroup("G1"); - auto G2 = a2a1 - .createGroup("G2"); + auto G2 = a2a1.createGroup("G2"); G1.out << &n21 << &n22; G2.in << &h31 << &h32 << &h33; diff --git a/tests/distributed/test_complete5.cpp b/tests/distributed/test_complete5.cpp new file mode 100644 index 00000000..4c7a70de --- /dev/null +++ b/tests/distributed/test_complete5.cpp @@ -0,0 +1,137 @@ +/* + * |-> Helper->Node21 -->| |-> Helper->Node31 -->| + * | | | | + * Node1-->|-> Helper->Node22 -->| -> |-> Helper->Node32 -->| -> Node4 + * | | | | + * |-> Helper->Node23 -->| |-> Helper->Node33 -->| + * + * /<---------- a2a0 ---------->/ /<------------ a2a ------------>/ + * /<---------------------------- pipe ---------------------------->/ + * + * G1: Node1 + * G2: Helper->Node21 and Helper->Node22 + * G3: Helper->Node23 + * G4: Helper->Node31 + * G5: Helper->Node32 and Helper->Node33 + * G6: Node4 + */ + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World") + std::to_string(get_my_id()); + return t; + } +}; + +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + } + long ntasks; + long processed=0; +}; + +struct Helper: ff_minode_t { + myTask_t* svc(myTask_t* t) { return t; } +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Helper h21, h22, h23; + Node2 n21, n22, n23; + Helper h31, h32, h33; + Node3 n31, n32, n33; + Node4 n4(ntasks); + ff_a2a a2a0; + a2a0.add_firstset({&n1}); + a2a0.add_secondset({new ff_comb(&h21, &n21), new ff_comb(&h22, &n22), new ff_comb(&h23, &n23)}, true); + ff_a2a a2a1; + a2a1.add_firstset({new ff_comb(&h31, &n31), new ff_comb(&h32, &n32), new ff_comb(&h33, &n33)}, 0, true); + a2a1.add_secondset({&n4}); + pipe.add_stage(&a2a0); + pipe.add_stage(&a2a1); + + auto G1 = a2a0.createGroup("G1"); + auto G2 = a2a0.createGroup("G2"); + auto G3 = a2a0.createGroup("G3"); + auto G4 = a2a0.createGroup("G4"); + auto G5 = a2a0.createGroup("G5"); + auto G6 = a2a0.createGroup("G6"); + + /* ----------------- */ G1.out << &n1; + G2.in << &h21 << &h22; G2.out << &n21 << &n22; + G3.in << &h23; G3.out << &n23; + G4.in << &h31; G4.out << &n31; + G5.in << &h32 << &h33; G2.out << &n32 << &n33; + G6.in << &n4; /* ------------------- */ + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete5.json b/tests/distributed/test_complete5.json new file mode 100644 index 00000000..19cf2d9f --- /dev/null +++ b/tests/distributed/test_complete5.json @@ -0,0 +1,33 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G4", "G5"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006", + "OConn" : ["G4", "G5"] + }, + { + "name" : "G4", + "endpoint": "localhost:8007", + "OConn" : ["G6"] + }, + { + "name" : "G5", + "endpoint": "localhost:8008", + "OConn" : ["G6"] + }, + { + "name" : "G6", + "endpoint": "localhost:8007" + } + ] +} From 6a44a53867ded0c9c2c74391761ca8f9b24f003f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Fri, 26 Mar 2021 11:16:05 +0100 Subject: [PATCH 034/202] Fixed test_complete5. Dff_run now takes care to check the returned value of each process --- ff/distributed/loader/dff_run.cpp | 10 ++++++---- tests/distributed/test_complete5.cpp | 13 ++++++------- tests/distributed/test_complete5.json | 12 ++++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index ea30af89..9144fec4 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -151,7 +152,10 @@ int main(int argc, char** argv) { char buff[1024]; char* result = fgets(buff, sizeof(buff), g.fd); if (result == NULL){ - pclose(g.fd); g.fd = nullptr; + int code = pclose(g.fd); + if (WEXITSTATUS(code) != 0) + std::cout << "[" << g.name << "][ERR] Report an return code: " << WEXITSTATUS(code) << std::endl; + g.fd = nullptr; } else { if (seeAll || find(viewGroups.begin(), viewGroups.end(), g.name) != viewGroups.end()) std::cout << "[" << g.name << "]" << buff; @@ -159,11 +163,9 @@ int main(int argc, char** argv) { } } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - + std::this_thread::sleep_for(std::chrono::milliseconds(15)); } - std::cout << "Everything terminated correctly!" << std::endl; std::cout << "Elapsed time: " << (getusec()-(Tstart))/1000 << " ms" << std::endl; diff --git a/tests/distributed/test_complete5.cpp b/tests/distributed/test_complete5.cpp index 4c7a70de..040f5610 100644 --- a/tests/distributed/test_complete5.cpp +++ b/tests/distributed/test_complete5.cpp @@ -15,7 +15,6 @@ * G5: Helper->Node32 and Helper->Node33 * G6: Node4 */ - */ #include @@ -108,9 +107,9 @@ int main(int argc, char*argv[]){ Node4 n4(ntasks); ff_a2a a2a0; a2a0.add_firstset({&n1}); - a2a0.add_secondset({new ff_comb(&h21, &n21), new ff_comb(&h22, &n22), new ff_comb(&h23, &n23)}, true); + a2a0.add_secondset({new ff_comb(&h21, &n21), new ff_comb(&h22, &n22), new ff_comb(&h23, &n23)}); ff_a2a a2a1; - a2a1.add_firstset({new ff_comb(&h31, &n31), new ff_comb(&h32, &n32), new ff_comb(&h33, &n33)}, 0, true); + a2a1.add_firstset({new ff_comb(&h31, &n31), new ff_comb(&h32, &n32), new ff_comb(&h33, &n33)}); a2a1.add_secondset({&n4}); pipe.add_stage(&a2a0); pipe.add_stage(&a2a1); @@ -118,15 +117,15 @@ int main(int argc, char*argv[]){ auto G1 = a2a0.createGroup("G1"); auto G2 = a2a0.createGroup("G2"); auto G3 = a2a0.createGroup("G3"); - auto G4 = a2a0.createGroup("G4"); - auto G5 = a2a0.createGroup("G5"); - auto G6 = a2a0.createGroup("G6"); + auto G4 = a2a1.createGroup("G4"); + auto G5 = a2a1.createGroup("G5"); + auto G6 = a2a1.createGroup("G6"); /* ----------------- */ G1.out << &n1; G2.in << &h21 << &h22; G2.out << &n21 << &n22; G3.in << &h23; G3.out << &n23; G4.in << &h31; G4.out << &n31; - G5.in << &h32 << &h33; G2.out << &n32 << &n33; + G5.in << &h32 << &h33; G5.out << &n32 << &n33; G6.in << &n4; /* ------------------- */ if (pipe.run_and_wait_end()<0) { diff --git a/tests/distributed/test_complete5.json b/tests/distributed/test_complete5.json index 19cf2d9f..9330dfd6 100644 --- a/tests/distributed/test_complete5.json +++ b/tests/distributed/test_complete5.json @@ -3,31 +3,31 @@ { "endpoint" : "localhost:8004", "name" : "G1", - "OConn" : ["G2"] + "OConn" : ["G2", "G3"] }, { "name" : "G2", "endpoint": "localhost:8005", - "OConn" : ["G4", "G5"] + "OConn" : ["G4", "G5"] }, { "name" : "G3", "endpoint": "localhost:8006", - "OConn" : ["G4", "G5"] + "OConn" : ["G4", "G5"] }, { "name" : "G4", "endpoint": "localhost:8007", - "OConn" : ["G6"] + "OConn" : ["G6"] }, { "name" : "G5", "endpoint": "localhost:8008", - "OConn" : ["G6"] + "OConn" : ["G6"] }, { "name" : "G6", - "endpoint": "localhost:8007" + "endpoint": "localhost:8009" } ] } From 79911252228bb050b96cd1d2644cc69307ded40c Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 29 Mar 2021 09:48:59 +0200 Subject: [PATCH 035/202] - new tests - fixed problem with internal_mi_transformer in FastFlow --- ff/distributed/ff_dgroup.hpp | 2 +- ff/distributed/ff_wrappers.hpp | 4 +- ff/multinode.hpp | 8 ++ tests/distributed/test_complete2.cpp | 32 ++++--- tests/distributed/test_complete3.cpp | 14 ++- tests/distributed/test_complete4.cpp | 7 +- tests/distributed/test_complete5.cpp | 1 + tests/distributed/test_complete6.cpp | 123 ++++++++++++++++++++++++++ tests/distributed/test_complete6.json | 18 ++++ 9 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 tests/distributed/test_complete6.cpp create mode 100644 tests/distributed/test_complete6.json diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index e747063b..20481d5e 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -626,4 +626,4 @@ ff::dGroup& ff_pipeline::createGroup(std::string name){ return *g; } -#endif \ No newline at end of file +#endif diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 7231a5f5..622b0a11 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -47,7 +47,7 @@ class WrapperIN: public internal_mi_transformer { } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { - ff_node::registerCallback(cb,arg); + internal_mi_transformer::registerCallback(cb,arg); } Tin* deserialize(void* buffer) { @@ -332,4 +332,4 @@ class WrapperINOUT: public internal_mi_transformer { } }; -#endif \ No newline at end of file +#endif diff --git a/ff/multinode.hpp b/ff/multinode.hpp index bc48c174..7361db54 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -1014,6 +1014,14 @@ struct internal_mi_transformer: ff_minode { return n->set_output_buffer(o); } + FFBUFFER * get_out_buffer() const { return n->get_out_buffer();} + + bool ff_send_out(void * task, int id=-1, + unsigned long retry=((unsigned long)-1), + unsigned long ticks=(TICKS2WAIT)) { + return n->ff_send_out(task,id,retry,ticks); + } + bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool feedback=true) { diff --git a/tests/distributed/test_complete2.cpp b/tests/distributed/test_complete2.cpp index 3de01609..e2db74d6 100644 --- a/tests/distributed/test_complete2.cpp +++ b/tests/distributed/test_complete2.cpp @@ -1,12 +1,12 @@ /* - * * * Node1-->Node2 --> Node3 --> Node4 * * /<-- pipe0-->/ /<---pipe1--->/ * /<----------- pipe ------------>/ * - * + * G1: pipe0 + * G2: pipe1 */ @@ -48,26 +48,36 @@ struct Node1: ff_monode_t{ struct Node2: ff_node_t{ myTask_t* svc(myTask_t* t){ t->str += std::string(" World"); - std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; return t; } }; -struct Node3: ff_minode_t{ +struct Node3: ff_monode_t{ myTask_t* svc(myTask_t* t){ t->S.t += 1; - t->S.f += 1.0; + t->S.f += 1.0; return t; } }; -struct Node4: ff_node_t{ +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} myTask_t* svc(myTask_t* t){ - std::cout << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; delete t; return GO_ON; } + void svc_end() { + if (processed != ntasks) { + abort(); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; }; @@ -76,14 +86,16 @@ int main(int argc, char*argv[]){ error("DFF_Init\n"); return -1; } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } - long ntasks = 100; - ff_pipeline pipe; Node1 n1(ntasks); Node2 n2; Node3 n3; - Node4 n4; + Node4 n4(ntasks); ff_pipeline pipe0; pipe0.add_stage(&n1); pipe0.add_stage(&n2); diff --git a/tests/distributed/test_complete3.cpp b/tests/distributed/test_complete3.cpp index 285fa916..75214ea7 100644 --- a/tests/distributed/test_complete3.cpp +++ b/tests/distributed/test_complete3.cpp @@ -7,7 +7,8 @@ * G1 G2 * /<----------------- pipe ---------------->/ * - * + * G1: pipe0 + * G2: a2a */ @@ -58,6 +59,11 @@ struct Node3: ff_monode_t{ t->S.f += get_my_id()*1.0; return t; } + void eosnotify(ssize_t) { + printf("Node3 %ld EOS RECEIVED\n", get_my_id()); + fflush(NULL); + } + }; struct Node4: ff_minode_t{ @@ -68,10 +74,16 @@ struct Node4: ff_minode_t{ delete t; return GO_ON; } + void eosnotify(ssize_t id) { + printf("Node4 EOS RECEIVED from %ld\n", id); + fflush(NULL); + } + void svc_end() { if (processed != ntasks) { abort(); } + std::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_complete4.cpp b/tests/distributed/test_complete4.cpp index 7ef77c8e..7feb2766 100644 --- a/tests/distributed/test_complete4.cpp +++ b/tests/distributed/test_complete4.cpp @@ -5,10 +5,12 @@ * |-> Helper->Node2 -->| | | * |-> Helper->Node3 -->| * - * /<---------- a2a0 ---------->/ /<------------ a2a ------------>/ + * /<---------- a2a0 ---------->/ /<------------ a2a1 ------------>/ * G1 G2 * /<---------------------------- pipe ---------------------------->/ * + * G1: a2a0 + * G2: a2a1 * */ @@ -65,7 +67,7 @@ struct Node3: ff_monode_t{ struct Node4: ff_minode_t{ Node4(long ntasks):ntasks(ntasks) {} myTask_t* svc(myTask_t* t){ - //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n" << std::flush; ++processed; delete t; return GO_ON; @@ -74,6 +76,7 @@ struct Node4: ff_minode_t{ if (processed != ntasks) { abort(); } + std::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_complete5.cpp b/tests/distributed/test_complete5.cpp index 040f5610..f8bfc701 100644 --- a/tests/distributed/test_complete5.cpp +++ b/tests/distributed/test_complete5.cpp @@ -78,6 +78,7 @@ struct Node4: ff_minode_t{ if (processed != ntasks) { abort(); } + std::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_complete6.cpp b/tests/distributed/test_complete6.cpp new file mode 100644 index 00000000..4e1064e1 --- /dev/null +++ b/tests/distributed/test_complete6.cpp @@ -0,0 +1,123 @@ +/* + * |-> Node31 -->| + * |-> Node21 -->| | + * Node1---->| |-> Node32 -->| ----> Node4 + * |-> Node22 -->| | + * |-> Node33 -->| + * + * /<-pipe0->//<--------- a2a0 ---------->//<- pipe1->/ + * /<-------------..------ pipe --------------------->/ + * + * G1: pipe0 + * G2: a2a0 + * G3: pipe1 + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World") + std::to_string(get_my_id()); + return t; + } +}; + +struct Node3: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe, pipe0, pipe1; + Node1 n1(ntasks); + Node2 n21, n22; + Node3 n31, n32, n33; + Node4 n4(ntasks); + pipe0.add_stage(&n1); + pipe1.add_stage(&n4); + ff_a2a a2a; + a2a.add_firstset({&n21, &n22}); + a2a.add_secondset({&n31, &n32, &n33}); + + pipe.add_stage(&pipe0); + pipe.add_stage(&a2a); + pipe.add_stage(&pipe1); + + auto G1 = pipe0.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + auto G3 = pipe1.createGroup("G3"); + + + /* ----------------- */ G1.out << &n1; + G2.in << &n21 << &n22; G2.out << &n31 << &n32 << &n33; + G3.in << &n4; /* -------------------------- */ + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete6.json b/tests/distributed/test_complete6.json new file mode 100644 index 00000000..eb42a9ed --- /dev/null +++ b/tests/distributed/test_complete6.json @@ -0,0 +1,18 @@ +{ + "groups" : [ + { + "endpoint": "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G3"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006" + } + ] +} From 06d320f967e7ed7a946dd6ef8121673e63f261a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 29 Mar 2021 12:35:08 +0200 Subject: [PATCH 036/202] Change cleanup policy to change_node in ff_dgroup.hpp --- ff/distributed/ff_dgroup.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 20481d5e..63c130d0 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -92,17 +92,17 @@ class dGroup : public ff_farm { ff_node* wrapped = wrappers_[node].first; if (parentBB->isPipe()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, true); + reinterpret_cast(parentBB)->change_node(node, wrapped, false, true); continue; } if (parentBB->isAll2All()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, true); + reinterpret_cast(parentBB)->change_node(node, wrapped, false, true); continue; } if (parentBB->isComp()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, true); + reinterpret_cast(parentBB)->change_node(node, wrapped, false, true); continue; } From 604af6ae31c4544d757c23b78aa57dd9698da06d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 29 Mar 2021 12:52:06 +0200 Subject: [PATCH 037/202] Change cleanup policy to << operators in ff_dgroup.hpp --- ff/distributed/ff_dgroup.hpp | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 63c130d0..ecab46df 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -92,17 +92,17 @@ class dGroup : public ff_farm { ff_node* wrapped = wrappers_[node].first; if (parentBB->isPipe()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, false, true); + reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); continue; } if (parentBB->isAll2All()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, false, true); + reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); continue; } if (parentBB->isComp()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, false, true); + reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); continue; } @@ -333,12 +333,12 @@ MySet& MySet::operator<<(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else - this->group->in_.insert({node, {new WrapperIN(node, true), true}}); + this->group->in_.insert({node, {new WrapperIN(node, false), true}}); return *this; } @@ -357,12 +357,12 @@ MySet& MySet::operator<<(ff_minode_t* node){ if (!handle.empty()){// the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); } else - this->group->in_.insert({node, {new WrapperIN(node, true), true}}); + this->group->in_.insert({node, {new WrapperIN(node, false), true}}); return *this; } @@ -377,12 +377,12 @@ MySet& MySet::operator<<(ff_monode_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else { - ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true); + ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true, false); this->group->in_.insert({node, {combine, true}}); } @@ -398,12 +398,12 @@ MySet& MySet::operator<<(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else - this->group->out_.insert({node, {new WrapperOUT(node, true), true}}); + this->group->out_.insert({node, {new WrapperOUT(node, false), true}}); return *this; } @@ -417,10 +417,10 @@ MySet& MySet::operator<<(ff_minode_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else { ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, true), false, true); this->group->out_.insert({node, {combine, true}}); @@ -438,12 +438,12 @@ MySet& MySet::operator<<(ff_monode_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); } else - this->group->out_.insert({node, {new WrapperOUT(node, true), true}}); + this->group->out_.insert({node, {new WrapperOUT(node, false), true}}); return *this; } From cb3ced0f4a872a8342f244522956f11d3b203f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 29 Mar 2021 19:31:26 +0200 Subject: [PATCH 038/202] Added full parametric distributed A2A performance test --- ff/distributed/ff_dgroup.hpp | 2 +- tests/distributed/test_parametricPerf.cpp | 137 +++++++++++++++++++++ tests/distributed/test_parametricPerf.json | 13 ++ 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 tests/distributed/test_parametricPerf.cpp create mode 100644 tests/distributed/test_parametricPerf.json diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index ecab46df..f97b12af 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -295,7 +295,7 @@ class dGroup : public ff_farm { return ff_farm::run(skip_init); } - int wait() override{return ff_farm::wait();} + //int wait() {return ff_farm::wait();} void setEndpoint(const std::string address, const int port){ diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp new file mode 100644 index 00000000..45ff58a6 --- /dev/null +++ b/tests/distributed/test_parametricPerf.cpp @@ -0,0 +1,137 @@ +/* + * FastFlow concurrent network: + * + * |--> MiNode + * MoNode-->| + * |--> MiNode + * MoNode-->| + * |--> MiNode + * + * /<------- a2a ------>/ + * /<---- pipeMain ---->/ + */ + + +#include +#include +#include +#include + +std::mutex mtx; // used only for pretty printing + +float active_delay(int msecs) { + // read current time + float x = 1.25f; + auto start = std::chrono::high_resolution_clock::now(); + auto end = false; + while(!end) { + auto elapsed = std::chrono::high_resolution_clock::now() - start; + auto msec = std::chrono::duration_cast(elapsed).count(); + x *= sin(x) / atan(x) * tanh(x) * sqrt(x); + if(msec>=msecs) + end = true; + } + return x; +} + +struct ExcType { + int taskID; + std::vector data; + ExcType(){} + ExcType(int taskID, long length) : taskID(taskID), data(length, 'F') {} + + template + void serialize(Archive & archive) { + archive(taskID, data); + } +}; + + +struct MoNode : ff::ff_monode_t{ + int processedItems = 0; + int items, execTime; + long dataLength; + + MoNode(int itemsToGenerate, int execTime, long dataLength): items(itemsToGenerate), execTime(execTime), dataLength(dataLength) {} + + ExcType* svc(int*){ + for(int i=0; i< items; i++){ + active_delay(this->execTime); + ff_send_out(new ExcType(i, dataLength)); + } + return this->EOS; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << processedItems << std::endl; + } +}; + +struct MiNode : ff::ff_minode_t{ + int processedItems = 0; + int execTime; + MiNode(int execTime) : execTime(execTime) {} + + ExcType* svc(ExcType* i){ + active_delay(this->execTime); + ++processedItems; + delete i; + return this->GO_ON; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[MiNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + + +int main(int argc, char*argv[]){ + + DFF_Init(argc, argv); + + if (argc < 7){ + std::cout << "Usage: " << argv[0] << "#items #byteXitem #execTimeSource #execTimeSink #nw_sx #nw_dx" << std::endl; + return -1; + } + + int items = atoi(argv[1]); + long bytexItem = atol(argv[2]); + int execTimeSource = atoi(argv[3]); + int execTimeSink = atoi(argv[4]); + int numWorkerSx = atoi(argv[5]); + int numWorkerDx = atoi(argv[6]); + + ff_pipeline mainPipe; + ff::ff_a2a a2a; + + mainPipe.add_stage(&a2a); + + std::vector sxWorkers; + std::vector dxWorkers; + + for(int i = 0; i < numWorkerSx; i++) + sxWorkers.push_back(new MoNode(items, execTimeSource, bytexItem)); + + for(int i = 0; i < numWorkerDx; i++) + dxWorkers.push_back(new MiNode(execTimeSink)); + + a2a.add_firstset(sxWorkers); + a2a.add_secondset(dxWorkers); + + //mainPipe.run_and_wait_end(); + + auto g1 = a2a.createGroup("G1"); + auto g2 = a2a.createGroup("G2"); + + for(int i = 0; i < numWorkerSx; i++) g1.out << sxWorkers[i]; + + for(int i = 0; i < numWorkerDx; i++) g2.in << dxWorkers[i]; + + if (mainPipe.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } + +} \ No newline at end of file diff --git a/tests/distributed/test_parametricPerf.json b/tests/distributed/test_parametricPerf.json new file mode 100644 index 00000000..fab97f4a --- /dev/null +++ b/tests/distributed/test_parametricPerf.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} From 42d1b3e3c02fe85a4ac78706eb09f53066c38c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 1 Apr 2021 11:01:39 +0200 Subject: [PATCH 039/202] Added test_complete7 --- tests/distributed/test_complete7.cpp | 122 ++++++++++++++++++++++++++ tests/distributed/test_complete7.json | 18 ++++ 2 files changed, 140 insertions(+) create mode 100644 tests/distributed/test_complete7.cpp create mode 100644 tests/distributed/test_complete7.json diff --git a/tests/distributed/test_complete7.cpp b/tests/distributed/test_complete7.cpp new file mode 100644 index 00000000..282ed554 --- /dev/null +++ b/tests/distributed/test_complete7.cpp @@ -0,0 +1,122 @@ +/* + * + * | -> Node3 -->| + * Node1--> -->| -> Node3 -->| -> Node4 + * | -> Node3 -->| + * /<-- pipe0-->/ /<--------- a2a -------->/ + * G1 G2 + * /<----------------- pipe ---------------->/ + * + * G1: pipe0 + * G2: a2a + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + std::cout << "Source exiting!" << std::endl; + return EOS; + } + const long ntasks; +}; + +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } + void eosnotify(ssize_t) { + printf("Node3 %ld EOS RECEIVED\n", get_my_id()); + fflush(NULL); + } + +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void eosnotify(ssize_t id) { + printf("Node4 EOS RECEIVED from %ld\n", id); + fflush(NULL); + } + + void svc_end() { + if (processed != ntasks) { + abort(); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node3 n31, n32, n33; + Node4 n4(ntasks); + ff_pipeline pipe0; + pipe0.add_stage(&n1); + ff_a2a a2a; + a2a.add_firstset({&n31, &n32, &n33}); + a2a.add_secondset({&n4}); + pipe.add_stage(&pipe0); + pipe.add_stage(&a2a); + + auto G1 = pipe0.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + auto G3 = a2a.createGroup("G3"); + + G1.out << &n1; + G2.in << &n31 << &n32 << &n33; G2.out << &n31 << &n32 << &n33; + G3.in << &n4; + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete7.json b/tests/distributed/test_complete7.json new file mode 100644 index 00000000..beae1ddf --- /dev/null +++ b/tests/distributed/test_complete7.json @@ -0,0 +1,18 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G3"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006" + } + ] +} From cfecd7597bbdb929d164de7b99dc51901c506f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 1 Apr 2021 11:36:53 +0200 Subject: [PATCH 040/202] Added test_complete8 --- tests/distributed/test_complete7.cpp | 2 +- tests/distributed/test_complete8.cpp | 124 ++++++++++++++++++++++++++ tests/distributed/test_complete8.json | 18 ++++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 tests/distributed/test_complete8.cpp create mode 100644 tests/distributed/test_complete8.json diff --git a/tests/distributed/test_complete7.cpp b/tests/distributed/test_complete7.cpp index 282ed554..62a7f0f6 100644 --- a/tests/distributed/test_complete7.cpp +++ b/tests/distributed/test_complete7.cpp @@ -47,7 +47,7 @@ struct Node1: ff_monode_t{ const long ntasks; }; -struct Node3: ff_monode_t{ +struct Node3: ff_node_t{ myTask_t* svc(myTask_t* t){ t->S.t += get_my_id(); t->S.f += get_my_id()*1.0; diff --git a/tests/distributed/test_complete8.cpp b/tests/distributed/test_complete8.cpp new file mode 100644 index 00000000..f30c0bb8 --- /dev/null +++ b/tests/distributed/test_complete8.cpp @@ -0,0 +1,124 @@ +/* + * + * Node1-->Node2 --> Node3 --> Node4 + * + * /<-- pipe0-->//<-- pipe1 -->//<-pipe2->/ + * /<----------- pipe ------------>/ + * + * G1: pipe0 + * G2: pipe1 + * G3: pipe2 + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + + return t; + } +}; + +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += 1; + t->S.f += 1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2; + Node3 n3; + Node4 n4(ntasks); + ff_pipeline pipe0; + pipe0.add_stage(&n1); + pipe0.add_stage(&n2); + ff_pipeline pipe1; + pipe1.add_stage(&n3); + ff_pipeline pipe2; + pipe2.add_stage(&n4); + pipe.add_stage(&pipe0); + pipe.add_stage(&pipe1); + pipe.add_stage(&pipe2); + + auto G1 = pipe0.createGroup("G1"); + auto G2 = pipe1.createGroup("G2"); + auto G3 = pipe2.createGroup("G3"); + + G1.out << &n2; + G2.in << &n3; G2.out << &n3; + G3.in << &n4; + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete8.json b/tests/distributed/test_complete8.json new file mode 100644 index 00000000..beae1ddf --- /dev/null +++ b/tests/distributed/test_complete8.json @@ -0,0 +1,18 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G3"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006" + } + ] +} From a6abcf6de4d6a912f8719276b6c0d7b63b815b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 1 Apr 2021 12:37:53 +0200 Subject: [PATCH 041/202] Added test_wrapper --- ff/distributed/ff_dgroup.hpp | 3 + tests/distributed/test_complete8.cpp | 8 +- tests/distributed/test_complete8.json | 6 +- tests/distributed/test_wrapper.cpp | 103 ++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 tests/distributed/test_wrapper.cpp diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f97b12af..39f2c986 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -250,6 +250,7 @@ class dGroup : public ff_farm { // in/out nodes left to be added to the farm. The next lines does it for (const auto& pair : this->inout_){ + std::cout << "Added INOUT node" << std::endl; this->add_workers({pair.second}); } @@ -333,6 +334,7 @@ MySet& MySet::operator<<(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ + std::cout << "Annoted INOUT!" << std::endl; this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else @@ -398,6 +400,7 @@ MySet& MySet::operator<<(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ + std::cout << "Annoted INOUT!" << std::endl; this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); } } else diff --git a/tests/distributed/test_complete8.cpp b/tests/distributed/test_complete8.cpp index f30c0bb8..9c13fd81 100644 --- a/tests/distributed/test_complete8.cpp +++ b/tests/distributed/test_complete8.cpp @@ -31,7 +31,7 @@ struct myTask_t { }; -struct Node1: ff_monode_t{ +struct Node1: ff_node_t{ Node1(long ntasks):ntasks(ntasks) {} myTask_t* svc(myTask_t*){ for(long i=0; i< ntasks; i++) { @@ -55,7 +55,7 @@ struct Node2: ff_node_t{ } }; -struct Node3: ff_monode_t{ +struct Node3: ff_node_t{ myTask_t* svc(myTask_t* t){ t->S.t += 1; t->S.f += 1.0; @@ -63,7 +63,7 @@ struct Node3: ff_monode_t{ } }; -struct Node4: ff_minode_t{ +struct Node4: ff_node_t{ Node4(long ntasks):ntasks(ntasks) {} myTask_t* svc(myTask_t* t){ //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; @@ -113,7 +113,7 @@ int main(int argc, char*argv[]){ auto G3 = pipe2.createGroup("G3"); G1.out << &n2; - G2.in << &n3; G2.out << &n3; + G2.in << &n3; G2.out << &n3; G3.in << &n4; if (pipe.run_and_wait_end()<0) { diff --git a/tests/distributed/test_complete8.json b/tests/distributed/test_complete8.json index beae1ddf..72bcbe71 100644 --- a/tests/distributed/test_complete8.json +++ b/tests/distributed/test_complete8.json @@ -1,18 +1,18 @@ { "groups" : [ { - "endpoint" : "localhost:8004", + "endpoint" : "localhost:9004", "name" : "G1", "OConn" : ["G2"] }, { "name" : "G2", - "endpoint": "localhost:8005", + "endpoint": "localhost:9005", "OConn" : ["G3"] }, { "name" : "G3", - "endpoint": "localhost:8006" + "endpoint": "localhost:9006" } ] } diff --git a/tests/distributed/test_wrapper.cpp b/tests/distributed/test_wrapper.cpp new file mode 100644 index 00000000..84270946 --- /dev/null +++ b/tests/distributed/test_wrapper.cpp @@ -0,0 +1,103 @@ +/* + * + * Node1-->Node2 --> Node3 --> Node4 + * /<----------- pipe ------------>/ + * + */ + + +#include +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + + +struct Node1: ff_node_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + + return t; + } +}; + +struct Node3: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += 1; + t->S.f += 1.0; + return t; + } +}; + +struct Node4: ff_node_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2; + Node3 n3; + Node4 n4(ntasks); + + pipe.add_stage(&n1); + pipe.add_stage(new WrapperOUT(&n2)); + pipe.add_stage(new WrapperINOUT(&n3)); + pipe.add_stage(new WrapperIN(&n4)); + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} From 23d9875f0cecfc1f0be705dda260511540fd5448 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 1 Apr 2021 15:58:19 +0200 Subject: [PATCH 042/202] fixed problem with WrapperINOUT fixed problem araising when a worker of a farm building-block is an all-to-all (FastFlow issue) removed missing test from Makefile --- ff/distributed/ff_wrappers.hpp | 8 ++++++-- ff/farm.hpp | 16 ++++++++++++++-- tests/Makefile | 2 +- tests/distributed/test_complete7.cpp | 2 +- tests/distributed/test_wrapper.cpp | 7 +++++++ 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 622b0a11..58af33e7 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -244,6 +244,10 @@ class WrapperINOUT: public internal_mi_transformer { registerCallback(ff_send_out_to_cbk, this); } + void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { + internal_mi_transformer::registerCallback(cb,arg); + } + Tin* deserialize(void* buffer) { message_t* msg = (message_t*)buffer; @@ -271,7 +275,7 @@ class WrapperINOUT: public internal_mi_transformer { } bool serialize(Tout* in, int id) { - if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); + if ((void*)in > FF_TAG_MIN) return ff_node::ff_send_out(in); message_t* msg = nullptr; @@ -293,7 +297,7 @@ class WrapperINOUT: public internal_mi_transformer { msg->chid = id; } - return this->ff_send_out(msg); + return ff_node::ff_send_out(msg); } int svc_init() { diff --git a/ff/farm.hpp b/ff/farm.hpp index 210089dc..e73e5893 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -1392,7 +1392,13 @@ class ff_farm: public ff_node { */ int wait() { int ret=0; - if (lb->waitWorkers()<0) ret = -1; + //if (lb->waitWorkers()<0) ret = -1; + for(size_t i=0;iwait()<0) { + error("FARM, waiting worker thread, id = %d\n",workers[i]->get_my_id()); + ret = -1; + } + lb->running = -1; if (lb->waitlb()<0) ret=-1; if (!collector_removed && collector) if (gt->wait()<0) ret=-1; return ret; @@ -1415,7 +1421,13 @@ class ff_farm: public ff_node { */ inline int wait_freezing(/* timeval */ ) { int ret=0; - if (lb->wait_freezingWorkers()<0) ret = -1; + //if (lb->wait_freezingWorkers()<0) ret = -1; + for(size_t i=0;iwait_freezing()<0) { + error("FARM, waiting freezing of worker thread, id = %d\n",workers[i]->get_my_id()); + ret = -1; + } + lb->running = -1; if (lb->wait_lb_freezing()<0) ret=-1; if (!collector_removed && collector) if (gt->wait_freezing()<0) ret=-1; return ret; diff --git a/tests/Makefile b/tests/Makefile index ecf844d0..b9351dad 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator test_staticallocator2 test_staticallocator3 test_staticallocator4 test_changenode +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode #test_taskf2 test_taskf3 diff --git a/tests/distributed/test_complete7.cpp b/tests/distributed/test_complete7.cpp index 62a7f0f6..0df142b4 100644 --- a/tests/distributed/test_complete7.cpp +++ b/tests/distributed/test_complete7.cpp @@ -55,7 +55,7 @@ struct Node3: ff_node_t{ } void eosnotify(ssize_t) { printf("Node3 %ld EOS RECEIVED\n", get_my_id()); - fflush(NULL); + fflush(NULL); } }; diff --git a/tests/distributed/test_wrapper.cpp b/tests/distributed/test_wrapper.cpp index 84270946..adea9567 100644 --- a/tests/distributed/test_wrapper.cpp +++ b/tests/distributed/test_wrapper.cpp @@ -53,8 +53,15 @@ struct Node2: ff_node_t{ struct Node3: ff_node_t{ myTask_t* svc(myTask_t* t){ + static bool even=true; t->S.t += 1; t->S.f += 1.0; + if (even) { + ff_send_out(t); + even=false; + return GO_ON; + } + even=true; return t; } }; From 0c9e22256c81900b3d704c96a877962afefc31ad Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 3 Apr 2021 15:35:37 +0200 Subject: [PATCH 043/202] new tests added the DFF_getMyGroup method --- ff/distributed/ff_dgroups.hpp | 8 +- tests/distributed/dwordcount/dwordcount.cpp | 387 +++++++++++++++++++ tests/distributed/dwordcount/dwordcount.json | 13 + tests/distributed/test_wrapper.cpp | 8 +- tests/distributed/test_wrapper2.cpp | 144 +++++++ 5 files changed, 555 insertions(+), 5 deletions(-) create mode 100644 tests/distributed/dwordcount/dwordcount.cpp create mode 100644 tests/distributed/dwordcount/dwordcount.json create mode 100644 tests/distributed/test_wrapper2.cpp diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 09b6501d..9e7ce1ce 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -36,6 +36,8 @@ class dGroups { void setRunningGroup(std::string g){this->runningGroup = g;} + const std::string& getRunningGroup() const { return runningGroup; } + int run_and_wait_end(ff_node* parent){ if (groups.find(runningGroup) == groups.end()){ ff::error("The group specified is not found nor implemented!\n"); @@ -104,7 +106,7 @@ class dGroups { } }; -int DFF_Init(int& argc, char**& argv){ +static inline int DFF_Init(int& argc, char**& argv){ std::string configFile, groupName; @@ -166,6 +168,10 @@ int DFF_Init(int& argc, char**& argv){ return 0; } +static inline const std::string DFF_getMyGroup() { + return dGroups::Instance()->getRunningGroup(); +} + } #endif diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp new file mode 100644 index 00000000..f4177b4b --- /dev/null +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -0,0 +1,387 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + +/* + * The Source produces a continuous stream of lines of a text file. + * The Splitter tokenizes each line producing a new output item for each + * word extracted. The Counter receives single words from the line + * splitter and counts how many occurrences of the same word appeared + * on the stream until that moment. The Sink node receives every result + * produced by the word counter and counts the total number of words. + * + * One possible FastFlow graph is the following: + * + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * + * /<---- pipe1 ---->/ /<--- pipe2 --->/ + * /<----------------- a2a ------------------>/ + * + * G1: pipe1 + * G2: pipe2 + * + */ + +#define FF_BOUNDED_BUFFER +#define DEFAULT_BUFFER_CAPACITY 2048 +#define BYKEY true + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ff; +using namespace std; + +const size_t qlen = DEFAULT_BUFFER_CAPACITY; +const int MAXLINE=128; +const int MAXWORD=32; + +struct tuple_t { + char text_line[MAXLINE]; // line of the parsed dataset (text, book, ...) + size_t key; // line number + uint64_t id; // id set to zero + uint64_t ts; // timestamp +}; + +struct result_t { + char key[MAXWORD]; // key word + uint64_t id; // id that indicates the current number of occurrences of the key word + uint64_t ts; // timestamp + + template + void serialize(Archive & archive) { + archive(key, id, ts); + } +}; + +vector dataset; // contains all the input tuples in memory +atomic total_lines=0; // total number of lines processed by the system +atomic total_bytes=0; // total number of bytes processed by the system + +/// application run time (source generates the stream for app_run_time seconds, then sends out EOS) +unsigned long app_run_time = 15; // time in seconds + +static inline unsigned long current_time_usecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000L + (t.tv_nsec / 1000); +} +static inline unsigned long current_time_nsecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000000L + t.tv_nsec; +} + +struct Source: ff_monode_t { + size_t next_tuple_idx = 0; // index of the next tuple to be sent + int generations = 0; // counts the times the file is generated + long generated_tuples = 0; // tuples counter + long generated_bytes = 0; // bytes counter + + // time variables + unsigned long app_start_time; // application start time + unsigned long current_time; + + Source(const unsigned long _app_start_time): + app_start_time(_app_start_time),current_time(_app_start_time) { + } + + tuple_t* svc(tuple_t*) { + while(1) { + tuple_t* t = new tuple_t; + assert(t); + + if (generated_tuples > 0) current_time = current_time_nsecs(); + if (next_tuple_idx == 0) generations++; // file generations counter + generated_tuples++; // tuples counter + // put a timestamp and send the tuple + *t = dataset.at(next_tuple_idx); + generated_bytes += sizeof(tuple_t); + t->ts = current_time - app_start_time; + ff_send_out(t); + ++next_tuple_idx; + next_tuple_idx %= dataset.size(); + // EOS reached + if (current_time - app_start_time >= (app_run_time*1000000000L) && next_tuple_idx == 0) + break; + } + total_lines.fetch_add(generated_tuples); + total_bytes.fetch_add(generated_bytes); + return EOS; + } +}; +struct Splitter: ff_monode_t { + Splitter(long noutch):noutch(noutch) {} + + // int svc_init() { + // noutch=get_num_outchannels(); // TODO: this doesn't work, it must be fixed! + // /return 0; + // } + + result_t* svc(tuple_t* in) { + char *tmpstr; + char *token = strtok_r(in->text_line, " ", &tmpstr); + while (token) { +#if defined(BYKEY) + int ch = std::hash()(std::string(token)) % noutch; +#else + int ch = ++idx % noutch; +#endif + result_t* r = new result_t; + assert(r); + strncpy(r->key, token, MAXWORD-1); + r->key[MAXWORD-1]='\0'; + r->ts = in->ts; + + ff_send_out_to(r, ch); + token = strtok_r(NULL, " ", &tmpstr); + } + delete in; + return GO_ON; + } + long noutch=0; + long idx=-1; +}; + +struct Counter: ff_minode_t { + result_t* svc(result_t* in) { + ++M[std::string(in->key)]; + // number of occurrences of the string word up to now + in->id = M[std::string(in->key)]; + return in; + } + size_t unique() { + // std::cout << "Counter:\n"; + // for (const auto& kv : M) { + // std::cout << kv.first << " --> " << kv.second << "\n"; + // } + return M.size(); + } + std::map M; +}; + +struct Sink: ff_node_t { + result_t* svc(result_t* in) { + ++words; + delete in; + return GO_ON; + } + size_t words=0; // total number of words received +}; + + +/** + * @brief Parse the input file and create all the tuples + * + * The created tuples are maintained in memory. The source node will generate the stream by + * reading all the tuples from main memory. + * + * @param file_path the path of the input dataset file + */ +int parse_dataset_and_create_tuples(const string& file_path) { + ifstream file(file_path); + if (file.is_open()) { + size_t all_records = 0; // counter of all records (dataset line) read + string line; + while (getline(file, line)) { + // process file line + if (!line.empty()) { + if (line.length() > MAXLINE) { + std::cerr << "ERROR INCREASE MAXLINE\n"; + exit(EXIT_FAILURE); + } + tuple_t t; + strncpy(t.text_line, line.c_str(), MAXLINE-1); + t.text_line[MAXLINE-1]='\0'; + t.key = all_records; + t.id = 0; + t.ts = 0; + all_records++; + dataset.push_back(t); + } + } + file.close(); + } else { + std::cerr << "Unable to open the file " << file_path << "\n"; + return -1; + } + return 0; +} + + + +int main(int argc, char* argv[]) { + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + /// parse arguments from command line + std::string file_path(""); + size_t source_par_deg = 0; + size_t sink_par_deg = 0; + + if (argc >= 3 || argc == 1) { + int option = 0; + while ((option = getopt(argc, argv, "f:p:t:")) != -1) { + switch (option) { + case 'f': file_path=string(optarg); break; + case 'p': { + vector par_degs; + string pars(optarg); + stringstream ss(pars); + for (size_t i; ss >> i;) { + par_degs.push_back(i); + if (ss.peek() == ',') + ss.ignore(); + } + if (par_degs.size() != 2) { + std::cerr << "Error in parsing the input arguments -p, the format is n,m\n"; + return -1; + } else { + source_par_deg = par_degs[0]; + sink_par_deg = par_degs[1]; + } + } break; + case 't': { + long t = stol(optarg); + if (t<=0 || t > 100) { + std::cerr << "Wrong value for the '-t' option, it should be in the range [1,100]\n"; + return -1; + } + app_run_time = t; + } break; + default: { + std::cerr << "Error in parsing the input arguments\n"; + return -1; + } + } + } + } else { + std::cerr << "Parameters: -p -f \n"; + return -1; + } + if (file_path.length()==0) { + std::cerr << "The file path is empty, please use option -f \n"; + return -1; + } + if (source_par_deg == 0 || sink_par_deg==0) { + std::cerr << "Wrong values for the parallelism degree, please use option -p \n"; + return -1; + } + + if (DFF_getMyGroup() == "G1") { + /// data pre-processing + if (parse_dataset_and_create_tuples(file_path)< 0) + return -1; + + cout << "Executing WordCount with parameters:" << endl; + cout << " * source/splitter : " << source_par_deg << endl; + cout << " * counter/sink : " << sink_par_deg << endl; + cout << " * running time : " << app_run_time << " (s)\n"; + } + + /// application starting time + unsigned long app_start_time = current_time_nsecs(); + + std::vector C(sink_par_deg); + std::vector S(sink_par_deg); + std::vector L; // left and right workers of the A2A + std::vector R; + + ff_a2a a2a(false, qlen, qlen, true); + + auto G1 = a2a.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + + for (size_t i=0;iadd_stage(new Source(app_start_time)); + Splitter* sp = new Splitter(sink_par_deg); + pipe0->add_stage(sp); + L.push_back(pipe0); + + G1.out << sp; + } + for (size_t i=0;iadd_stage(C[i]); + pipe1->add_stage(S[i]); + R.push_back(pipe1); + + G2.in << C[i]; + } + + a2a.add_firstset(L, 0, true); + a2a.add_secondset(R, true); + ff_pipeline pipeMain(false, qlen, qlen, true); + pipeMain.add_stage(&a2a); + + std::cout << "Starting " << pipeMain.numThreads() << " threads\n"; + /// evaluate topology execution time + volatile unsigned long start_time_main_usecs = current_time_usecs(); + if (pipeMain.run_and_wait_end()<0) { + error("running pipeMain\n"); + return -1; + } + volatile unsigned long end_time_main_usecs = current_time_usecs(); + cout << "Exiting" << endl; + double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); + cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; + if (DFF_getMyGroup() == "G1") { + cout << "total_lines sent : " << total_lines << "\n"; + cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; + //double throughput = total_lines / elapsed_time_seconds; + //double mbs = (double)((total_bytes / 1048576) / elapsed_time_seconds); + //cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; + } else { + size_t words=0; + size_t unique=0; + for(size_t i=0;iwords; + unique+= C[i]->unique(); + delete S[i]; + delete C[i]; + } + cout << "words : " << words << "\n"; + cout << "unique : " << unique<< "\n"; + } + + return 0; +} diff --git a/tests/distributed/dwordcount/dwordcount.json b/tests/distributed/dwordcount/dwordcount.json new file mode 100644 index 00000000..fab97f4a --- /dev/null +++ b/tests/distributed/dwordcount/dwordcount.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} diff --git a/tests/distributed/test_wrapper.cpp b/tests/distributed/test_wrapper.cpp index adea9567..b197d41a 100644 --- a/tests/distributed/test_wrapper.cpp +++ b/tests/distributed/test_wrapper.cpp @@ -42,7 +42,7 @@ struct Node1: ff_node_t{ const long ntasks; }; -struct Node2: ff_node_t{ +struct Node2: ff_monode_t{ myTask_t* svc(myTask_t* t){ t->str += std::string(" World"); //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; @@ -98,9 +98,9 @@ int main(int argc, char*argv[]){ Node4 n4(ntasks); pipe.add_stage(&n1); - pipe.add_stage(new WrapperOUT(&n2)); - pipe.add_stage(new WrapperINOUT(&n3)); - pipe.add_stage(new WrapperIN(&n4)); + pipe.add_stage(new WrapperOUT(&n2), true); + pipe.add_stage(new WrapperINOUT(&n3), true); + pipe.add_stage(new WrapperIN(&n4), true); if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); diff --git a/tests/distributed/test_wrapper2.cpp b/tests/distributed/test_wrapper2.cpp new file mode 100644 index 00000000..7a879269 --- /dev/null +++ b/tests/distributed/test_wrapper2.cpp @@ -0,0 +1,144 @@ +/* + * | -> Node2 ->| | -> Node3->Helper ->| + * Node1-->| | -->| | --> Node4 + * | -> Node2 ->| | -> Node3->Helper ->| + * + * /<-- combine -->/ + * /<-------------- a2a ---------------->/ + * /<------------------------ pipe ------------------------>/ + * + * + * Node2 uses the WrapperOUT + * Node3 uses the WrapperIN + * Helper uses the WrapperOUT + * Node4 uses the WrapperIN + */ + +#include +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + + int dest = ++cnt % get_num_outchannels(); + ff_send_out_to(t, dest); + + return GO_ON; + } + size_t cnt=0; +}; + +struct Node3: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + static bool even=true; + std::cout << "Node3" << get_my_id()+1 << ": from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + + t->S.t += 1; + t->S.f += 1.0; + if (even) { + ff_send_out(t); + even=false; + return GO_ON; + } + even=true; + return t; + } +}; + +struct Helper: ff_node_t { + myTask_t* svc(myTask_t* t) { return t;} +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + ff_a2a a2a; + Node2 n21, n22; + Node3 n31, n32; + Node4 n4(ntasks); + + pipe.add_stage(&n1); + std::vector L; + std::vector R; + L.push_back(new WrapperOUT(&n21, 2)); + L.push_back(new WrapperOUT(&n22, 2)); + a2a.add_firstset(L, 0 ,true); + + ff_comb comb1(new WrapperIN(&n31), + new WrapperOUT(new Helper,1,true), + true,true); + ff_comb comb2(new WrapperIN(&n32), + new WrapperOUT(new Helper,1,true), + true,true); + + R.push_back(&comb1); + R.push_back(&comb2); + a2a.add_secondset(R); + pipe.add_stage(&a2a); + pipe.add_stage(new WrapperIN(&n4), true); + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} From 9a63e4f4dbd7d9605b10115701c2da75939ba17d Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 6 Apr 2021 11:06:12 +0200 Subject: [PATCH 044/202] modified the command-line arguments parsing for the loader --- ff/distributed/loader/dff_run.cpp | 93 ++++++++++++++++++------------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 9144fec4..3f9d9739 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -2,9 +2,9 @@ #include #include #include +#include #include #include -#include #include @@ -20,7 +20,7 @@ static inline unsigned long getusec() { return (unsigned long)(tv.tv_sec*1e6+tv.tv_usec); } -std::string configFile; +std::string configFile(""); std::string executable; inline std::vector split (const std::string &s, char delim) { @@ -55,7 +55,7 @@ struct G { void run(){ char b[150]; // ssh -t sprintf(b, " %s %s %s --DFF_Config=%s --DFF_GName=%s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : "") , executable.c_str(), configFile.c_str(), this->name.c_str()); - std::cout << "Executing the following string: " << b << std::endl; + std::cout << "Executing the following command: " << b << std::endl; fd = popen(b, "r"); if (fd == NULL) { @@ -76,49 +76,66 @@ bool allTerminated(std::vector& groups){ return true; } +static inline void usage(char* progname) { + std::cout << "\nUSAGE: " << progname << " [Options] -f \n" + << "Options: \n" + << "\t -v ,..., \t Prints the output of the specified groups\n" + << "\t -V \t Print the output of all groups\n"; + +} + int main(int argc, char** argv) { if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-help") == 0 || strcmp(argv[0], "-h") == 0){ - std::cout << "USAGE: " << argv[0] << " [options] -f \n\n" - << "OPTIONS: \n" - << "\t -v ,..., \t Print the ouput of the g1 and g2 processes. If no groups are specified all are printed\n"; - + usage(argv[0]); exit(EXIT_SUCCESS); } - std::vector viewGroups; bool seeAll = false; - - int c; - while ((c = getopt(argc, argv, "Vv:f:")) != -1){ - - switch (c){ - case 'f': - configFile = std::string(optarg); - break; - case 'V': - seeAll = true; - break; - case 'v': - viewGroups = split(optarg, ','); - break; - case '?': - if (optopt == 'f') - printf ("Option -%c requires an argument.\n", optopt); - else if (isprint (optopt)) - printf ("Unknown option `-%c'.\n", optopt); - else - printf ("Unknown option character `\\x%x'.\n", optopt); - return 1; - default: - abort(); - } - } - - for (int index = optind; index < argc; index++) + int optind=0; + for(int i=1;i Date: Tue, 6 Apr 2021 22:02:46 +0200 Subject: [PATCH 045/202] Added hostname query to detect local processes in loader --- ff/distributed/loader/dff_run.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 3f9d9739..de76c7e8 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -20,6 +20,7 @@ static inline unsigned long getusec() { return (unsigned long)(tv.tv_sec*1e6+tv.tv_usec); } +char hostname[100]; std::string configFile(""); std::string executable; @@ -64,7 +65,7 @@ struct G { } } - bool isRemote(){return !(!host.compare("127.0.0.1") || !host.compare("localhost"));} + bool isRemote(){return !(!host.compare("127.0.0.1") || !host.compare("localhost") || !host.compare(hostname));} }; @@ -91,6 +92,9 @@ int main(int argc, char** argv) { exit(EXIT_SUCCESS); } + // get the hostname + gethostname(hostname, 100); + std::vector viewGroups; bool seeAll = false; int optind=0; From f9f0e9d285842daaf7aac6b06d86e932ec106955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 8 Apr 2021 10:53:08 +0200 Subject: [PATCH 046/202] Fixed test_parametricPerf --- tests/distributed/test_parametricPerf.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 45ff58a6..bb986d46 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -48,7 +48,6 @@ struct ExcType { struct MoNode : ff::ff_monode_t{ - int processedItems = 0; int items, execTime; long dataLength; @@ -64,7 +63,7 @@ struct MoNode : ff::ff_monode_t{ void svc_end(){ const std::lock_guard lock(mtx); - std::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << processedItems << std::endl; + std::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << std::endl; } }; @@ -112,7 +111,7 @@ int main(int argc, char*argv[]){ std::vector dxWorkers; for(int i = 0; i < numWorkerSx; i++) - sxWorkers.push_back(new MoNode(items, execTimeSource, bytexItem)); + sxWorkers.push_back(new MoNode(ceil((double)items/numWorkerSx), execTimeSource, bytexItem)); for(int i = 0; i < numWorkerDx; i++) dxWorkers.push_back(new MiNode(execTimeSink)); From 5d78c648cd649692340c29f496e6e50b31858022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 8 Apr 2021 18:45:37 +0200 Subject: [PATCH 047/202] Added <<= operator for accepting a node pointer and transform or finalize function wrapped in a std::pair. Bug fixes on <<= operator --- ff/distributed/ff_dgroup.hpp | 143 ++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 39f2c986..4e845b21 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -45,12 +45,31 @@ class MySet { template MySet& operator<<=(ff_node_t*) {} + template + MySet& operator<<=(std::pair*, const std::function)>>) {} + + template + MySet& operator<<=(std::pair*, std::function(std::add_pointer_t)>>) {} + template MySet& operator<<=(ff_minode_t*) {} + template + MySet& operator<<=(std::pair*, const std::function)>>) {} + + template + MySet& operator<<=(std::pair*, std::function(std::add_pointer_t)>>) {} + template MySet& operator<<=(ff_monode_t*) {} + template + MySet& operator<<=(std::pair*, const std::function)>>) {} + + template + MySet& operator<<=(std::pair*, std::function(std::add_pointer_t)>>) {} + + bool check_inout(ff_node* node); }; @@ -471,6 +490,25 @@ MySet& MySet::operator<<=(ff_node_t* node){ return *this; } +template<> +template +MySet& MySet::operator<<=(std::pair*, const std::function)>> nodeFun){ + if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(nodeFun.first); + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, true, nodeFun.second)}); + } + } else + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + } else + this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, true, nodeFun.second), false}}); + + return *this; +} + template<> template @@ -484,7 +522,7 @@ MySet& MySet::operator<<=(ff_minode_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); } else this->group->in_.insert({node, {new WrapperIN(node, true), false}}); @@ -492,6 +530,26 @@ MySet& MySet::operator<<=(ff_minode_t* node){ return *this; } +template<> +template +MySet& MySet::operator<<=(std::pair*, const std::function)>> nodeFun){ + if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(nodeFun.first); + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, true, nodeFun.second)}); + } + } else + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + + } else + this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, true, nodeFun.second), false}}); + + return *this; +} + template<> template MySet& MySet::operator<<=(ff_monode_t* node){ @@ -514,6 +572,28 @@ MySet& MySet::operator<<=(ff_monode_t* node){ return *this; } +template<> +template +MySet& MySet::operator<<=(std::pair*, const std::function)>> nodeFun){ + if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->out_.extract(nodeFun.first); + if (!handle.empty()){ // the node is edge also in its output + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, true, nodeFun.second)}); + } + } else + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + + } else { + ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true, nodeFun.second), nodeFun.first, true); + this->group->in_.insert({nodeFun.first, {combine, false}}); + } + + return *this; +} + template<> template MySet& MySet::operator<<=(ff_node_t* node){ @@ -533,6 +613,27 @@ MySet& MySet::operator<<=(ff_node_t* node){ return *this; } +template<> +template +MySet& MySet::operator<<=(std::pair*, std::function(std::add_pointer_t)>> nodeFun){ + if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(nodeFun.first); + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, true, nullptr, nodeFun.second)}); + } + } else + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); + } else + this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, true, nodeFun.second), false}}); + + return *this; +} + + + template<> template MySet& MySet::operator<<=(ff_minode_t* node){ @@ -554,6 +655,27 @@ MySet& MySet::operator<<=(ff_minode_t* node){ return *this; } +template<> +template +MySet& MySet::operator<<=(std::pair*, std::function(std::add_pointer_t)>> nodeFun){ + if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(nodeFun.first); + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, true, nullptr, nodeFun.second)}); + } + } else + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); + } else { + ff_comb* combine = new ff_comb(nodeFun.first, new WrapperOUT(new ForwarderNode, true, nodeFun.second), false, true); + this->group->out_.insert({nodeFun.first, {combine, false}}); + } + + return *this; +} + template<> template MySet& MySet::operator<<=(ff_monode_t* node){ @@ -573,6 +695,25 @@ MySet& MySet::operator<<=(ff_monode_t* node){ return *this; } +template<> +template +MySet& MySet::operator<<=(std::pair*, std::function(std::add_pointer_t)>> nodeFun){ + if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! + + auto handle = this->group->in_.extract(nodeFun.first); + if (!handle.empty()){ // the node is edge also in its input + if (handle.mapped().second){ + if constexpr (cereal::traits::is_output_serializable::value){ + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, true, nullptr, nodeFun.second)}); + } + } else + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer(), nodeFun.second)}); + } else + this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, true, nodeFun.second), false}}); + + return *this; +} + template bool MySet::check_inout(ff_node* node){ return this->group->inout_.find(node) != this->group->inout_.end(); From 4825aa519d2f1cb6b81134a381f4dbc6ba33928f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 8 Apr 2021 22:12:06 +0200 Subject: [PATCH 048/202] Deleted write call to send the payload in dsender, now its handled by the iovector and send everything in one shot using a writev --- ff/distributed/ff_dsender.hpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 66149481..d02ba944 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -139,21 +139,18 @@ class ff_dsender: public ff_minode_t { task->chid = htonl(task->chid); size_t sz = htobe64(task->data.getLen()); - struct iovec iov[3]; + struct iovec iov[4]; iov[0].iov_base = &task->sender; iov[0].iov_len = sizeof(task->sender); iov[1].iov_base = &task->chid; iov[1].iov_len = sizeof(task->chid); iov[2].iov_base = &sz; iov[2].iov_len = sizeof(sz); + iov[3].iov_base = task->data.getPtr(); + iov[3].iov_len = task->data.getLen(); - if (writevn(sck, iov, 3) < 0){ - error("Error writing on socket header\n"); - return -1; - } - - if (writen(sck, task->data.getPtr(), task->data.getLen()) < 0){ - error("Error writing on socket data\n"); + if (writevn(sck, iov, 4) < 0){ + error("Error writing on socket\n"); return -1; } From 94bf26de17005a46262bd7782dafabf7ac463186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 12 Apr 2021 16:52:02 +0200 Subject: [PATCH 049/202] Fixed custom serialization operators annotation. --- ff/distributed/ff_dgroup.hpp | 53 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 4e845b21..e7a4263e 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -45,29 +45,20 @@ class MySet { template MySet& operator<<=(ff_node_t*) {} - template - MySet& operator<<=(std::pair*, const std::function)>>) {} + template + MySet& operator<<=(std::pair*, Function>); template - MySet& operator<<=(std::pair*, std::function(std::add_pointer_t)>>) {} + MySet& operator<<=(ff_minode_t*); - template - MySet& operator<<=(ff_minode_t*) {} + template + MySet& operator<<=(std::pair*, Function>); template - MySet& operator<<=(std::pair*, const std::function)>>) {} + MySet& operator<<=(ff_monode_t*); - template - MySet& operator<<=(std::pair*, std::function(std::add_pointer_t)>>) {} - - template - MySet& operator<<=(ff_monode_t*) {} - - template - MySet& operator<<=(std::pair*, const std::function)>>) {} - - template - MySet& operator<<=(std::pair*, std::function(std::add_pointer_t)>>) {} + template + MySet& operator<<=(std::pair*, Function>); bool check_inout(ff_node* node); @@ -491,8 +482,8 @@ MySet& MySet::operator<<=(ff_node_t* node){ } template<> -template -MySet& MySet::operator<<=(std::pair*, const std::function)>> nodeFun){ +template +MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(nodeFun.first); @@ -531,8 +522,8 @@ MySet& MySet::operator<<=(ff_minode_t* node){ } template<> -template -MySet& MySet::operator<<=(std::pair*, const std::function)>> nodeFun){ +template +MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(nodeFun.first); @@ -545,7 +536,7 @@ MySet& MySet::operator<<=(std::pair*, const std:: this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); } else - this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, true, nodeFun.second), false}}); + this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, 1, true, nodeFun.second), false}}); return *this; } @@ -573,8 +564,8 @@ MySet& MySet::operator<<=(ff_monode_t* node){ } template<> -template -MySet& MySet::operator<<=(std::pair*, const std::function)>> nodeFun){ +template +MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->out_.extract(nodeFun.first); @@ -614,8 +605,8 @@ MySet& MySet::operator<<=(ff_node_t* node){ } template<> -template -MySet& MySet::operator<<=(std::pair*, std::function(std::add_pointer_t)>> nodeFun){ +template +MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->in_.extract(nodeFun.first); @@ -656,8 +647,8 @@ MySet& MySet::operator<<=(ff_minode_t* node){ } template<> -template -MySet& MySet::operator<<=(std::pair*, std::function(std::add_pointer_t)>> nodeFun){ +template +MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->in_.extract(nodeFun.first); @@ -696,8 +687,8 @@ MySet& MySet::operator<<=(ff_monode_t* node){ } template<> -template -MySet& MySet::operator<<=(std::pair*, std::function(std::add_pointer_t)>> nodeFun){ +template +MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! auto handle = this->group->in_.extract(nodeFun.first); @@ -709,7 +700,7 @@ MySet& MySet::operator<<=(std::pair*, std::func } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer(), nodeFun.second)}); } else - this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, true, nodeFun.second), false}}); + this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, nodeFun.second), false}}); return *this; } From 32dac15fbca38da99dfcf1dda56d3ca3b3aef89c Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 17 Apr 2021 11:20:36 +0200 Subject: [PATCH 050/202] improved test_parametricPerf, modified the signature of the "manual" serialization/deserialization functions --- ff/distributed/ff_dgroup.hpp | 15 +++ ff/distributed/ff_wrappers.hpp | 58 ++++----- tests/distributed/test_parametricPerf.cpp | 138 ++++++++++++++++------ 3 files changed, 147 insertions(+), 64 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index e7a4263e..9dc54709 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -761,4 +761,19 @@ ff::dGroup& ff_pipeline::createGroup(std::string name){ return *g; } +// utility functions useful for creating suitable pairs to be used +// when defining custom serialization/deserialization functions +template +static inline std::pair*, Function> packup(ff_node_t* node, Function f){ + return std::make_pair(node, f); +} +template +static inline std::pair*, Function> packup(ff_minode_t* node, Function f){ + return std::make_pair(node, f); +} +template +static inline std::pair*, Function> packup(ff_monode_t* node, Function f){ + return std::make_pair(node, f); +} + #endif diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 58af33e7..1f1acd10 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -12,6 +12,7 @@ using namespace ff; + /* Wrapper IN class */ @@ -21,27 +22,28 @@ class WrapperIN: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have public: + using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } - WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } - WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } template - WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } @@ -66,9 +68,7 @@ class WrapperIN: public internal_mi_transformer { // deserialize the buffer into a heap allocated buffer msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - data = reinterpret_cast(msg->data.getPtr()); - - if (finalizer_) finalizer_(data); + if (finalizer_) data = finalizer_(msg->data.getPtr(), msg->data.getLen()); } delete msg; @@ -97,9 +97,8 @@ class WrapperIN: public internal_mi_transformer { ff_node* getOriginal(){ return this->n; } - std::function finalizer_; - - std::function getFinalizer() {return this->finalizer_;} + ff_deserialize_F_t finalizer_; + ff_deserialize_F_t getFinalizer() {return this->finalizer_;} }; @@ -113,32 +112,33 @@ class WrapperOUT: public internal_mo_transformer { public: typedef Tout T_out; + using ff_serialize_F_t = std::function(std::add_pointer_t)>; - WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -192,10 +192,10 @@ class WrapperOUT: public internal_mo_transformer { return internal_mo_transformer::run(skip_init); } - std::function(Tout*)> getTransform(){return this->transform_;} - - std::function(Tout*)> transform_; + ff_serialize_F_t transform_; + ff_serialize_F_t getTransform() { return this->transform_;} + ff::ff_node* getOriginal(){return this->n;} static inline bool ff_send_out_to_cbk(void* task, int id, @@ -218,27 +218,29 @@ class WrapperINOUT: public internal_mi_transformer { public: typedef Tout T_out; + using ff_serialize_F_t = std::function(std::add_pointer_t)>; + using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, const std::function)> finalizer = nullptr, std::function(std::add_pointer_t)> transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -265,9 +267,7 @@ class WrapperINOUT: public internal_mi_transformer { // deserialize the buffer into a heap allocated buffer msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - data = reinterpret_cast(msg->data.getPtr()); - - if (finalizer_) finalizer_(data); + if (finalizer_) data = finalizer_(msg->data.getPtr(), msg->data.getLen()); } delete msg; @@ -323,12 +323,12 @@ class WrapperINOUT: public internal_mi_transformer { serialize(reinterpret_cast(out), -1); return GO_ON; } - - ff_node* getOriginal(){ return this->n; } - std::function finalizer_; + ff_deserialize_F_t finalizer_; std::function(Tout*)> transform_; + ff_node* getOriginal(){ return this->n; } + static inline bool ff_send_out_to_cbk(void* task, int id, unsigned long retry, unsigned long ticks, void* obj) { diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index bb986d46..03e244a4 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -17,6 +17,8 @@ #include #include +#define MANUAL_SERIALIZATION 1 + std::mutex mtx; // used only for pretty printing float active_delay(int msecs) { @@ -35,30 +37,64 @@ float active_delay(int msecs) { } struct ExcType { - int taskID; - std::vector data; - ExcType(){} - ExcType(int taskID, long length) : taskID(taskID), data(length, 'F') {} - - template + ExcType():contiguous(false) {} + ExcType(bool): contiguous(true) {} + ~ExcType() { + if (!contiguous) + delete [] C; + } + + size_t clen = 0; + char* C = nullptr; + bool contiguous; + + +#if !defined(MANUAL_SERIALIZATION) + template void serialize(Archive & archive) { - archive(taskID, data); + archive(clen); + if (!C) { + assert(!contiguous); + C = new char[clen]; + } + archive(cereal::binary_data(C, clen)); } +#endif }; +static ExcType* allocateExcType(size_t size, bool setdata=false) { + char* _p = (char*)malloc(size+sizeof(ExcType)); + ExcType* p = new (_p) ExcType(true); // contiguous allocation + + p->clen = size; + p->C = (char*)p+sizeof(ExcType); + if (setdata) { + bzero(p->C, p->clen); + p->C[0] = 'c'; + if (size>10) + p->C[10] = 'i'; + if (size>100) + p->C[100] = 'a'; + if (size>500) + p->C[500] = 'o'; + } + return p; +} -struct MoNode : ff::ff_monode_t{ + +struct MoNode : ff::ff_monode_t{ int items, execTime; long dataLength; + bool checkdata; + MoNode(int itemsToGenerate, int execTime, long dataLength, bool checkdata): + items(itemsToGenerate), execTime(execTime), dataLength(dataLength), checkdata(checkdata) {} - MoNode(int itemsToGenerate, int execTime, long dataLength): items(itemsToGenerate), execTime(execTime), dataLength(dataLength) {} - - ExcType* svc(int*){ + ExcType* svc(ExcType*){ for(int i=0; i< items; i++){ - active_delay(this->execTime); - ff_send_out(new ExcType(i, dataLength)); + if (execTime) active_delay(this->execTime); + ff_send_out(allocateExcType(dataLength, checkdata)); } - return this->EOS; + return this->EOS; } void svc_end(){ @@ -70,13 +106,25 @@ struct MoNode : ff::ff_monode_t{ struct MiNode : ff::ff_minode_t{ int processedItems = 0; int execTime; - MiNode(int execTime) : execTime(execTime) {} - - ExcType* svc(ExcType* i){ - active_delay(this->execTime); - ++processedItems; - delete i; - return this->GO_ON; + bool checkdata; + MiNode(int execTime, bool checkdata=false): execTime(execTime),checkdata(checkdata) {} + + ExcType* svc(ExcType* in){ + if (execTime) active_delay(this->execTime); + ++processedItems; + if (checkdata) { + assert(in->C[0] == 'c'); + if (in->clen>10) + assert(in->C[10] == 'i'); + if (in->clen>100) + assert(in->C[100] == 'a'); + if (in->clen>500) + assert(in->C[500] == 'o'); + std::cout << "message [size=" << in->clen << "] id=" << processedItems << " OK\n"; + } + + delete in; + return this->GO_ON; } void svc_end(){ @@ -85,16 +133,15 @@ struct MiNode : ff::ff_minode_t{ } }; - int main(int argc, char*argv[]){ DFF_Init(argc, argv); if (argc < 7){ - std::cout << "Usage: " << argv[0] << "#items #byteXitem #execTimeSource #execTimeSink #nw_sx #nw_dx" << std::endl; + std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #nw_sx #nw_dx" << std::endl; return -1; } - + bool check = false; int items = atoi(argv[1]); long bytexItem = atol(argv[2]); int execTimeSource = atoi(argv[3]); @@ -102,6 +149,10 @@ int main(int argc, char*argv[]){ int numWorkerSx = atoi(argv[5]); int numWorkerDx = atoi(argv[6]); + char* p=nullptr; + if ((p=getenv("CHECK_DATA"))!=nullptr) check=true; + printf("chackdata = %s\n", p); + ff_pipeline mainPipe; ff::ff_a2a a2a; @@ -111,10 +162,10 @@ int main(int argc, char*argv[]){ std::vector dxWorkers; for(int i = 0; i < numWorkerSx; i++) - sxWorkers.push_back(new MoNode(ceil((double)items/numWorkerSx), execTimeSource, bytexItem)); + sxWorkers.push_back(new MoNode(ceil((double)items/numWorkerSx), execTimeSource, bytexItem, check)); for(int i = 0; i < numWorkerDx; i++) - dxWorkers.push_back(new MiNode(execTimeSink)); + dxWorkers.push_back(new MiNode(execTimeSink, check)); a2a.add_firstset(sxWorkers); a2a.add_secondset(dxWorkers); @@ -124,13 +175,30 @@ int main(int argc, char*argv[]){ auto g1 = a2a.createGroup("G1"); auto g2 = a2a.createGroup("G2"); - for(int i = 0; i < numWorkerSx; i++) g1.out << sxWorkers[i]; - - for(int i = 0; i < numWorkerDx; i++) g2.in << dxWorkers[i]; - if (mainPipe.run_and_wait_end()<0) { - error("running mainPipe\n"); - return -1; - } - -} \ No newline at end of file + for(int i = 0; i < numWorkerSx; i++) { +#if defined(MANUAL_SERIALIZATION) + g1.out <<= packup(sxWorkers[i], [](ExcType* in) {return std::make_pair((char*)in, in->clen+sizeof(ExcType)); }); +#else + g1.out << sxWorkers[i]; +#endif + } + for(int i = 0; i < numWorkerDx; i++) { +#if defined(MANUAL_SERIALIZATION) + g2.in <<= packup(dxWorkers[i], [](char* in, size_t len) { + ExcType* p = new (in) ExcType(true); + p->C = in + sizeof(ExcType); + p->clen = len - sizeof(ExcType); + return p; + }); +#else + g2.in << dxWorkers[i]; +#endif + } + + if (mainPipe.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } + return 0; +} From 9d7076d40f6d691f6e825739ce82105520d53f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Sat, 17 Apr 2021 16:45:50 +0200 Subject: [PATCH 051/202] Fixed custom serialization operators annotation in instantiating WrapperINOUT --- ff/distributed/ff_dgroup.hpp | 36 +++++++++++------------ ff/distributed/ff_wrappers.hpp | 8 ++--- tests/distributed/test_parametricPerf.cpp | 9 +++--- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 9dc54709..78adf503 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -345,7 +345,7 @@ MySet& MySet::operator<<(ff_node_t* node){ if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ std::cout << "Annoted INOUT!" << std::endl; - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); @@ -369,7 +369,7 @@ MySet& MySet::operator<<(ff_minode_t* node){ if (!handle.empty()){// the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); @@ -389,7 +389,7 @@ MySet& MySet::operator<<(ff_monode_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); @@ -411,7 +411,7 @@ MySet& MySet::operator<<(ff_node_t* node){ if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ std::cout << "Annoted INOUT!" << std::endl; - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); @@ -430,7 +430,7 @@ MySet& MySet::operator<<(ff_minode_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); @@ -451,7 +451,7 @@ MySet& MySet::operator<<(ff_monode_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, false)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); @@ -471,7 +471,7 @@ MySet& MySet::operator<<=(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); @@ -490,7 +490,7 @@ MySet& MySet::operator<<=(std::pair*, Function> nod if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, true, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second)}); } } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); @@ -510,7 +510,7 @@ MySet& MySet::operator<<=(ff_minode_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); @@ -530,7 +530,7 @@ MySet& MySet::operator<<=(std::pair*, Function> n if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, true, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second)}); } } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); @@ -550,7 +550,7 @@ MySet& MySet::operator<<=(ff_monode_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); @@ -572,7 +572,7 @@ MySet& MySet::operator<<=(std::pair*, Function> n if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, true, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second)}); } } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); @@ -594,7 +594,7 @@ MySet& MySet::operator<<=(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); @@ -613,7 +613,7 @@ MySet& MySet::operator<<=(std::pair*, Function> n if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, true, nullptr, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, nullptr, nodeFun.second)}); } } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); @@ -634,7 +634,7 @@ MySet& MySet::operator<<=(ff_minode_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); @@ -655,7 +655,7 @@ MySet& MySet::operator<<=(std::pair*, Function> if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, true, nullptr, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, nullptr, nodeFun.second)}); } } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); @@ -676,7 +676,7 @@ MySet& MySet::operator<<=(ff_monode_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, true)}); + this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); } } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); @@ -695,7 +695,7 @@ MySet& MySet::operator<<=(std::pair*, Function> if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, true, nullptr, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, nullptr, nodeFun.second)}); } } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer(), nodeFun.second)}); diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 1f1acd10..18cacf07 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -221,26 +221,26 @@ class WrapperINOUT: public internal_mi_transformer { using ff_serialize_F_t = std::function(std::add_pointer_t)>; using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 03e244a4..8efb9f44 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -48,8 +48,7 @@ struct ExcType { char* C = nullptr; bool contiguous; - -#if !defined(MANUAL_SERIALIZATION) + template void serialize(Archive & archive) { archive(clen); @@ -59,7 +58,7 @@ struct ExcType { } archive(cereal::binary_data(C, clen)); } -#endif + }; static ExcType* allocateExcType(size_t size, bool setdata=false) { @@ -178,14 +177,14 @@ int main(int argc, char*argv[]){ for(int i = 0; i < numWorkerSx; i++) { #if defined(MANUAL_SERIALIZATION) - g1.out <<= packup(sxWorkers[i], [](ExcType* in) {return std::make_pair((char*)in, in->clen+sizeof(ExcType)); }); + g1.out <<= packup(sxWorkers[i], [](ExcType* in) -> std::pair {return std::make_pair((char*)in, in->clen+sizeof(ExcType)); }); #else g1.out << sxWorkers[i]; #endif } for(int i = 0; i < numWorkerDx; i++) { #if defined(MANUAL_SERIALIZATION) - g2.in <<= packup(dxWorkers[i], [](char* in, size_t len) { + g2.in <<= packup(dxWorkers[i], [](char* in, size_t len) -> ExcType* { ExcType* p = new (in) ExcType(true); p->C = in + sizeof(ExcType); p->clen = len - sizeof(ExcType); From c2f5cef593adef192bcd1745b334ed8074af899c Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sun, 18 Apr 2021 10:26:57 +0200 Subject: [PATCH 052/202] improvement of "manual" serialization, cosmetic modifications to some tests --- ff/distributed/ff_wrappers.hpp | 22 ++++++++-------- tests/distributed/dwordcount/dwordcount.cpp | 11 +++----- tests/distributed/test_parametricPerf.cpp | 28 +++++++++++++-------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 18cacf07..f52edc23 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -24,26 +24,26 @@ class WrapperIN: public internal_mi_transformer { public: using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } - WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } - WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } template - WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer = nullptr): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } @@ -68,7 +68,7 @@ class WrapperIN: public internal_mi_transformer { // deserialize the buffer into a heap allocated buffer msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - if (finalizer_) data = finalizer_(msg->data.getPtr(), msg->data.getLen()); + data = finalizer_(msg->data.getPtr(), msg->data.getLen()); } delete msg; @@ -221,26 +221,26 @@ class WrapperINOUT: public internal_mi_transformer { using ff_serialize_F_t = std::function(std::add_pointer_t)>; using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer = nullptr, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -267,7 +267,7 @@ class WrapperINOUT: public internal_mi_transformer { // deserialize the buffer into a heap allocated buffer msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - if (finalizer_) data = finalizer_(msg->data.getPtr(), msg->data.getLen()); + data = finalizer_(msg->data.getPtr(), msg->data.getLen()); } delete msg; diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index f4177b4b..c7f05c28 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -80,11 +80,6 @@ struct result_t { char key[MAXWORD]; // key word uint64_t id; // id that indicates the current number of occurrences of the key word uint64_t ts; // timestamp - - template - void serialize(Archive & archive) { - archive(key, id, ts); - } }; vector dataset; // contains all the input tuples in memory @@ -335,7 +330,8 @@ int main(int argc, char* argv[]) { pipe0->add_stage(sp); L.push_back(pipe0); - G1.out << sp; + // using the default serialization since the result_t is contiguous in memory + G1.out <<= sp; } for (size_t i=0;iadd_stage(S[i]); R.push_back(pipe1); - G2.in << C[i]; + // using the default deserialization + G2.in <<= C[i]; } a2a.add_firstset(L, 0, true); diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 8efb9f44..9301bc86 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -19,9 +19,9 @@ #define MANUAL_SERIALIZATION 1 +// ------------------------------------------------------ std::mutex mtx; // used only for pretty printing - -float active_delay(int msecs) { +static inline float active_delay(int msecs) { // read current time float x = 1.25f; auto start = std::chrono::high_resolution_clock::now(); @@ -35,7 +35,14 @@ float active_delay(int msecs) { } return x; } - +// this assert will not be removed by -DNDEBUG +#define myassert(c) { \ + if (!(c)) { \ + std::cerr << "ERROR: assert at line " << __LINE__ << " failed\n"; \ + abort(); \ + } \ + } +// ----------------------------------------------------- struct ExcType { ExcType():contiguous(false) {} ExcType(bool): contiguous(true) {} @@ -53,7 +60,7 @@ struct ExcType { void serialize(Archive & archive) { archive(clen); if (!C) { - assert(!contiguous); + myassert(!contiguous); C = new char[clen]; } archive(cereal::binary_data(C, clen)); @@ -77,6 +84,7 @@ static ExcType* allocateExcType(size_t size, bool setdata=false) { if (size>500) p->C[500] = 'o'; } + p->C[p->clen-1] = 'F'; return p; } @@ -112,16 +120,16 @@ struct MiNode : ff::ff_minode_t{ if (execTime) active_delay(this->execTime); ++processedItems; if (checkdata) { - assert(in->C[0] == 'c'); + myassert(in->C[0] == 'c'); if (in->clen>10) - assert(in->C[10] == 'i'); + myassert(in->C[10] == 'i'); if (in->clen>100) - assert(in->C[100] == 'a'); + myassert(in->C[100] == 'a'); if (in->clen>500) - assert(in->C[500] == 'o'); - std::cout << "message [size=" << in->clen << "] id=" << processedItems << " OK\n"; + myassert(in->C[500] == 'o'); + std::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; } - + myassert(in->C[in->clen-1] == 'F'); delete in; return this->GO_ON; } From 66b6511a6efcd16bc773ea3c42fc13f142021f6c Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sun, 18 Apr 2021 17:08:14 +0200 Subject: [PATCH 053/202] added "a naive" form of batching in the distributed wordcount (dwordcountb.cpp) --- tests/distributed/dwordcount/dwordcount.cpp | 7 + tests/distributed/dwordcount/dwordcountb.cpp | 436 +++++++++++++++++++ 2 files changed, 443 insertions(+) create mode 100644 tests/distributed/dwordcount/dwordcountb.cpp diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index c7f05c28..73ed115e 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -350,6 +350,13 @@ int main(int argc, char* argv[]) { ff_pipeline pipeMain(false, qlen, qlen, true); pipeMain.add_stage(&a2a); +#if 0 + if (DFF_getMyGroup() == "G1") { + threadMapper::instance()->setMappingList("0,1,2,3,4,5,6,7,8,9,10,11, 24,25,26,27,28,29,30,31,32,33,34,35"); + } else { + threadMapper::instance()->setMappingList("12,13,14,15,16,17,18,19,20,21,22,23, 36,37,38,39,40,41,42,43,44,45,46,47"); + } +#endif std::cout << "Starting " << pipeMain.numThreads() << " threads\n"; /// evaluate topology execution time volatile unsigned long start_time_main_usecs = current_time_usecs(); diff --git a/tests/distributed/dwordcount/dwordcountb.cpp b/tests/distributed/dwordcount/dwordcountb.cpp new file mode 100644 index 00000000..7d3e55b6 --- /dev/null +++ b/tests/distributed/dwordcount/dwordcountb.cpp @@ -0,0 +1,436 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + +/* + * The Source produces a continuous stream of lines of a text file. + * The Splitter tokenizes each line producing a new output item for each + * word extracted. The Counter receives single words from the line + * splitter and counts how many occurrences of the same word appeared + * on the stream until that moment. The Sink node receives every result + * produced by the word counter and counts the total number of words. + * + * One possible FastFlow graph is the following: + * + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * + * /<---- pipe1 ---->/ /<--- pipe2 --->/ + * /<----------------- a2a ------------------>/ + * + * G1: pipe1 + * G2: pipe2 + * + */ + +#define FF_BOUNDED_BUFFER +#define DEFAULT_BUFFER_CAPACITY 2048 +#define BYKEY true + +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace ff; +using namespace std; + +const size_t qlen = DEFAULT_BUFFER_CAPACITY; +const int MAXLINE=128; +const int MAXWORD=32; + +struct tuple_t { + char text_line[MAXLINE]; // line of the parsed dataset (text, book, ...) + size_t key; // line number + uint64_t id; // id set to zero + uint64_t ts; // timestamp +}; + +struct result_t { + char key[MAXWORD]; // key word + uint64_t id; // id that indicates the current number of occurrences of the key word + uint64_t ts; // timestamp + + template + void serialize(Archive & archive) { + archive(key,id,ts); + } +}; +struct Result_t { + std::vector keys; + + template + void serialize(Archive & archive) { + archive(keys); + } +}; + + +vector dataset; // contains all the input tuples in memory +atomic total_lines=0; // total number of lines processed by the system +atomic total_bytes=0; // total number of bytes processed by the system + +/// application run time (source generates the stream for app_run_time seconds, then sends out EOS) +unsigned long app_run_time = 15; // time in seconds + +static inline unsigned long current_time_usecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000L + (t.tv_nsec / 1000); +} +static inline unsigned long current_time_nsecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000000L + t.tv_nsec; +} + +struct Source: ff_monode_t { + size_t next_tuple_idx = 0; // index of the next tuple to be sent + int generations = 0; // counts the times the file is generated + long generated_tuples = 0; // tuples counter + long generated_bytes = 0; // bytes counter + + // time variables + unsigned long app_start_time; // application start time + unsigned long current_time; + + Source(const unsigned long _app_start_time): + app_start_time(_app_start_time),current_time(_app_start_time) { + } + + tuple_t* svc(tuple_t*) { + while(1) { + tuple_t* t = new tuple_t; + assert(t); + + if (generated_tuples > 0) current_time = current_time_nsecs(); + if (next_tuple_idx == 0) generations++; // file generations counter + generated_tuples++; // tuples counter + // put a timestamp and send the tuple + *t = dataset.at(next_tuple_idx); + generated_bytes += sizeof(tuple_t); + t->ts = current_time - app_start_time; + ff_send_out(t); + ++next_tuple_idx; + next_tuple_idx %= dataset.size(); + // EOS reached + if (current_time - app_start_time >= (app_run_time*1000000000L) && next_tuple_idx == 0) + break; + } + total_lines.fetch_add(generated_tuples); + total_bytes.fetch_add(generated_bytes); + return EOS; + } +}; +struct Splitter: ff_monode_t { + Splitter(long noutch, long buffered_lines):noutch(noutch),buffered_lines(buffered_lines), outV(noutch,nullptr) { } + + // int svc_init() { + // noutch=get_num_outchannels(); // TODO: this doesn't work, it must be fixed! + // /return 0; + // } + + Result_t* svc(tuple_t* in) { + char *tmpstr; + char *token = strtok_r(in->text_line, " ", &tmpstr); + while (token) { +#if defined(BYKEY) + int ch = std::hash()(std::string(token)) % noutch; +#else + int ch = ++idx % noutch; +#endif + if (outV[ch] == nullptr) { + Result_t* r = new Result_t; + assert(r); + outV[ch] = r; + } + result_t r; + strncpy(r.key, token, MAXWORD-1); + r.key[MAXWORD-1]='\0'; + r.ts = in->ts; + outV[ch]->keys.push_back(r); + + token = strtok_r(NULL, " ", &tmpstr); + } + ++nmsgs; + if (nmsgs>=buffered_lines) { + for(long i=0;i outV; +}; + +struct Counter: ff_minode_t { + Result_t* svc(Result_t* in) { + for(size_t i=0;ikeys.size();++i) { + ++M[std::string(in->keys[i].key)]; + // number of occurrences of the string word up to now + in->keys[i].id = M[std::string(in->keys[i].key)]; + } + return in; + } + size_t unique() { + // std::cout << "Counter:\n"; + // for (const auto& kv : M) { + // std::cout << kv.first << " --> " << kv.second << "\n"; + // } + return M.size(); + } + std::map M; +}; + +struct Sink: ff_node_t { + Result_t* svc(Result_t* in) { + words+= in->keys.size(); + delete in; + return GO_ON; + } + size_t words=0; // total number of words received +}; + + +/** + * @brief Parse the input file and create all the tuples + * + * The created tuples are maintained in memory. The source node will generate the stream by + * reading all the tuples from main memory. + * + * @param file_path the path of the input dataset file + */ +int parse_dataset_and_create_tuples(const string& file_path) { + ifstream file(file_path); + if (file.is_open()) { + size_t all_records = 0; // counter of all records (dataset line) read + string line; + while (getline(file, line)) { + // process file line + if (!line.empty()) { + if (line.length() > MAXLINE) { + std::cerr << "ERROR INCREASE MAXLINE\n"; + exit(EXIT_FAILURE); + } + tuple_t t; + strncpy(t.text_line, line.c_str(), MAXLINE-1); + t.text_line[MAXLINE-1]='\0'; + t.key = all_records; + t.id = 0; + t.ts = 0; + all_records++; + dataset.push_back(t); + } + } + file.close(); + } else { + std::cerr << "Unable to open the file " << file_path << "\n"; + return -1; + } + return 0; +} + + + +int main(int argc, char* argv[]) { + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + /// parse arguments from command line + std::string file_path(""); + size_t source_par_deg = 0; + size_t sink_par_deg = 0; + long buffered_lines = 100; + + if (argc >= 3 || argc == 1) { + int option = 0; + while ((option = getopt(argc, argv, "f:p:t:b:")) != -1) { + switch (option) { + case 'f': file_path=string(optarg); break; + case 'p': { + vector par_degs; + string pars(optarg); + stringstream ss(pars); + for (size_t i; ss >> i;) { + par_degs.push_back(i); + if (ss.peek() == ',') + ss.ignore(); + } + if (par_degs.size() != 2) { + std::cerr << "Error in parsing the input arguments -p, the format is n,m\n"; + return -1; + } else { + source_par_deg = par_degs[0]; + sink_par_deg = par_degs[1]; + } + } break; + case 't': { + long t = stol(optarg); + if (t<=0 || t > 100) { + std::cerr << "Wrong value for the '-t' option, it should be in the range [1,100]\n"; + return -1; + } + app_run_time = t; + } break; + case 'b': { + buffered_lines = stol(optarg); + if (buffered_lines<=0 || buffered_lines>10000000) { + std::cerr << "Wrong value fro the '-b' option\n"; + return -1; + } + } break; + default: { + std::cerr << "Error in parsing the input arguments\n"; + return -1; + } + } + } + } else { + std::cerr << "Parameters: -p -f -t -b \n"; + return -1; + } + if (file_path.length()==0) { + std::cerr << "The file path is empty, please use option -f \n"; + return -1; + } + if (source_par_deg == 0 || sink_par_deg==0) { + std::cerr << "Wrong values for the parallelism degree, please use option -p \n"; + return -1; + } + + if (DFF_getMyGroup() == "G1") { + /// data pre-processing + if (parse_dataset_and_create_tuples(file_path)< 0) + return -1; + + cout << "Executing WordCount with parameters:" << endl; + cout << " * source/splitter : " << source_par_deg << endl; + cout << " * counter/sink : " << sink_par_deg << endl; + cout << " * running time : " << app_run_time << " (s)\n"; + } + + /// application starting time + unsigned long app_start_time = current_time_nsecs(); + + std::vector C(sink_par_deg); + std::vector S(sink_par_deg); + std::vector L; // left and right workers of the A2A + std::vector R; + + ff_a2a a2a(false, qlen, qlen, true); + + auto G1 = a2a.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + + for (size_t i=0;iadd_stage(new Source(app_start_time)); + Splitter* sp = new Splitter(sink_par_deg, buffered_lines); + pipe0->add_stage(sp); + L.push_back(pipe0); + + G1.out << sp; + } + for (size_t i=0;iadd_stage(C[i]); + pipe1->add_stage(S[i]); + R.push_back(pipe1); + + G2.in << C[i]; + } + + a2a.add_firstset(L, 0, true); + a2a.add_secondset(R, true); + ff_pipeline pipeMain(false, qlen, qlen, true); + pipeMain.add_stage(&a2a); +#if 0 + if (DFF_getMyGroup() == "G1") { + threadMapper::instance()->setMappingList("0,1,2,3,4,5,6,7,8,9,10,11, 24,25,26,27,28,29,30,31,32,33,34,35"); + } else { + threadMapper::instance()->setMappingList("12,13,14,15,16,17,18,19,20,21,22,23, 36,37,38,39,40,41,42,43,44,45,46,47"); + } +#endif + + std::cout << "Starting " << pipeMain.numThreads() << " threads\n"; + /// evaluate topology execution time + volatile unsigned long start_time_main_usecs = current_time_usecs(); + if (pipeMain.run_and_wait_end()<0) { + error("running pipeMain\n"); + return -1; + } + volatile unsigned long end_time_main_usecs = current_time_usecs(); + cout << "Exiting" << endl; + double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); + cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; + if (DFF_getMyGroup() == "G1") { + cout << "total_lines sent : " << total_lines << "\n"; + cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; + //double throughput = total_lines / elapsed_time_seconds; + //double mbs = (double)((total_bytes / 1048576) / elapsed_time_seconds); + //cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; + } else { + size_t words=0; + size_t unique=0; + for(size_t i=0;iwords; + unique+= C[i]->unique(); + delete S[i]; + delete C[i]; + } + cout << "words : " << words << "\n"; + cout << "unique : " << unique<< "\n"; + } + + return 0; +} From 5f7353bd1f2bc47b1ef3eaab04a20fd8ef9cc220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 19 Apr 2021 10:40:38 +0200 Subject: [PATCH 054/202] Added conditional compilation in dff_run to allow compiling it in G++ 7 --- ff/distributed/loader/dff_run.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index de76c7e8..5aca967e 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -13,6 +12,14 @@ #include #include +#if(defined(_MSC_VER) or (defined(__GNUC__) and (7 <= __GNUC_MAJOR__))) + #include + using n_fs = ::std::filesystem; +#else + #include + using n_fs = ::std::experimental::filesystem; +#endif + static inline unsigned long getusec() { struct timeval tv; @@ -131,7 +138,7 @@ int main(int argc, char** argv) { usage(argv[0]); exit(EXIT_FAILURE); } - if (!std::filesystem::exists(std::string(argv[optind]))) { + if (!n_fs::exists(std::string(argv[optind]))) { std::cerr << "ERROR: Unable to find the executable file (we found as executable \'" << argv[optind] << "\')\n"; exit(EXIT_FAILURE); } From ec9080cb08efead2b53846b5a9daafd62140c446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 19 Apr 2021 10:54:36 +0200 Subject: [PATCH 055/202] Added conditional compilation in dff_run to allow compiling it in G++ 7 --- ff/distributed/loader/Makefile | 2 +- ff/distributed/loader/dff_run.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ff/distributed/loader/Makefile b/ff/distributed/loader/Makefile index 508f842b..e7623b9f 100644 --- a/ff/distributed/loader/Makefile +++ b/ff/distributed/loader/Makefile @@ -38,7 +38,7 @@ endif CXXFLAGS += -Wall -LIBS = -pthread +LIBS = -pthread -lstdc++fs INCLUDES = $(INCS) SOURCES = $(wildcard *.cpp) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 5aca967e..ddd10c1f 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -14,10 +14,10 @@ #if(defined(_MSC_VER) or (defined(__GNUC__) and (7 <= __GNUC_MAJOR__))) #include - using n_fs = ::std::filesystem; + namespace n_fs = std::filesystem; #else #include - using n_fs = ::std::experimental::filesystem; + namespace n_fs = std::experimental::filesystem; #endif From 8ed953a32a9204a04d0d178dbf1d44655d12b8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 22 Apr 2021 14:28:50 +0200 Subject: [PATCH 056/202] Bug fixes and delteded some prints in the RTS --- ff/distributed/ff_dgroup.hpp | 10 ++++------ ff/distributed/ff_dreceiver.hpp | 7 ++----- ff/distributed/loader/dff_run.cpp | 14 ++++++++++---- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 78adf503..467118af 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -260,7 +260,7 @@ class dGroup : public ff_farm { // in/out nodes left to be added to the farm. The next lines does it for (const auto& pair : this->inout_){ - std::cout << "Added INOUT node" << std::endl; + //std::cout << "Added INOUT node" << std::endl; this->add_workers({pair.second}); } @@ -270,17 +270,17 @@ class dGroup : public ff_farm { // create receiver if (!isSource()){ - std::cout << "Creating the receiver!" << std::endl; + //std::cout << "Creating the receiver!" << std::endl; this->add_emitter(new ff_dreceiver(0 , this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! } // create sender if (!isSink()){ - std::cout << "Creating the sender!" << std::endl; + //std::cout << "Creating the sender!" << std::endl; this->add_collector(new ff_dsender(this->destinations), true); } - std::cout << "Built a farm of " << this->getNWorkers() << " workers!" << std::endl; + //std::cout << "Built a farm of " << this->getNWorkers() << " workers!" << std::endl; // call the base class (ff_farm)'s prepare return 0; } @@ -344,7 +344,6 @@ MySet& MySet::operator<<(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - std::cout << "Annoted INOUT!" << std::endl; this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else @@ -410,7 +409,6 @@ MySet& MySet::operator<<(ff_node_t* node){ if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - std::cout << "Annoted INOUT!" << std::endl; this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index ff9c6cc6..1317e280 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -30,8 +30,6 @@ class ff_dreceiver: public ff_monode_t { oarchive << reachableDestinations; - std::cout << "Sending routing table (" << buff.getLen() << " bytes)" << std::endl; - size_t sz = htobe64(buff.getLen()); struct iovec iov[1]; iov[0].iov_base = &sz; @@ -139,8 +137,9 @@ class ff_dreceiver: public ff_monode_t { return -1; } - for (const auto& e : routingTable) + /*for (const auto& e : routingTable) std::cout << "Entry: " << e.first << " -> " << e.second << std::endl; + */ return 0; } @@ -150,7 +149,6 @@ class ff_dreceiver: public ff_monode_t { #ifdef LOCAL unlink(this->acceptAddr.address.c_str()); #endif - std::cout << "Receiver of distribute group " << distributedGroupId << " shutdown " << std::endl; } /* Here i should not care of input type nor input data since they come from a socket listener. @@ -159,7 +157,6 @@ class ff_dreceiver: public ff_monode_t { message_t *svc(message_t* task) { /* here i should receive the task via socket */ - std::cout << "Receiver correctly started... start to listening" << std::endl; fd_set set, tmpset; // intialize both sets (master, temp) FD_ZERO(&set); diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index ddd10c1f..f4e0ded5 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -43,7 +43,7 @@ inline std::vector split (const std::string &s, char delim) { } struct G { - std::string name, host; + std::string name, host, preCmd; FILE* fd = nullptr; template @@ -57,12 +57,18 @@ struct G { } catch (cereal::Exception&) { host = "127.0.0.1"; // set the host to localhost if not found in config file! ar.setNextName(nullptr); - } + } + + try { + ar(cereal::make_nvp("preCmd", preCmd)); + } catch (cereal::Exception&) { + ar.setNextName(nullptr); + } } void run(){ - char b[150]; // ssh -t - sprintf(b, " %s %s %s --DFF_Config=%s --DFF_GName=%s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : "") , executable.c_str(), configFile.c_str(), this->name.c_str()); + char b[350]; // ssh -t + sprintf(b, " %s %s %s %s --DFF_Config=%s --DFF_GName=%s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : "") , this->preCmd.c_str(), executable.c_str(), configFile.c_str(), this->name.c_str()); std::cout << "Executing the following command: " << b << std::endl; fd = popen(b, "r"); From dbacb90da1c9a6087e676f7475bb6f995a7fe79e Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 18 Jun 2021 11:13:05 +0200 Subject: [PATCH 057/202] removed "reduce channel" feature from all2ll, other minor modifications --- ff/all2all.hpp | 57 +++++++++++--------------------- ff/farm.hpp | 42 +++++++++++++---------- ff/pipeline.hpp | 45 ++++++++++++++----------- tests/test8.cpp | 2 +- tests/test_all-or-none.cpp | 5 ++- tests/test_blk.cpp | 10 +++--- tests/test_blk3.cpp | 5 ++- tests/test_blk4.cpp | 5 ++- tests/test_stopstartthreads3.cpp | 5 ++- tests/test_torus2.cpp | 4 +-- 10 files changed, 85 insertions(+), 95 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 667dae5f..85c7c1ed 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -180,22 +180,7 @@ class ff_a2a: public ff_node { size_t nworkers1 = workers1.size(); size_t nworkers2 = workers2.size(); - // NOTE: the nodes of the second set will have in input a multi-producer queue - // if reduce_channels=true. The node itself is multi-input because otherwise we have - // problems with the blocking stuff. - if (reduce_channels) { - int ondemand = workers1[0]->ondemand_buffer(); // NOTE: here we suppose that all nodes in workers1 are homogeneous! - for(size_t i=0;iset_input(t); - for(size_t j=0;jset_output(t); - } - } else { + { int ondemand = workers1[0]->ondemand_buffer(); // NOTE: here we suppose that all nodes in workers1 are homogeneous! svector L; @@ -209,9 +194,7 @@ class ff_a2a: public ff_node { for(size_t i=0;iset_output(t); @@ -255,17 +238,10 @@ class ff_a2a: public ff_node { public: - /** - * - * reduce_channels should be set to true only if we want/need to reduce the number of channel - * by making use of multi-producer input queues. - * - */ - ff_a2a(bool reduce_channels=false, + ff_a2a(bool notusedanymore=false, int in_buffer_entries=DEFAULT_BUFFER_CAPACITY, int out_buffer_entries=DEFAULT_BUFFER_CAPACITY, - bool fixedsize=FF_FIXED_SIZE):prepared(false),fixedsize(fixedsize), - reduce_channels(reduce_channels), + bool fixedsize=FF_FIXED_SIZE):prepared(false),fixedsizeIN(fixedsize),fixedsizeOUT(fixedsize), in_buffer_entries(in_buffer_entries), out_buffer_entries(out_buffer_entries) {} @@ -396,7 +372,6 @@ class ff_a2a: public ff_node { return false; } - bool reduceChannel() const { return reduce_channels;} void skipfirstpop(bool sk) { for(size_t i=0;icreate_input_buffer_mp(nentries,fixedsize, neos)==-1) return -1; - } - return 0; - } - int create_output_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { size_t nworkers2 = workers2.size(); for(size_t i=0;i workers1; // first set, nodes must be multi-output diff --git a/ff/farm.hpp b/ff/farm.hpp index e73e5893..9dcdf4bc 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -270,7 +270,7 @@ class ff_farm: public ff_node { // accelerator if (has_input_channel) { - if (create_input_buffer(in_buffer_entries, fixedsize)<0) { + if (create_input_buffer(in_buffer_entries, fixedsizeIN)<0) { error("FARM, creating input buffer\n"); return -1; } @@ -315,7 +315,7 @@ class ff_farm: public ff_node { if (a2a_first->create_input_buffer((int) (ondemand ? ondemand: in_buffer_entries), - (ondemand ? true: fixedsize))<0) return -1; + (ondemand ? true: fixedsizeIN))<0) return -1; const svector& W1 = a2a_first->getFirstSet(); for(size_t i=0;icreate_input_buffer((int) (ondemand ? ondemand: in_buffer_entries), - (ondemand ? true: fixedsize))<0) return -1; + (ondemand ? true: fixedsizeIN))<0) return -1; lb->register_worker(workers[i]); } @@ -349,7 +349,7 @@ class ff_farm: public ff_node { // NOTE: the following call might fail because the buffers were already created for example by // the pipeline that contains this stage - a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize)); + a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT)); for(size_t i=0;i w(1); @@ -371,7 +371,7 @@ class ff_farm: public ff_node { } else { // NOTE: the following call might fail because the buffers were already created for example by // the pipeline that contains this stage - if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize))<0) { + if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) { if (lb->masterworker()) return -1; // something went wrong } if (lb->masterworker()) { @@ -427,21 +427,21 @@ class ff_farm: public ff_node { static int idx=0; for(size_t j=0;jset_output(t); gt->register_worker(t); } } else { // single node multi-output - ff_node* t = new ff_buffernode(out_buffer_entries,fixedsize, i); + ff_node* t = new ff_buffernode(out_buffer_entries,fixedsizeOUT, i); internalSupportNodes.push_back(t); workers[i]->set_output(t); if (!lb->masterworker()) workers[i]->set_output_buffer(t->get_out_buffer()); gt->register_worker(t); } } else { // standard worker or composition where the second stage is not multi-output - if (workers[i]->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize))<0) + if (workers[i]->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) return -1; assert(!lb->masterworker()); gt->register_worker(workers[i]); @@ -729,7 +729,7 @@ class ff_farm: public ff_node { * @param input_ch \p true for enabling the input stream */ ff_farm(const std::vector& W, ff_node *const Emitter=NULL, ff_node *const Collector=NULL, bool input_ch=false): - has_input_channel(input_ch),collector_removed(false),ordered(false),fixedsize(FF_FIXED_SIZE), + has_input_channel(input_ch),collector_removed(false),ordered(false),fixedsizeIN(FF_FIXED_SIZE),fixedsizeOUT(FF_FIXED_SIZE), myownlb(true),myowngt(true),worker_cleanup(false),emitter_cleanup(false), collector_cleanup(false),ondemand(0), in_buffer_entries(DEFAULT_BUFFER_CAPACITY), @@ -770,7 +770,7 @@ class ff_farm: public ff_node { * \param out_buffer_entries = output queue length * \param max_num_workers = highest number of farm's worker * \param worker_cleanup = true deallocate worker object at exit - * \param fixedsize = true uses only fixed size queue + * \param fixedsize = true uses only fixed size queue (both between Emitter and Workers and between Workers and Collector) */ explicit ff_farm(bool input_ch=false, int in_buffer_entries=DEFAULT_BUFFER_CAPACITY, @@ -778,7 +778,7 @@ class ff_farm: public ff_node { bool worker_cleanup=false, // NOTE: by default no cleanup at exit is done ! size_t max_num_workers=DEF_MAX_NUM_WORKERS, bool fixedsize=FF_FIXED_SIZE): - has_input_channel(input_ch),collector_removed(false), ordered(false), fixedsize(fixedsize), + has_input_channel(input_ch),collector_removed(false), ordered(false), fixedsizeIN(FF_FIXED_SIZE),fixedsizeOUT(FF_FIXED_SIZE), myownlb(true),myowngt(true),worker_cleanup(worker_cleanup),emitter_cleanup(false), collector_cleanup(false), ondemand(0), in_buffer_entries(in_buffer_entries), @@ -808,7 +808,8 @@ class ff_farm: public ff_node { collector_cleanup = f.collector_cleanup; max_nworkers = f.max_nworkers; internalSupportNodes= f.internalSupportNodes; - fixedsize = f.fixedsize; + fixedsizeIN = f.fixedsizeIN; + fixedsizeOUT = f.fixedsizeOUT; myownlb = f.myownlb; myowngt = f.myowngt; workers = f.workers; @@ -849,7 +850,8 @@ class ff_farm: public ff_node { emitter_cleanup = f.emitter_cleanup; collector_cleanup = f.collector_cleanup; max_nworkers = f.max_nworkers; - fixedsize = f.fixedsize; + fixedsizeIN = f.fixedsizeIN; + fixedsizeOUT = f.fixedsizeOUT; emitter = f.emitter; collector = f.collector; lb = f.lb; gt = f.gt; @@ -1695,9 +1697,15 @@ class ff_farm: public ff_node { * run_and_wait_end/run_then_freeze/run/....) they have no effect. * */ - void setFixedSize(bool fs) { fixedsize = fs; } - void setInputQueueLength(int sz) { in_buffer_entries = sz; } - void setOutputQueueLength(int sz) { out_buffer_entries = sz;} + void setFixedSize(bool fs) { fixedsizeIN = fixedsizeOUT = fs; } + void setInputQueueLength(int sz, bool fixedsize) { + in_buffer_entries = sz; + fixedsizeIN = fixedsize; + } + void setOutputQueueLength(int sz, bool fixedsize) { + out_buffer_entries = sz; + fixedsizeOUT = fixedsize; + } int numThreads() const { return cardinality(); } @@ -2034,7 +2042,7 @@ class ff_farm: public ff_node { bool has_input_channel; // for the accelerator mode bool collector_removed; bool ordered; - bool fixedsize; + bool fixedsizeIN, fixedsizeOUT; bool myownlb,myowngt; bool worker_cleanup, emitter_cleanup,collector_cleanup; diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index fad46c13..18e5eea9 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -162,7 +162,7 @@ class ff_pipeline: public ff_node { pthread_cond_t *c = NULL; if (curr_single_standard) { bool skip_set_output_blocking = false; - if (nodes_list[i]->create_input_buffer(in_buffer_entries, fixedsize)<0) return -1; + if (nodes_list[i]->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; if (prev_single_standard) { if (nodes_list[i-1]->set_output_buffer(nodes_list[i]->get_in_buffer())<0) return -1; } else { @@ -197,7 +197,7 @@ class ff_pipeline: public ff_node { } if (curr_single_multiinput) { if (prev_single_standard) { - if (nodes_list[i-1]->create_output_buffer(in_buffer_entries, fixedsize)<0) return -1; + if (nodes_list[i-1]->create_output_buffer(in_buffer_entries, fixedsizeOUT)<0) return -1; if (nodes_list[i]->set_input(nodes_list[i-1])<0) return -1; // blocking stuff -------------------------------------------- @@ -215,13 +215,13 @@ class ff_pipeline: public ff_node { // ------------------------------------------------------------ } else { if (prev_single_multioutput) { - ff_node* t = new ff_buffernode(in_buffer_entries,fixedsize, i); + ff_node* t = new ff_buffernode(in_buffer_entries,fixedsizeIN|fixedsizeOUT, i); internalSupportNodes.push_back(t); nodes_list[i-1]->set_output(t); nodes_list[i]->set_input(t); } if (prev_multi_standard) { - if (nodes_list[i-1]->create_output_buffer(out_buffer_entries, fixedsize)<0) return -1; + if (nodes_list[i-1]->create_output_buffer(out_buffer_entries, fixedsizeOUT)<0) return -1; svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); @@ -234,7 +234,7 @@ class ff_pipeline: public ff_node { nodes_list[i-1]->get_out_nodes(w); assert(w.size()); for(size_t j=0;jset_output(t); nodes_list[i]->set_input(t); @@ -269,7 +269,7 @@ class ff_pipeline: public ff_node { error("PIPE, cannot connect stage %d with stage %d because of different cardinality\n", i-1, i); return -1; } - if (a2a->create_input_buffer(in_buffer_entries, fixedsize)<0) return -1; + if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; svector w(1); nodes_list[i]->get_in_nodes(w); assert(w.size() == 1); @@ -288,7 +288,7 @@ class ff_pipeline: public ff_node { // ----------------------------------------------------------- } else { if (prev_single_multioutput) { - if (a2a->create_input_buffer(in_buffer_entries, fixedsize)<0) return -1; + if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; svector w(MAX_NUM_THREADS); nodes_list[i]->get_in_nodes(w); if (nodes_list[i-1]->set_output(w)<0) return -1; @@ -311,7 +311,7 @@ class ff_pipeline: public ff_node { error("PIPE, cannot connect stage %d with stage %d, because of different input/output cardinality (**)\n", i-1, i); return -1; } - if (a2a->create_input_buffer(in_buffer_entries, fixedsize)<0) return -1; + if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; // the previous node can be a farm or a all2all for(size_t i=0;icreate_input_buffer(in_buffer_entries, fixedsize)<0) return -1; + if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; svector w(1); nodes_list[i]->get_in_nodes(w); assert(w.size() == 1); @@ -357,7 +357,7 @@ class ff_pipeline: public ff_node { svector w(MAX_NUM_THREADS); W1[j]->get_in_nodes(w); for(size_t k=0;kset_input(t); nodes_list[i-1]->set_output(t); @@ -375,7 +375,7 @@ class ff_pipeline: public ff_node { return -1; } w.clear(); - if (nodes_list[i-1]->create_output_buffer(in_buffer_entries, fixedsize)<0) return -1; + if (nodes_list[i-1]->create_output_buffer(in_buffer_entries, fixedsizeOUT)<0) return -1; nodes_list[i-1]->get_out_nodes(w); assert(w.size()== W1.size()); @@ -390,7 +390,7 @@ class ff_pipeline: public ff_node { for(size_t k=0;kset_input(t); w[j]->set_output(t); @@ -427,7 +427,7 @@ class ff_pipeline: public ff_node { // Preparation of buffers for the accelerator int ret = 0; if (has_input_channel) { - if (create_input_buffer(in_buffer_entries, fixedsize)<0) { + if (create_input_buffer(in_buffer_entries, fixedsizeIN)<0) { error("PIPE, creating input buffer for the accelerator\n"); ret=-1; } else { @@ -529,7 +529,7 @@ class ff_pipeline: public ff_node { int out_buffer_entries=DEFAULT_BUFFER_CAPACITY, bool fixedsize=FF_FIXED_SIZE): has_input_channel(input_ch), - node_cleanup(false),fixedsize(fixedsize), + node_cleanup(false),fixedsizeIN(fixedsize),fixedsizeOUT(fixedsize), in_buffer_entries(in_buffer_entries), out_buffer_entries(out_buffer_entries) { } @@ -541,7 +541,8 @@ class ff_pipeline: public ff_node { } has_input_channel = p.has_input_channel; node_cleanup = p.node_cleanup; - fixedsize = p.fixedsize; + fixedsizeIN = p.fixedsizeIN; + fixedsizeOUT = p.fixedsizeOUT; in_buffer_entries = p.in_buffer_entries; out_buffer_entries = p.out_buffer_entries; nodes_list = p.nodes_list; @@ -586,9 +587,15 @@ class ff_pipeline: public ff_node { * run_and_wait_end/run_then_freeze/run/....) they have no effect. * */ - void setFixedSize(bool fs) { fixedsize = fs; } - void setXNodeInputQueueLength(int sz) { in_buffer_entries = sz; } - void setXNodeOutputQueueLength(int sz) { out_buffer_entries = sz;} + void setFixedSize(bool fs) { fixedsizeIN = fixedsizeOUT= fs; } + void setXNodeInputQueueLength(int sz, bool fixedsize) { + in_buffer_entries = sz; + fixedsizeIN = fixedsize; + } + void setXNodeOutputQueueLength(int sz, bool fixedsize) { + out_buffer_entries = sz; + fixedsizeOUT = fixedsize; + } int numThreads() const { return cardinality(); } @@ -1618,7 +1625,7 @@ class ff_pipeline: public ff_node { private: bool has_input_channel; // for accelerator bool node_cleanup; - bool fixedsize; + bool fixedsizeIN, fixedsizeOUT; int in_buffer_entries; int out_buffer_entries; svector nodes_list; diff --git a/tests/test8.cpp b/tests/test8.cpp index 0e54b62a..df8b8aec 100644 --- a/tests/test8.cpp +++ b/tests/test8.cpp @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) { secondStage F2; thirdStage F3; ff_Pipe<> pipe(F1,F2,F3); - pipe.setXNodeInputQueueLength(10); + pipe.setXNodeInputQueueLength(10,true); pipe.run_and_wait_end(); std::cout << "Time: " << pipe.ffTime() << "\n"; return 0; diff --git a/tests/test_all-or-none.cpp b/tests/test_all-or-none.cpp index 2628cf17..6df09b81 100644 --- a/tests/test_all-or-none.cpp +++ b/tests/test_all-or-none.cpp @@ -195,9 +195,8 @@ int main(int argc, char* argv[]) { farm.cleanup_workers(); ff_Pipe<> pipe(Gen, farm, Gat); - pipe.setFixedSize(true); - pipe.setXNodeInputQueueLength(512); - pipe.setXNodeOutputQueueLength(512); + pipe.setXNodeInputQueueLength(512,true); + pipe.setXNodeOutputQueueLength(512,true); if (pipe.run_and_wait_end()<0) { error("running pipe\n"); return -1; diff --git a/tests/test_blk.cpp b/tests/test_blk.cpp index 55054189..3fe4df28 100644 --- a/tests/test_blk.cpp +++ b/tests/test_blk.cpp @@ -95,13 +95,11 @@ int main() { return W; } () ); - farm.setFixedSize(true); - farm.setInputQueueLength(nworkers*1); - farm.setOutputQueueLength(nworkers*1); + farm.setInputQueueLength(nworkers*1, true); + farm.setOutputQueueLength(nworkers*1, true); ff_Pipe<> pipe(first,farm,last); - pipe.setFixedSize(true); - pipe.setXNodeInputQueueLength(1); - pipe.setXNodeOutputQueueLength(1); + pipe.setXNodeInputQueueLength(1,true); + pipe.setXNodeOutputQueueLength(1,true); pipe.run_then_freeze(); pipe.wait(); diff --git a/tests/test_blk3.cpp b/tests/test_blk3.cpp index 8326731a..131847a2 100644 --- a/tests/test_blk3.cpp +++ b/tests/test_blk3.cpp @@ -196,9 +196,8 @@ int main(int argc, char* argv[]) { First first(numtasks, ffalloc); ff_Pipe<> pipe(first, farm); - pipe.setFixedSize(true); - pipe.setXNodeInputQueueLength(100); - pipe.setXNodeOutputQueueLength(100); + pipe.setXNodeInputQueueLength(100,true); + pipe.setXNodeOutputQueueLength(100,true); if (pipe.run_and_wait_end()<0) { error("running pipeline\n"); return -1; diff --git a/tests/test_blk4.cpp b/tests/test_blk4.cpp index bc1936b5..28f9532b 100644 --- a/tests/test_blk4.cpp +++ b/tests/test_blk4.cpp @@ -145,9 +145,8 @@ int main(int argc, char* argv[]) { farm2.wrap_around(); ff_Pipe<> pipe(farm1, farm2); - pipe.setFixedSize(true); - pipe.setXNodeInputQueueLength(100); - pipe.setXNodeOutputQueueLength(100); + pipe.setXNodeInputQueueLength(100,true); + pipe.setXNodeOutputQueueLength(100,true); pipe.run_and_wait_end(); return 0; } diff --git a/tests/test_stopstartthreads3.cpp b/tests/test_stopstartthreads3.cpp index c1d31ae8..10614cc7 100644 --- a/tests/test_stopstartthreads3.cpp +++ b/tests/test_stopstartthreads3.cpp @@ -158,9 +158,8 @@ int main(int argc, char *argv[]) { Collector C(10); ofarm.add_emitter(E); ofarm.add_collector(C); - ofarm.setFixedSize(true); - ofarm.setInputQueueLength(4); // setting very small queues - ofarm.setOutputQueueLength(4); + ofarm.setInputQueueLength(4, true); // setting very small queues + ofarm.setOutputQueueLength(4, true); ofarm.run(); ofarm.getgt()->wait(); // waiting for the termination of the collector diff --git a/tests/test_torus2.cpp b/tests/test_torus2.cpp index 02a522fb..7bdc5bcc 100644 --- a/tests/test_torus2.cpp +++ b/tests/test_torus2.cpp @@ -167,8 +167,8 @@ int main(int argc, char * argv[]) { for(unsigned i=1;i Date: Fri, 18 Jun 2021 15:03:12 +0200 Subject: [PATCH 058/202] Introduced OnDemand feature --- ff/distributed/ff_dgroup.hpp | 32 ++++++++++++++++++-------- ff/distributed/ff_network.hpp | 20 +++++++++++++++++ ff/farm.hpp | 42 +++++++++++++++++++++-------------- 3 files changed, 68 insertions(+), 26 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 467118af..c3936c84 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -239,21 +241,32 @@ class dGroup : public ff_farm { for (const auto& pair : this->in_) in_C.push_back(pair.first); for (const auto& pair : this->out_) out_C.push_back(pair.first); + int onDemandQueueLength = 0; if (this->parentStructure->isPipe()) processBB(this->parentStructure, in_C, out_C); - if (this->parentStructure->isAll2All()){ ff_a2a * a2a = (ff_a2a*) this->parentStructure; if (!processBB(a2a, in_C, out_C)){ // if the user has not wrapped the whole a2a, expan its sets - + bool first = false, second = false; + for(ff_node* bb : a2a->getFirstSet()) - processBB(bb, in_C, out_C); + if (processBB(bb, in_C, out_C)) + first = true; for(ff_node* bb : a2a->getSecondSet()) - processBB(bb, in_C, out_C); + if (processBB(bb, in_C, out_C)) + second = true; + + if (first && second) throw FF_Exception("Nodes from first and second of an A2A cannot belong to the same group"); + // if the ondemand scheduling is set in the a2a, i need to adjust the queues of this farm in order to implement the ondemand policy + if (a2a->ondemand_buffer() > 0){ + onDemandQueueLength = a2a->ondemand_buffer(); + if (first) this->setOutputQueueLength(1, true); // always set to 1 the length of the queue between worker and collector (SOURCE side) + if (second) this->set_scheduling_ondemand(a2a->ondemand_buffer()); // set the right length of the queue between emitter and worker (SINK side) + } } } @@ -271,17 +284,18 @@ class dGroup : public ff_farm { // create receiver if (!isSource()){ //std::cout << "Creating the receiver!" << std::endl; - this->add_emitter(new ff_dreceiver(0 , this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! + this->add_emitter(new ff_dreceiverOD(0 , this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB), onDemandQueueLength > 0)); // set right parameters HERE!! } // create sender if (!isSink()){ //std::cout << "Creating the sender!" << std::endl; - this->add_collector(new ff_dsender(this->destinations), true); + if (onDemandQueueLength > 0) + this->add_collector(new ff_dsenderOD(this->destinations, onDemandQueueLength), true); + else + this->add_collector(new ff_dsender(this->destinations), true); + } - - //std::cout << "Built a farm of " << this->getNWorkers() << " workers!" << std::endl; - // call the base class (ff_farm)'s prepare return 0; } diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index e2dc7482..17f4f1a6 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -72,6 +72,9 @@ struct message_t { dataBuffer data; }; +struct ack_t { + char ack = 'A'; +}; struct ff_endpoint { std::string address; @@ -137,4 +140,21 @@ ssize_t writevn(int fd, struct iovec *v, int count){ } } +static inline ssize_t recvnnb(int fd, char *buf, size_t size) { + size_t left = size; + int r; + while(left>0) { + if ((r=recv(fd ,buf,left,MSG_DONTWAIT)) == -1) { + if (errno == EINTR) continue; + if (left == size) return -1; + break; + } + + if (r == 0) return 0; // EOF + left -= r; + buf += r; + } + return (size-left); +} + #endif diff --git a/ff/farm.hpp b/ff/farm.hpp index e73e5893..9dcdf4bc 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -270,7 +270,7 @@ class ff_farm: public ff_node { // accelerator if (has_input_channel) { - if (create_input_buffer(in_buffer_entries, fixedsize)<0) { + if (create_input_buffer(in_buffer_entries, fixedsizeIN)<0) { error("FARM, creating input buffer\n"); return -1; } @@ -315,7 +315,7 @@ class ff_farm: public ff_node { if (a2a_first->create_input_buffer((int) (ondemand ? ondemand: in_buffer_entries), - (ondemand ? true: fixedsize))<0) return -1; + (ondemand ? true: fixedsizeIN))<0) return -1; const svector& W1 = a2a_first->getFirstSet(); for(size_t i=0;icreate_input_buffer((int) (ondemand ? ondemand: in_buffer_entries), - (ondemand ? true: fixedsize))<0) return -1; + (ondemand ? true: fixedsizeIN))<0) return -1; lb->register_worker(workers[i]); } @@ -349,7 +349,7 @@ class ff_farm: public ff_node { // NOTE: the following call might fail because the buffers were already created for example by // the pipeline that contains this stage - a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize)); + a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT)); for(size_t i=0;i w(1); @@ -371,7 +371,7 @@ class ff_farm: public ff_node { } else { // NOTE: the following call might fail because the buffers were already created for example by // the pipeline that contains this stage - if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize))<0) { + if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) { if (lb->masterworker()) return -1; // something went wrong } if (lb->masterworker()) { @@ -427,21 +427,21 @@ class ff_farm: public ff_node { static int idx=0; for(size_t j=0;jset_output(t); gt->register_worker(t); } } else { // single node multi-output - ff_node* t = new ff_buffernode(out_buffer_entries,fixedsize, i); + ff_node* t = new ff_buffernode(out_buffer_entries,fixedsizeOUT, i); internalSupportNodes.push_back(t); workers[i]->set_output(t); if (!lb->masterworker()) workers[i]->set_output_buffer(t->get_out_buffer()); gt->register_worker(t); } } else { // standard worker or composition where the second stage is not multi-output - if (workers[i]->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsize))<0) + if (workers[i]->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) return -1; assert(!lb->masterworker()); gt->register_worker(workers[i]); @@ -729,7 +729,7 @@ class ff_farm: public ff_node { * @param input_ch \p true for enabling the input stream */ ff_farm(const std::vector& W, ff_node *const Emitter=NULL, ff_node *const Collector=NULL, bool input_ch=false): - has_input_channel(input_ch),collector_removed(false),ordered(false),fixedsize(FF_FIXED_SIZE), + has_input_channel(input_ch),collector_removed(false),ordered(false),fixedsizeIN(FF_FIXED_SIZE),fixedsizeOUT(FF_FIXED_SIZE), myownlb(true),myowngt(true),worker_cleanup(false),emitter_cleanup(false), collector_cleanup(false),ondemand(0), in_buffer_entries(DEFAULT_BUFFER_CAPACITY), @@ -770,7 +770,7 @@ class ff_farm: public ff_node { * \param out_buffer_entries = output queue length * \param max_num_workers = highest number of farm's worker * \param worker_cleanup = true deallocate worker object at exit - * \param fixedsize = true uses only fixed size queue + * \param fixedsize = true uses only fixed size queue (both between Emitter and Workers and between Workers and Collector) */ explicit ff_farm(bool input_ch=false, int in_buffer_entries=DEFAULT_BUFFER_CAPACITY, @@ -778,7 +778,7 @@ class ff_farm: public ff_node { bool worker_cleanup=false, // NOTE: by default no cleanup at exit is done ! size_t max_num_workers=DEF_MAX_NUM_WORKERS, bool fixedsize=FF_FIXED_SIZE): - has_input_channel(input_ch),collector_removed(false), ordered(false), fixedsize(fixedsize), + has_input_channel(input_ch),collector_removed(false), ordered(false), fixedsizeIN(FF_FIXED_SIZE),fixedsizeOUT(FF_FIXED_SIZE), myownlb(true),myowngt(true),worker_cleanup(worker_cleanup),emitter_cleanup(false), collector_cleanup(false), ondemand(0), in_buffer_entries(in_buffer_entries), @@ -808,7 +808,8 @@ class ff_farm: public ff_node { collector_cleanup = f.collector_cleanup; max_nworkers = f.max_nworkers; internalSupportNodes= f.internalSupportNodes; - fixedsize = f.fixedsize; + fixedsizeIN = f.fixedsizeIN; + fixedsizeOUT = f.fixedsizeOUT; myownlb = f.myownlb; myowngt = f.myowngt; workers = f.workers; @@ -849,7 +850,8 @@ class ff_farm: public ff_node { emitter_cleanup = f.emitter_cleanup; collector_cleanup = f.collector_cleanup; max_nworkers = f.max_nworkers; - fixedsize = f.fixedsize; + fixedsizeIN = f.fixedsizeIN; + fixedsizeOUT = f.fixedsizeOUT; emitter = f.emitter; collector = f.collector; lb = f.lb; gt = f.gt; @@ -1695,9 +1697,15 @@ class ff_farm: public ff_node { * run_and_wait_end/run_then_freeze/run/....) they have no effect. * */ - void setFixedSize(bool fs) { fixedsize = fs; } - void setInputQueueLength(int sz) { in_buffer_entries = sz; } - void setOutputQueueLength(int sz) { out_buffer_entries = sz;} + void setFixedSize(bool fs) { fixedsizeIN = fixedsizeOUT = fs; } + void setInputQueueLength(int sz, bool fixedsize) { + in_buffer_entries = sz; + fixedsizeIN = fixedsize; + } + void setOutputQueueLength(int sz, bool fixedsize) { + out_buffer_entries = sz; + fixedsizeOUT = fixedsize; + } int numThreads() const { return cardinality(); } @@ -2034,7 +2042,7 @@ class ff_farm: public ff_node { bool has_input_channel; // for the accelerator mode bool collector_removed; bool ordered; - bool fixedsize; + bool fixedsizeIN, fixedsizeOUT; bool myownlb,myowngt; bool worker_cleanup, emitter_cleanup,collector_cleanup; From ce149ac8dd9d64578efa0bdb3f1394e70286ea0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Fri, 18 Jun 2021 15:52:31 +0200 Subject: [PATCH 059/202] Added distributed components for ondemand scheduling policy --- ff/distributed/ff_dreceiverOD.hpp | 243 +++++++++++++++++++++++ ff/distributed/ff_dsenderOD.hpp | 312 ++++++++++++++++++++++++++++++ 2 files changed, 555 insertions(+) create mode 100644 ff/distributed/ff_dreceiverOD.hpp create mode 100644 ff/distributed/ff_dsenderOD.hpp diff --git a/ff/distributed/ff_dreceiverOD.hpp b/ff/distributed/ff_dreceiverOD.hpp new file mode 100644 index 00000000..bb8b202d --- /dev/null +++ b/ff/distributed/ff_dreceiverOD.hpp @@ -0,0 +1,243 @@ +#ifndef FF_DRECEIVEROD_H +#define FF_DRECEIVEROD_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ff; + +class ff_dreceiverOD: public ff_monode_t { +private: + + int sendRoutingTable(int sck){ + dataBuffer buff; std::ostream oss(&buff); + cereal::PortableBinaryOutputArchive oarchive(oss); + std::vector reachableDestinations; + + for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); + + oarchive << reachableDestinations; + + size_t sz = htobe64(buff.getLen()); + struct iovec iov[1]; + iov[0].iov_base = &sz; + iov[0].iov_len = sizeof(sz); + + if (writevn(sck, iov, 1) < 0 || writen(sck, buff.getPtr(), buff.getLen()) < 0){ + error("Error writing on socket the routing Table\n"); + return -1; + } + + return 0; + } + + int handleRequest(int sck){ + int sender; + int chid; + size_t sz; + struct iovec iov[3]; + iov[0].iov_base = &sender; + iov[0].iov_len = sizeof(sender); + iov[1].iov_base = &chid; + iov[1].iov_len = sizeof(chid); + iov[2].iov_base = &sz; + iov[2].iov_len = sizeof(sz); + + switch (readvn(sck, iov, 3)) { + case -1: error("Error reading from socket\n"); // fatal error + case 0: return -1; // connection close + } + + // convert values to host byte order + sender = ntohl(sender); + chid = ntohl(chid); + sz = be64toh(sz); + + if (sz > 0){ + char* buff = new char [sz]; + assert(buff); + if(readn(sck, buff, sz) < 0){ + error("Error reading from socket\n"); + delete [] buff; + return -1; + } + message_t* out = new message_t(buff, sz, true); + assert(out); + out->sender = sender; + out->chid = chid; + + if (chid != -1) + ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! + else + ff_send_out(out); + + // send back the ack only if the onDemand is true! + if (writen(sck, reinterpret_cast(&ACK),sizeof(ack_t)) < 0){ + error("Error sending back ACK to the sender\n"); + return -1; + } + + return 0; + } + + neos++; // increment the eos received + return -1; + } + +public: + ff_dreceiverOD(const int dGroup_id, ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, bool onDemand = false, int coreid=-1) + : onDemand(onDemand), input_channels(input_channels), acceptAddr(acceptAddr), routingTable(routingTable), + distributedGroupId(dGroup_id), coreid(coreid) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + #ifdef LOCAL + if ((listen_sck=socket(AF_LOCAL, SOCK_STREAM, 0)) < 0){ + error("Error creating the socket\n"); + return -1; + } + + struct sockaddr_un serv_addr; + memset(&serv_addr, '0', sizeof(serv_addr)); + serv_addr.sun_family = AF_LOCAL; + strncpy(serv_addr.sun_path, acceptAddr.address.c_str(), acceptAddr.address.size()+1); + #endif + + #ifdef REMOTE + if ((listen_sck=socket(AF_INET, SOCK_STREAM, 0)) < 0){ + error("Error creating the socket\n"); + return -1; + } + + int enable = 1; + // enable the reuse of the address + if (setsockopt(listen_sck, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + error("setsockopt(SO_REUSEADDR) failed\n"); + + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; // still listening from any interface + serv_addr.sin_port = htons( acceptAddr.port ); + + #endif + + if (bind(listen_sck, (struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0){ + error("Error binding\n"); + return -1; + } + + if (listen(listen_sck, MAXBACKLOG) < 0){ + error("Error listening\n"); + return -1; + } + + /*for (const auto& e : routingTable) + std::cout << "Entry: " << e.first << " -> " << e.second << std::endl; + */ + + return 0; + } + void svc_end() { + close(this->listen_sck); + + #ifdef LOCAL + unlink(this->acceptAddr.address.c_str()); + #endif + } + /* + Here i should not care of input type nor input data since they come from a socket listener. + Everything will be handled inside a while true in the body of this node where data is pulled from network + */ + message_t *svc(message_t* task) { + /* here i should receive the task via socket */ + + fd_set set, tmpset; + // intialize both sets (master, temp) + FD_ZERO(&set); + FD_ZERO(&tmpset); + + // add the listen socket to the master set + FD_SET(this->listen_sck, &set); + + // hold the greater descriptor + int fdmax = this->listen_sck; + + while(neos < input_channels){ + // copy the master set to the temporary + tmpset = set; + + switch(select(fdmax+1, &tmpset, NULL, NULL, NULL)){ + case -1: error("Error on selecting socket\n"); return EOS; + case 0: continue; + } + + // iterate over the file descriptor to see which one is active + int fixed_last = this->last_receive_fd + 1; + for(int i=0; i <= fdmax; i++){ + int actualFD = (fixed_last + i) % (fdmax +1); + if (FD_ISSET(actualFD, &tmpset)){ + if (actualFD == this->listen_sck) { + int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); + if (connfd == -1){ + error("Error accepting client\n"); + } else { + FD_SET(connfd, &set); + if(connfd > fdmax) fdmax = connfd; + + this->sendRoutingTable(connfd); // here i should check the result of the call! and handle possible errors! + } + continue; + } + + // it is not a new connection, call receive and handle possible errors + if (this->handleRequest(actualFD) < 0){ + close(actualFD); + FD_CLR(actualFD, &set); + + // update the maximum file descriptor + if (actualFD == fdmax) + for(int ii=(fdmax-1);ii>=0;--ii) + if (FD_ISSET(ii, &set)){ + fdmax = i; + break; + } + + } + // save the last socket i + this->last_receive_fd = actualFD; + } + } + } + + /* In theory i should never return because of the while true. In our first example this is necessary */ + return this->EOS; + } + +private: + const bool onDemand; + size_t neos = 0; + size_t input_channels; + int listen_sck; + ff_endpoint acceptAddr; + std::map routingTable; + int distributedGroupId; + int coreid; + int last_receive_fd = 0; + ack_t ACK; +}; + +#endif diff --git a/ff/distributed/ff_dsenderOD.hpp b/ff/distributed/ff_dsenderOD.hpp new file mode 100644 index 00000000..9e023885 --- /dev/null +++ b/ff/distributed/ff_dsenderOD.hpp @@ -0,0 +1,312 @@ +#ifndef FF_DSENDEROD_H +#define FF_DSENDEROD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +using namespace ff; + +class ff_dsenderOD: public ff_minode_t { +private: + size_t neos=0; + int last_rr_socket = 0; //next destiation to send for round robin policy + const int queueDim; + std::vector dest_endpoints; + std::map dest2Socket; + std::map sockCounters; + std::map sockets; + int coreid; + fd_set set, tmpset; + int fdmax = -1; + + int receiveReachableDestinations(int sck){ + size_t sz; + + //while (readvn(sck, iov, 1) != sizeof(sz)) // wait untill a size is received! + recv(sck, &sz, sizeof(sz), MSG_WAITALL); + + sz = be64toh(sz); + + std::cout << "Receiving routing table (" << sz << " bytes)" << std::endl; + char* buff = new char [sz]; + assert(buff); + + if(readn(sck, buff, sz) < 0){ + error("Error reading from socket\n"); + delete [] buff; + return -1; + } + + dataBuffer dbuff(buff, sz, true); + std::istream iss(&dbuff); + cereal::PortableBinaryInputArchive iarchive(iss); + std::vector destinationsList; + + iarchive >> destinationsList; + + for (int d : destinationsList) dest2Socket[d] = sck; + + return 0; + } + + int create_connect(const ff_endpoint& destination){ + int socketFD; + + #ifdef LOCAL + socketFD = socket(AF_LOCAL, SOCK_STREAM, 0); + if (socketFD < 0){ + error("\nError creating socket \n"); + return socketFD; + } + struct sockaddr_un serv_addr; + memset(&serv_addr, '0', sizeof(serv_addr)); + serv_addr.sun_family = AF_LOCAL; + + strncpy(serv_addr.sun_path, destination.address.c_str(), destination.address.size()+1); + + if (connect(socketFD, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){ + close(socketFD); + return -1; + } + #endif + + #ifdef REMOTE + struct addrinfo hints; + struct addrinfo *result, *rp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ + hints.ai_flags = 0; + hints.ai_protocol = IPPROTO_TCP; /* Allow only TCP */ + + // resolve the address + if (getaddrinfo(destination.address.c_str() , std::to_string(destination.port).c_str() , &hints, &result) != 0) + return -1; + + // try to connect to a possible one of the resolution results + for (rp = result; rp != NULL; rp = rp->ai_next) { + socketFD = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (socketFD == -1) + continue; + + if (connect(socketFD, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + + close(socketFD); + } + + if (rp == NULL) /* No address succeeded */ + return -1; + #endif + + // receive the reachable destination from this sockets + receiveReachableDestinations(socketFD); + + + return socketFD; + } + + int tryConnect(const ff_endpoint &destination){ + int fd, retries = 0; + + while((fd = this->create_connect(destination)) < 0 && ++retries < MAX_RETRIES) + if (retries < AGGRESSIVE_TRESHOLD) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + else + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + //std::this_thread::sleep_for(std::chrono::milliseconds((long)std::pow(2, retries - AGGRESSIVE_TRESHOLD))); + + return fd; + } + + int sendToSck(int sck, message_t* task){ + //std::cout << "received something from " << task->sender << " directed to " << task->chid << std::endl; + task->sender = htonl(task->sender); + task->chid = htonl(task->chid); + + size_t sz = htobe64(task->data.getLen()); + struct iovec iov[4]; + iov[0].iov_base = &task->sender; + iov[0].iov_len = sizeof(task->sender); + iov[1].iov_base = &task->chid; + iov[1].iov_len = sizeof(task->chid); + iov[2].iov_base = &sz; + iov[2].iov_len = sizeof(sz); + iov[3].iov_base = task->data.getPtr(); + iov[3].iov_len = task->data.getLen(); + + if (writevn(sck, iov, 4) < 0){ + error("Error writing on socket\n"); + return -1; + } + + return 0; + } + + +public: + ff_dsenderOD(ff_endpoint dest_endpoint, int queueDim = 1, int coreid=-1) + : queueDim(queueDim), coreid(coreid) { + this->dest_endpoints.push_back(std::move(dest_endpoint)); + } + + ff_dsenderOD( std::vector dest_endpoints_, int queueDim = 1, int coreid=-1) + : queueDim(queueDim), dest_endpoints(std::move(dest_endpoints_)),coreid(coreid) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + FD_ZERO(&set); + FD_ZERO(&tmpset); + + for(size_t i=0; i < this->dest_endpoints.size(); i++){ + if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; + + // execute the following block only if the scheduling is onDemand + + sockCounters[sockets[i]] = queueDim; + FD_SET(sockets[i], &set); + if (sockets[i] > fdmax) + fdmax = sockets[i]; + + } + + return 0; + } + + int waitAckFrom(int sck){ + while (sockCounters[sck] == 0){ + for (int i = 0; i < this->sockets.size(); ++i){ + int r; ack_t a; + if ((r = recvnnb(sockets[i], reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ + if (errno == EWOULDBLOCK){ + assert(r == -1); + continue; + } + perror("readn ack"); + return -1; + } else + //printf("received ACK from conn %d\n", i); + sockCounters[sockets[i]]++; + + } + + if (sockCounters[sck] == 0){ + tmpset = set; + if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ + perror("select"); + return -1; + } + } + } + return 1; + } + + bool isOneReady(){ + for(const auto& pair : sockCounters) + if (pair.second > 0) return true; + return false; + } + + int getNextReady(){ + for(int i = 0; i < this->sockets.size(); i++){ + int actualSocket = (last_rr_socket + 1 + i) % this->sockets.size(); + int sck = sockets[actualSocket]; + if (sockCounters[sck] > 0) { + last_rr_socket = actualSocket; + return sck; + } + } + + // devo ricevere almeno un ack da qualcuno + while(!isOneReady()){ + + // se non ho ricevuto nessun ack mi sospendo finche non ricevo qualcosa + tmpset = set; + if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ + perror("select"); + return -1; + } + + for (int i = 0; i < this->sockets.size(); ++i){ + int r; ack_t a; + int sck = sockets[i]; + if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ + if (errno == EWOULDBLOCK){ + assert(r == -1); + continue; + } + perror("readn ack"); + return -1; + } else { + //printf("received ACK from conn %d\n", i); + + sockCounters[sck]++; + last_rr_socket = i; + return sck; + } + } + } + } + + + void svc_end() { + // close the socket not matter if local or remote + for(size_t i=0; i < this->sockets.size(); i++) + close(sockets[i]); + } + message_t *svc(message_t* task) { + int sck; + if (task->chid != -1){ + sck = dest2Socket[task->chid]; + if (sockCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand + error("Error waiting Ack from....\n"); + delete task; return this->GO_ON; + } + } else + sck = getNextReady(); // blocking call if scheduling is ondemand + + sendToSck(sck, task); + + // update the counters + sockCounters[sck]--; + + delete task; + return this->GO_ON; + } + + void eosnotify(ssize_t) { + if (++neos >= this->get_num_inchannels()){ + message_t * E_O_S = new message_t; + E_O_S->chid = 0; + E_O_S->sender = 0; + for(const auto& pair : sockets) + sendToSck(pair.second, E_O_S); + + delete E_O_S; + } + } + + +}; + +#endif From 6b1d9f7bb2b8d4b1a15dee53345d6327ee646a9c Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 21 Jun 2021 10:33:10 +0200 Subject: [PATCH 060/202] minor modification for the ondemand --- ff/distributed/ff_dgroup.hpp | 10 +- ff/distributed/ff_dreceiverOD.hpp | 9 +- ff/distributed/ff_dsenderOD.hpp | 115 +++++++++--------- ff/distributed/ff_network.hpp | 2 + tests/distributed/test_a2aOnDemand.cpp | 149 ++++++++++++++++++++++++ tests/distributed/test_a2aOnDemand.json | 18 +++ 6 files changed, 243 insertions(+), 60 deletions(-) mode change 100644 => 100755 ff/distributed/ff_dgroup.hpp create mode 100644 tests/distributed/test_a2aOnDemand.cpp create mode 100644 tests/distributed/test_a2aOnDemand.json diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp old mode 100644 new mode 100755 index c3936c84..f34bdfe6 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -241,7 +241,7 @@ class dGroup : public ff_farm { for (const auto& pair : this->in_) in_C.push_back(pair.first); for (const auto& pair : this->out_) out_C.push_back(pair.first); - int onDemandQueueLength = 0; + int onDemandQueueLength = 0; bool onDemandReceiver = false; bool onDemandSender = false; if (this->parentStructure->isPipe()) processBB(this->parentStructure, in_C, out_C); @@ -264,8 +264,8 @@ class dGroup : public ff_farm { // if the ondemand scheduling is set in the a2a, i need to adjust the queues of this farm in order to implement the ondemand policy if (a2a->ondemand_buffer() > 0){ onDemandQueueLength = a2a->ondemand_buffer(); - if (first) this->setOutputQueueLength(1, true); // always set to 1 the length of the queue between worker and collector (SOURCE side) - if (second) this->set_scheduling_ondemand(a2a->ondemand_buffer()); // set the right length of the queue between emitter and worker (SINK side) + if (first) {this->setOutputQueueLength(1, true); onDemandSender = true;} // always set to 1 the length of the queue between worker and collector (SOURCE side) + if (second) {this->set_scheduling_ondemand(a2a->ondemand_buffer()); onDemandReceiver = true;} // set the right length of the queue between emitter and worker (SINK side) } } @@ -284,12 +284,12 @@ class dGroup : public ff_farm { // create receiver if (!isSource()){ //std::cout << "Creating the receiver!" << std::endl; - this->add_emitter(new ff_dreceiverOD(0 , this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB), onDemandQueueLength > 0)); // set right parameters HERE!! + this->add_emitter(new ff_dreceiverOD(0 , this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB), onDemandReceiver)); // set right parameters HERE!! } // create sender if (!isSink()){ //std::cout << "Creating the sender!" << std::endl; - if (onDemandQueueLength > 0) + if (onDemandSender) this->add_collector(new ff_dsenderOD(this->destinations, onDemandQueueLength), true); else this->add_collector(new ff_dsender(this->destinations), true); diff --git a/ff/distributed/ff_dreceiverOD.hpp b/ff/distributed/ff_dreceiverOD.hpp index bb8b202d..5ca0c8d4 100644 --- a/ff/distributed/ff_dreceiverOD.hpp +++ b/ff/distributed/ff_dreceiverOD.hpp @@ -83,11 +83,14 @@ class ff_dreceiverOD: public ff_monode_t { else ff_send_out(out); - // send back the ack only if the onDemand is true! + if (onDemand) { if (writen(sck, reinterpret_cast(&ACK),sizeof(ack_t)) < 0){ - error("Error sending back ACK to the sender\n"); - return -1; + if (errno != ECONNRESET || errno != EPIPE) { + error("Error sending back ACK to the sender (errno=%d)\n",errno); + return -1; + } } + } return 0; } diff --git a/ff/distributed/ff_dsenderOD.hpp b/ff/distributed/ff_dsenderOD.hpp index 9e023885..9a5c6985 100644 --- a/ff/distributed/ff_dsenderOD.hpp +++ b/ff/distributed/ff_dsenderOD.hpp @@ -30,7 +30,7 @@ class ff_dsenderOD: public ff_minode_t { std::vector dest_endpoints; std::map dest2Socket; std::map sockCounters; - std::map sockets; + std::vector sockets; int coreid; fd_set set, tmpset; int fdmax = -1; @@ -38,17 +38,26 @@ class ff_dsenderOD: public ff_minode_t { int receiveReachableDestinations(int sck){ size_t sz; - //while (readvn(sck, iov, 1) != sizeof(sz)) // wait untill a size is received! - recv(sck, &sz, sizeof(sz), MSG_WAITALL); - + // wait until the size is received! + //recv(sck, &sz, sizeof(sz), MSG_WAITALL); + long r; + if ((r=readn(sck, (char*)&sz, sizeof(sz)))!=sizeof(sz)) { + if (r==0) + error("Error unexpected connection closed by receiver\n"); + else + error("Error reading size (errno=%d)"); + return -1; + } + sz = be64toh(sz); std::cout << "Receiving routing table (" << sz << " bytes)" << std::endl; char* buff = new char [sz]; - assert(buff); + assert(buff); - if(readn(sck, buff, sz) < 0){ - error("Error reading from socket\n"); + if((r=readn(sck, buff, sz)) != (long)sz){ + if (r==0) error("Error unexpected connection closed by receiver\n"); + else error("Error reading from socket\n"); delete [] buff; return -1; } @@ -178,6 +187,7 @@ class ff_dsenderOD: public ff_minode_t { FD_ZERO(&set); FD_ZERO(&tmpset); + sockets.resize(this->dest_endpoints.size()); for(size_t i=0; i < this->dest_endpoints.size(); i++){ if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; @@ -195,21 +205,21 @@ class ff_dsenderOD: public ff_minode_t { int waitAckFrom(int sck){ while (sockCounters[sck] == 0){ - for (int i = 0; i < this->sockets.size(); ++i){ + for (size_t i = 0; i < this->sockets.size(); ++i){ int r; ack_t a; if ((r = recvnnb(sockets[i], reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ if (errno == EWOULDBLOCK){ assert(r == -1); continue; } - perror("readn ack"); + perror("recvnnb ack"); return -1; } else //printf("received ACK from conn %d\n", i); sockCounters[sockets[i]]++; } - + if (sockCounters[sck] == 0){ tmpset = set; if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ @@ -221,14 +231,35 @@ class ff_dsenderOD: public ff_minode_t { return 1; } - bool isOneReady(){ - for(const auto& pair : sockCounters) - if (pair.second > 0) return true; - return false; - } + int waitAckFromAny() { + tmpset = set; + if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ + perror("select"); + return -1; + } + // try to receive from all connections in a non blocking way + for (size_t i = 0; i < this->sockets.size(); ++i){ + int r; ack_t a; + int sck = sockets[i]; + if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ + if (errno == EWOULDBLOCK){ + assert(r == -1); + continue; + } + perror("recvnnb ack"); + return -1; + } else { + sockCounters[sck]++; + last_rr_socket = i; + return sck; + } + } + assert(1==0); + return -1; + } int getNextReady(){ - for(int i = 0; i < this->sockets.size(); i++){ + for(size_t i = 0; i < this->sockets.size(); i++){ int actualSocket = (last_rr_socket + 1 + i) % this->sockets.size(); int sck = sockets[actualSocket]; if (sockCounters[sck] > 0) { @@ -236,43 +267,24 @@ class ff_dsenderOD: public ff_minode_t { return sck; } } - - // devo ricevere almeno un ack da qualcuno - while(!isOneReady()){ - - // se non ho ricevuto nessun ack mi sospendo finche non ricevo qualcosa - tmpset = set; - if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ - perror("select"); - return -1; - } - - for (int i = 0; i < this->sockets.size(); ++i){ - int r; ack_t a; - int sck = sockets[i]; - if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ - if (errno == EWOULDBLOCK){ - assert(r == -1); - continue; - } - perror("readn ack"); - return -1; - } else { - //printf("received ACK from conn %d\n", i); - - sockCounters[sck]++; - last_rr_socket = i; - return sck; - } - } - } + return waitAckFromAny(); } void svc_end() { - // close the socket not matter if local or remote - for(size_t i=0; i < this->sockets.size(); i++) - close(sockets[i]); + long totalack = sockets.size()*queueDim; + long currack = 0; + for(const auto& pair : sockCounters) + currack += pair.second; + while(curracksockets.size(); i++) { + close(sockets[i]); + } } message_t *svc(message_t* task) { int sck; @@ -299,14 +311,13 @@ class ff_dsenderOD: public ff_minode_t { message_t * E_O_S = new message_t; E_O_S->chid = 0; E_O_S->sender = 0; - for(const auto& pair : sockets) - sendToSck(pair.second, E_O_S); + for(const auto& fd : sockets) + sendToSck(fd, E_O_S); delete E_O_S; } } - }; #endif diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 17f4f1a6..22d7d573 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -110,6 +110,7 @@ ssize_t readvn(int fd, struct iovec *v, int count){ v[cur].iov_base = (char *)v[cur].iov_base + rread; v[cur].iov_len -= rread; } + return -1; } ssize_t writen(int fd, const char *ptr, size_t n) { @@ -138,6 +139,7 @@ ssize_t writevn(int fd, struct iovec *v, int count){ v[cur].iov_base = (char *)v[cur].iov_base + written; v[cur].iov_len -= written; } + return -1; } static inline ssize_t recvnnb(int fd, char *buf, size_t size) { diff --git a/tests/distributed/test_a2aOnDemand.cpp b/tests/distributed/test_a2aOnDemand.cpp new file mode 100644 index 00000000..848293c6 --- /dev/null +++ b/tests/distributed/test_a2aOnDemand.cpp @@ -0,0 +1,149 @@ +/* + * FastFlow concurrent network: + * + * |--> MiNode + * |-> MoNode-->| + * MoNode -->| |--> MiNode + * |-> MoNode-->| + * |--> MiNode + * + * /<- pipe ->//<-------- a2a -------->/ + * /<----------- pipeMain ------------->/ + */ + + +#include +#include +#include +#include +#include + +using namespace ff; + +// ------------------------------------------------------ +std::mutex mtx; // used only for pretty printing +static inline float active_delay(int msecs) { + // read current time + float x = 1.25f; + auto start = std::chrono::high_resolution_clock::now(); + auto end = false; + while(!end) { + auto elapsed = std::chrono::high_resolution_clock::now() - start; + auto msec = std::chrono::duration_cast(elapsed).count(); + x *= sin(x) / atan(x) * tanh(x) * sqrt(x); + if(msec>=msecs) + end = true; + } + return x; +} + +// ----------------------------------------------------- +struct DataType { + long x; + long y; + + template + void serialize(Archive & archive) { + archive(x,y); + } +}; + +struct MoNode : ff::ff_monode_t{ + MoNode(int itemsToGenerate):items(itemsToGenerate) {} + + DataType* svc(DataType* in){ + if (items) { + for(int i=0; i< items; i++){ + auto d = new DataType; + d->x=i, d->y=i+1; + printf("generator %ld,%ld\n", d->x, d->y); + ff_send_out(d); + } + return this->EOS; + } + printf("MoNode %ld,%ld\n", in->x, in->y); + return in; + } + void svc_end() { + const std::lock_guard lock(mtx); + std::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << std::endl; + } + + long items; +}; + +struct MiNode : ff::ff_minode_t{ + int processedItems = 0; + int execTime; + bool checkdata; + MiNode(int execTime): execTime(execTime) {} + + DataType* svc(DataType* in){ + printf("MiNode receved data\n"); + active_delay(this->execTime); + ++processedItems; + delete in; + return this->GO_ON; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[MiNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +int main(int argc, char*argv[]){ + + DFF_Init(argc, argv); + + if (argc != 4){ + std::cout << "Usage: " << argv[0] << " #items #nw_sx #nw_dx" << std::endl; + return -1; + } + int items = atoi(argv[1]); + int numWorkerSx = atoi(argv[2]); + int numWorkerDx = atoi(argv[3]); + + ff_pipeline mainPipe; + ff_pipeline pipe; + ff::ff_a2a a2a; + + MoNode generator(items); + pipe.add_stage(&generator); + + mainPipe.add_stage(&pipe); + mainPipe.add_stage(&a2a); + + std::vector sxWorkers; + std::vector dxWorkers; + + for(int i = 0; i < numWorkerSx; i++) + sxWorkers.push_back(new MoNode(0)); + + for(int i = 0; i < numWorkerDx; i++) + dxWorkers.push_back(new MiNode(i*100)); + + a2a.add_firstset(sxWorkers, 1); + a2a.add_secondset(dxWorkers); + + + auto g0 = pipe.createGroup("G0"); + auto g1 = a2a.createGroup("G1"); + auto g2 = a2a.createGroup("G2"); + + g0.out << &generator; + + for(int i = 0; i < numWorkerSx; i++) { + g1.in << sxWorkers[i]; + g1.out << sxWorkers[i]; + } + for(int i = 0; i < numWorkerDx; i++) { + g2.in << dxWorkers[i]; + } + + if (mainPipe.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_a2aOnDemand.json b/tests/distributed/test_a2aOnDemand.json new file mode 100644 index 00000000..240e4334 --- /dev/null +++ b/tests/distributed/test_a2aOnDemand.json @@ -0,0 +1,18 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8003", + "name" : "G0", + "OConn" : ["G1"] + }, + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} From 028cedb55225f7cfe6040a1d83f98443520a0e79 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 21 Jun 2021 10:39:05 +0200 Subject: [PATCH 061/202] removed x flag to the ff_dgroup.hpp --- ff/distributed/ff_dgroup.hpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 ff/distributed/ff_dgroup.hpp diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp old mode 100755 new mode 100644 From 46da5af3f6f0d2d76c535895142700ded342a078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 21 Jun 2021 15:30:22 +0200 Subject: [PATCH 062/202] Fixed Ondemand on in/out nodes; Added a new test for distributed on-demand A2A --- ff/distributed/ff_dgroup.hpp | 16 +++- tests/distributed/test_a2aOnDemand2.cpp | 93 ++++++++++++++++++++++++ tests/distributed/test_a2aOnDemand2.json | 13 ++++ 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 tests/distributed/test_a2aOnDemand2.cpp create mode 100644 tests/distributed/test_a2aOnDemand2.json diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f34bdfe6..c2b24f76 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -249,7 +249,7 @@ class dGroup : public ff_farm { if (this->parentStructure->isAll2All()){ ff_a2a * a2a = (ff_a2a*) this->parentStructure; - if (!processBB(a2a, in_C, out_C)){ // if the user has not wrapped the whole a2a, expan its sets + if (!processBB(a2a, in_C, out_C)){ // if the user has not wrapped the whole a2a, expand its sets bool first = false, second = false; for(ff_node* bb : a2a->getFirstSet()) @@ -260,13 +260,24 @@ class dGroup : public ff_farm { if (processBB(bb, in_C, out_C)) second = true; - if (first && second) throw FF_Exception("Nodes from first and second of an A2A cannot belong to the same group"); + // if the ondemand scheduling is set in the a2a, i need to adjust the queues of this farm in order to implement the ondemand policy if (a2a->ondemand_buffer() > 0){ + if (!first && !second){ + for (const auto& pair : this->inout_){ + if (std::find(a2a->getFirstSet().begin(), a2a->getFirstSet().end(), pair.first) != a2a->getFirstSet().end()) + first = true; + else if (std::find(a2a->getSecondSet().begin(), a2a->getSecondSet().end(), pair.first) != a2a->getSecondSet().end()) + second = true; + } + } + onDemandQueueLength = a2a->ondemand_buffer(); if (first) {this->setOutputQueueLength(1, true); onDemandSender = true;} // always set to 1 the length of the queue between worker and collector (SOURCE side) if (second) {this->set_scheduling_ondemand(a2a->ondemand_buffer()); onDemandReceiver = true;} // set the right length of the queue between emitter and worker (SINK side) } + + if (first && second) throw FF_Exception("Nodes from first and second of an A2A cannot belong to the same group"); } } @@ -279,7 +290,6 @@ class dGroup : public ff_farm { if (this->getNWorkers() == 0) return -1; - // create receiver if (!isSource()){ diff --git a/tests/distributed/test_a2aOnDemand2.cpp b/tests/distributed/test_a2aOnDemand2.cpp new file mode 100644 index 00000000..519e6e28 --- /dev/null +++ b/tests/distributed/test_a2aOnDemand2.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include + +std::mutex mtx; + +static inline float active_delay(int msecs) { + // read current time + float x = 1.25f; + auto start = std::chrono::high_resolution_clock::now(); + auto end = false; + while(!end) { + auto elapsed = std::chrono::high_resolution_clock::now() - start; + auto msec = std::chrono::duration_cast(elapsed).count(); + x *= sin(x) / atan(x) * tanh(x) * sqrt(x); + if(msec>=msecs) + end = true; + } + return x; +} + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Sink: ff::ff_minode_t{ + Sink(long sleep_):sleep_(sleep_) {} + myTask_t* svc(myTask_t* t){ + active_delay(sleep_); + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + const std::lock_guard lock(mtx); + std::cout << "Node("<< sleep_ <<") Received " << processed << " tasks\n"; + } + long sleep_; + long processed=0; +}; + +struct Source: ff::ff_monode_t{ + Source(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + + +int main(int argc, char*argv[]){ + DFF_Init(argc, argv); + + ff::ff_pipeline mainPipe; + ff::ff_a2a a2a; + + auto g2 = a2a.createGroup("G2"); + + Source s(10); + a2a.add_firstset({&s}, 1); + a2a.createGroup("G1").out << &s; + + std::vector sinks; + for(int i = 0; i < 3; i++){ + Sink * sink = new Sink((long)1000*(i+1)); + sinks.push_back(sink); + g2.in << sink; + } + + a2a.add_secondset(sinks); + + mainPipe.add_stage(&a2a); + mainPipe.run_and_wait_end(); + +} \ No newline at end of file diff --git a/tests/distributed/test_a2aOnDemand2.json b/tests/distributed/test_a2aOnDemand2.json new file mode 100644 index 00000000..fab97f4a --- /dev/null +++ b/tests/distributed/test_a2aOnDemand2.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} From d594934cf76f195243654cc894e0a4415b0de2eb Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 22 Jun 2021 10:46:31 +0200 Subject: [PATCH 063/202] minor modifications to the on-demand tests --- tests/distributed/test_a2aOnDemand.cpp | 3 -- tests/distributed/test_a2aOnDemand2.cpp | 47 +++++++++++++++++++------ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/tests/distributed/test_a2aOnDemand.cpp b/tests/distributed/test_a2aOnDemand.cpp index 848293c6..b5998813 100644 --- a/tests/distributed/test_a2aOnDemand.cpp +++ b/tests/distributed/test_a2aOnDemand.cpp @@ -56,12 +56,10 @@ struct MoNode : ff::ff_monode_t{ for(int i=0; i< items; i++){ auto d = new DataType; d->x=i, d->y=i+1; - printf("generator %ld,%ld\n", d->x, d->y); ff_send_out(d); } return this->EOS; } - printf("MoNode %ld,%ld\n", in->x, in->y); return in; } void svc_end() { @@ -79,7 +77,6 @@ struct MiNode : ff::ff_minode_t{ MiNode(int execTime): execTime(execTime) {} DataType* svc(DataType* in){ - printf("MiNode receved data\n"); active_delay(this->execTime); ++processedItems; delete in; diff --git a/tests/distributed/test_a2aOnDemand2.cpp b/tests/distributed/test_a2aOnDemand2.cpp index 519e6e28..8b7ba0a3 100644 --- a/tests/distributed/test_a2aOnDemand2.cpp +++ b/tests/distributed/test_a2aOnDemand2.cpp @@ -69,25 +69,50 @@ struct Source: ff::ff_monode_t{ int main(int argc, char*argv[]){ DFF_Init(argc, argv); + if (argc != 5){ + std::cout << "Usage: " << argv[0] << " #items #nw_sx #nw_dx #async" << std::endl; + return -1; + } + + + int items = atoi(argv[1]); + int numWorkerSx = atoi(argv[2]); + int numWorkerDx = atoi(argv[3]); + int asyncdegree = atoi(argv[4]); + + if (numWorkerSx <= 0 || + numWorkerDx <= 0 || + asyncdegree <= 0) { + error("Bad parameter values\n"); + return -1; + } + ff::ff_pipeline mainPipe; ff::ff_a2a a2a; + auto g1 = a2a.createGroup("G1"); auto g2 = a2a.createGroup("G2"); - Source s(10); - a2a.add_firstset({&s}, 1); - a2a.createGroup("G1").out << &s; + std::vector sx; + std::vector dx; - std::vector sinks; - for(int i = 0; i < 3; i++){ - Sink * sink = new Sink((long)1000*(i+1)); - sinks.push_back(sink); - g2.in << sink; + for(int i = 0; i < numWorkerSx; i++) { + sx.push_back(new Source(items)); + g1.out << sx[i]; } + a2a.add_firstset(sx, asyncdegree); - a2a.add_secondset(sinks); + for(int i = 0; i < numWorkerDx; i++){ + dx.push_back(new Sink((long)100*(i+1))); + g2.in << dx[i]; + } + a2a.add_secondset(dx); mainPipe.add_stage(&a2a); - mainPipe.run_and_wait_end(); + if (mainPipe.run_and_wait_end()<0) { + error("running pipe"); + return -1; + } -} \ No newline at end of file + return 0; +} From 207898694219fa2a6e0cc0f1b02c63c63ce6d8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 28 Jun 2021 14:00:07 +0200 Subject: [PATCH 064/202] First working version of sender and receiver based on MPI --- ff/distributed/ff_dgroup.hpp | 11 +- ff/distributed/ff_dreceiverMPI.hpp | 207 +++++++++++++++++++++++++++++ ff/distributed/ff_dsenderMPI.hpp | 123 +++++++++++++++++ ff/distributed/ff_network.hpp | 30 ++++- 4 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 ff/distributed/ff_dreceiverMPI.hpp create mode 100644 ff/distributed/ff_dsenderMPI.hpp diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index c2b24f76..b92ee939 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -260,10 +260,8 @@ class dGroup : public ff_farm { if (processBB(bb, in_C, out_C)) second = true; - - // if the ondemand scheduling is set in the a2a, i need to adjust the queues of this farm in order to implement the ondemand policy - if (a2a->ondemand_buffer() > 0){ - if (!first && !second){ + // check on input/output nodes, used for ondemand stuff and for checking collision between nodes + if (!first && !second){ for (const auto& pair : this->inout_){ if (std::find(a2a->getFirstSet().begin(), a2a->getFirstSet().end(), pair.first) != a2a->getFirstSet().end()) first = true; @@ -271,13 +269,16 @@ class dGroup : public ff_farm { second = true; } } + + // if the ondemand scheduling is set in the a2a, i need to adjust the queues of this farm in order to implement the ondemand policy + if (a2a->ondemand_buffer() > 0){ onDemandQueueLength = a2a->ondemand_buffer(); if (first) {this->setOutputQueueLength(1, true); onDemandSender = true;} // always set to 1 the length of the queue between worker and collector (SOURCE side) if (second) {this->set_scheduling_ondemand(a2a->ondemand_buffer()); onDemandReceiver = true;} // set the right length of the queue between emitter and worker (SINK side) } - if (first && second) throw FF_Exception("Nodes from first and second of an A2A cannot belong to the same group"); + if (first && second) throw FF_Exception("Nodes from first and second of an A2A cannot belong to the same group!!"); } } diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp new file mode 100644 index 00000000..eb4fde1c --- /dev/null +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -0,0 +1,207 @@ +#ifndef FF_DRECEIVER_MPI_H +#define FF_DRECEIVER_MPI_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ff; + +class ff_dreceiverMPI: public ff_monode_t { +private: + + int sendRoutingTable(int rank){ + dataBuffer buff; std::ostream oss(&buff); + cereal::PortableBinaryOutputArchive oarchive(oss); + std::vector reachableDestinations; + + for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); + + oarchive << reachableDestinations; + + /*size_t sz = htobe64(buff.getLen()); + struct iovec iov[1]; + iov[0].iov_base = &sz; + iov[0].iov_len = sizeof(sz); + + if (writevn(sck, iov, 1) < 0 || writen(sck, buff.getPtr(), buff.getLen()) < 0){ + error("Error writing on socket the routing Table\n"); + return -1; + }*/ + + if (MPI_Send(buff.getPtr(), buff.getLen(), MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) + return -1; + + return 0; + } + +public: + ff_dreceiverMPI(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : input_channels(input_channels), routingTable(routingTable), coreid(coreid) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + /*#ifdef LOCAL + if ((listen_sck=socket(AF_LOCAL, SOCK_STREAM, 0)) < 0){ + error("Error creating the socket\n"); + return -1; + } + + struct sockaddr_un serv_addr; + memset(&serv_addr, '0', sizeof(serv_addr)); + serv_addr.sun_family = AF_LOCAL; + strncpy(serv_addr.sun_path, acceptAddr.address.c_str(), acceptAddr.address.size()+1); + #endif + + #ifdef REMOTE + if ((listen_sck=socket(AF_INET, SOCK_STREAM, 0)) < 0){ + error("Error creating the socket\n"); + return -1; + } + + int enable = 1; + // enable the reuse of the address + if (setsockopt(listen_sck, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + error("setsockopt(SO_REUSEADDR) failed\n"); + + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; // still listening from any interface + serv_addr.sin_port = htons( acceptAddr.port ); + + #endif + + if (bind(listen_sck, (struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0){ + error("Error binding\n"); + return -1; + } + + if (listen(listen_sck, MAXBACKLOG) < 0){ + error("Error listening\n"); + return -1; + } + + /*for (const auto& e : routingTable) + std::cout << "Entry: " << e.first << " -> " << e.second << std::endl; + */ + + return 0; + } + + /* + Here i should not care of input type nor input data since they come from a socket listener. + Everything will be handled inside a while true in the body of this node where data is pulled from network + */ + message_t *svc(message_t* task) { + MPI_Status status; + char headerBuff[sizeof(size_t)+2*sizeof(int)]; + while(neos < input_channels){ + MPI_Recv(headerBuff, sizeof(size_t)+2*sizeof(int), MPI_BYTE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); + + if (status.MPI_TAG == DFF_ROUTING_TABLE_TAG){ + sendRoutingTable(status.MPI_SOURCE); + continue; + } + + size_t sz = *reinterpret_cast(headerBuff); + + if (sz == 0){ + neos++; + continue; + } + + char* buff = new char [sz]; + assert(buff); + + MPI_Recv(buff,sz,MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + message_t* out = new message_t(buff, sz, true); + assert(out); + out->sender = *reinterpret_cast(headerBuff+sizeof(size_t)); + out->chid = *reinterpret_cast(headerBuff+sizeof(size_t)+sizeof(int)); + + //std::cout << "received something from " << sender << " directed to " << chid << std::endl; + + ff_send_out_to(out, this->routingTable[out->chid]); // assume the routing table is consistent WARNING!!! + } + + + + + /* here i should receive the task via socket */ + + /*fd_set set, tmpset; + // intialize both sets (master, temp) + FD_ZERO(&set); + FD_ZERO(&tmpset); + + // add the listen socket to the master set + FD_SET(this->listen_sck, &set); + + // hold the greater descriptor + int fdmax = this->listen_sck; + + while(neos < input_channels){ + // copy the master set to the temporary + tmpset = set; + + switch(select(fdmax+1, &tmpset, NULL, NULL, NULL)){ + case -1: error("Error on selecting socket\n"); return EOS; + case 0: continue; + } + + // iterate over the file descriptor to see which one is active + for(int i=0; i <= fdmax; i++) + if (FD_ISSET(i, &tmpset)){ + if (i == this->listen_sck) { + int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); + if (connfd == -1){ + error("Error accepting client\n"); + } else { + FD_SET(connfd, &set); + if(connfd > fdmax) fdmax = connfd; + + this->sendRoutingTable(connfd); // here i should check the result of the call! and handle possible errors! + } + continue; + } + + // it is not a new connection, call receive and handle possible errors + if (this->handleRequest(i) < 0){ + close(i); + FD_CLR(i, &set); + + // update the maximum file descriptor + if (i == fdmax) + for(int i=(fdmax-1);i>=0;--i) + if (FD_ISSET(i, &set)){ + fdmax = i; + break; + } + + } + } + + } + + /* In theory i should never return because of the while true. In our first example this is necessary */ + return this->EOS; + } + +private: + size_t neos = 0; + size_t input_channels; + std::map routingTable; + int coreid; +}; + +#endif diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp new file mode 100644 index 00000000..796a1c21 --- /dev/null +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -0,0 +1,123 @@ +#ifndef FF_DSENDER_MPI_H +#define FF_DSENDER_MPI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +using namespace ff; + +class ff_dsenderMPI: public ff_minode_t { +private: + size_t neos=0; + int next_rr_destination = 0; //next destiation to send for round robin policy + std::map dest2Rank; + std::vector destRanks; + int coreid; + + int receiveReachableDestinations(int rank){ + int sz; + int cmd = DFF_REQUEST_ROUTING_TABLE; + char* buff = new char [1000]; + + MPI_Status status; + MPI_Sendrecv(&cmd, 1, MPI_INT, rank, DFF_ROUTING_TABLE_TAG, buff, 1000, MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD, &status); + + MPI_Get_count(&status,MPI_BYTE, &sz); + + std::cout << "Received routing table (" << sz << " bytes)" << std::endl; + + dataBuffer dbuff(buff, sz, true); + std::istream iss(&dbuff); + cereal::PortableBinaryInputArchive iarchive(iss); + std::vector destinationsList; + + iarchive >> destinationsList; + + for (int d : destinationsList) dest2Rank[d] = rank; + + return 0; + } + + + int sendToSck(int rank, message_t* task){ + //std::cout << "received something from " << task->sender << " directed to " << task->chid << std::endl; + size_t sz = task->data.getLen(); + + char headerBuff[sizeof(size_t)+2*sizeof(int)]; + memcpy(headerBuff, &sz, sizeof(size_t)); + memcpy(headerBuff+sizeof(size_t), &task->sender, sizeof(int)); + memcpy(headerBuff+sizeof(int)+sizeof(size_t), &task->chid, sizeof(int)); + + if (MPI_Send(headerBuff, sizeof(size_t)+2*sizeof(int), MPI_BYTE, rank, DFF_HEADER_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) + return -1; + if (sz > 0) + if (MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) + return -1; + + return 0; + } + + +public: + ff_dsenderMPI(int destRank, int coreid=-1) + : coreid(coreid) { + this->destRanks.push_back(std::move(destRank)); + } + + ff_dsenderMPI( std::vector destRanks_, int coreid=-1) + : destRanks(std::move(destRanks_)),coreid(coreid) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + for(const int& rank: this->destRanks) + receiveReachableDestinations(rank); + + return 0; + } + + + message_t *svc(message_t* task) { + /* here i should send the task via socket */ + if (task->chid == -1){ // roundrobin over the destinations + task->chid = next_rr_destination; + next_rr_destination = (next_rr_destination + 1) % dest2Rank.size(); + } + + sendToSck(dest2Rank[task->chid], task); + + delete task; + return this->GO_ON; + } + + void eosnotify(ssize_t) { + if (++neos >= this->get_num_inchannels()){ + message_t * E_O_S = new message_t; + E_O_S->chid = 0; + E_O_S->sender = 0; + for(const auto& rank : destRanks) + sendToSck(rank, E_O_S); + + delete E_O_S; + } + } + + +}; + +#endif diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 22d7d573..a0401121 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -12,7 +12,24 @@ #include #include -//#define LOCAL +#include +#include + +// define the type my_MPI_SIZE_T +#if SIZE_MAX == UCHAR_MAX + #define my_MPI_SIZE_T MPI_UNSIGNED_CHAR +#elif SIZE_MAX == USHRT_MAX + #define my_MPI_SIZE_T MPI_UNSIGNED_SHORT +#elif SIZE_MAX == UINT_MAX + #define my_MPI_SIZE_T MPI_UNSIGNED +#elif SIZE_MAX == ULONG_MAX + #define my_MPI_SIZE_T MPI_UNSIGNED_LONG +#elif SIZE_MAX == ULLONG_MAX + #define my_MPI_SIZE_T MPI_UNSIGNED_LONG_LONG +#else + #error "what is happening here?" +#endif + #define REMOTE #ifdef __APPLE__ @@ -159,4 +176,15 @@ static inline ssize_t recvnnb(int fd, char *buf, size_t size) { return (size-left); } + +/* + MPI DEFINES +*/ + +#define DFF_ROUTING_TABLE_TAG 2 +#define DFF_TASK_TAG 3 +#define DFF_HEADER_TAG 4 + +#define DFF_REQUEST_ROUTING_TABLE 10 + #endif From 19f858081a2da723d36d7c45410e453783cb8af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Fri, 16 Jul 2021 16:55:42 +0200 Subject: [PATCH 065/202] Refactored receiver and sender, normal vs ondemand, TCP vs MPI --- ff/distributed/ff_dgroup.hpp | 7 +- ff/distributed/ff_dreceiver.hpp | 116 +++++++++-- ff/distributed/ff_dreceiverMPI.hpp | 195 ++++++++--------- ff/distributed/ff_dreceiverOD.hpp | 246 ---------------------- ff/distributed/ff_dsender.hpp | 182 ++++++++++++++-- ff/distributed/ff_dsenderMPI.hpp | 138 +++++++++--- ff/distributed/ff_dsenderOD.hpp | 323 ----------------------------- ff/distributed/ff_network.hpp | 19 +- ff/distributed/loader/dff_run.cpp | 2 +- 9 files changed, 469 insertions(+), 759 deletions(-) delete mode 100644 ff/distributed/ff_dreceiverOD.hpp delete mode 100644 ff/distributed/ff_dsenderOD.hpp diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index b92ee939..a87b0d80 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include #include @@ -295,7 +293,10 @@ class dGroup : public ff_farm { // create receiver if (!isSource()){ //std::cout << "Creating the receiver!" << std::endl; - this->add_emitter(new ff_dreceiverOD(0 , this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB), onDemandReceiver)); // set right parameters HERE!! + if (onDemandReceiver) + this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! + else + this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); } // create sender if (!isSink()){ diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 1317e280..3911eca1 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -19,7 +19,7 @@ using namespace ff; class ff_dreceiver: public ff_monode_t { -private: +protected: int sendRoutingTable(int sck){ dataBuffer buff; std::ostream oss(&buff); @@ -43,8 +43,8 @@ class ff_dreceiver: public ff_monode_t { return 0; } - int handleRequest(int sck){ - int sender; + virtual int handleRequest(int sck){ + int sender; int chid; size_t sz; struct iovec iov[3]; @@ -89,9 +89,8 @@ class ff_dreceiver: public ff_monode_t { } public: - ff_dreceiver(const int dGroup_id, ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) - : input_channels(input_channels), acceptAddr(acceptAddr), routingTable(routingTable), - distributedGroupId(dGroup_id), coreid(coreid) {} + ff_dreceiver(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : input_channels(input_channels), acceptAddr(acceptAddr), routingTable(routingTable), coreid(coreid) {} int svc_init() { if (coreid!=-1) @@ -178,9 +177,11 @@ class ff_dreceiver: public ff_monode_t { } // iterate over the file descriptor to see which one is active - for(int i=0; i <= fdmax; i++) - if (FD_ISSET(i, &tmpset)){ - if (i == this->listen_sck) { + int fixed_last = (this->last_receive_fd + 1) % (fdmax +1); + for(int i=0; i <= fdmax; i++){ + int actualFD = (fixed_last + i) % (fdmax +1); + if (FD_ISSET(actualFD, &tmpset)){ + if (actualFD == this->listen_sck) { int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); if (connfd == -1){ error("Error accepting client\n"); @@ -194,35 +195,112 @@ class ff_dreceiver: public ff_monode_t { } // it is not a new connection, call receive and handle possible errors - if (this->handleRequest(i) < 0){ - close(i); - FD_CLR(i, &set); + // save the last socket i + this->last_receive_fd = actualFD; + + + if (this->handleRequest(actualFD) < 0){ + close(actualFD); + FD_CLR(actualFD, &set); // update the maximum file descriptor - if (i == fdmax) - for(int i=(fdmax-1);i>=0;--i) - if (FD_ISSET(i, &set)){ - fdmax = i; + if (actualFD == fdmax) + for(int ii=(fdmax-1);ii>=0;--ii) + if (FD_ISSET(ii, &set)){ + fdmax = ii; + this->last_receive_fd = -1; break; } } + } - + } } /* In theory i should never return because of the while true. In our first example this is necessary */ return this->EOS; } -private: +protected: size_t neos = 0; size_t input_channels; int listen_sck; ff_endpoint acceptAddr; std::map routingTable; - int distributedGroupId; + int last_receive_fd = -1; int coreid; }; +/* + ONDEMAND specification +*/ + + +class ff_dreceiverOD: public ff_dreceiver { +protected: + virtual int handleRequest(int sck) override { + int sender; + int chid; + size_t sz; + struct iovec iov[3]; + iov[0].iov_base = &sender; + iov[0].iov_len = sizeof(sender); + iov[1].iov_base = &chid; + iov[1].iov_len = sizeof(chid); + iov[2].iov_base = &sz; + iov[2].iov_len = sizeof(sz); + + switch (readvn(sck, iov, 3)) { + case -1: error("Error reading from socket\n"); // fatal error + case 0: return -1; // connection close + } + + // convert values to host byte order + sender = ntohl(sender); + chid = ntohl(chid); + sz = be64toh(sz); + + if (sz > 0){ + char* buff = new char [sz]; + assert(buff); + if(readn(sck, buff, sz) < 0){ + error("Error reading from socket\n"); + delete [] buff; + return -1; + } + message_t* out = new message_t(buff, sz, true); + assert(out); + out->sender = sender; + out->chid = chid; + + if (chid != -1) + ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! + else + ff_send_out(out); + + + if (writen(sck, reinterpret_cast(&ACK),sizeof(ack_t)) < 0){ + if (errno != ECONNRESET || errno != EPIPE) { + error("Error sending back ACK to the sender (errno=%d)\n",errno); + return -1; + } + } + + + return 0; + } + + neos++; // increment the eos received + return -1; + } + +public: + ff_dreceiverOD(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid) {} + +private: + ack_t ACK; +}; + #endif diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index eb4fde1c..85eac84e 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -26,16 +26,6 @@ class ff_dreceiverMPI: public ff_monode_t { oarchive << reachableDestinations; - /*size_t sz = htobe64(buff.getLen()); - struct iovec iov[1]; - iov[0].iov_base = &sz; - iov[0].iov_len = sizeof(sz); - - if (writevn(sck, iov, 1) < 0 || writen(sck, buff.getPtr(), buff.getLen()) < 0){ - error("Error writing on socket the routing Table\n"); - return -1; - }*/ - if (MPI_Send(buff.getPtr(), buff.getLen(), MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) return -1; @@ -50,50 +40,6 @@ class ff_dreceiverMPI: public ff_monode_t { if (coreid!=-1) ff_mapThreadToCpu(coreid); - /*#ifdef LOCAL - if ((listen_sck=socket(AF_LOCAL, SOCK_STREAM, 0)) < 0){ - error("Error creating the socket\n"); - return -1; - } - - struct sockaddr_un serv_addr; - memset(&serv_addr, '0', sizeof(serv_addr)); - serv_addr.sun_family = AF_LOCAL; - strncpy(serv_addr.sun_path, acceptAddr.address.c_str(), acceptAddr.address.size()+1); - #endif - - #ifdef REMOTE - if ((listen_sck=socket(AF_INET, SOCK_STREAM, 0)) < 0){ - error("Error creating the socket\n"); - return -1; - } - - int enable = 1; - // enable the reuse of the address - if (setsockopt(listen_sck, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - error("setsockopt(SO_REUSEADDR) failed\n"); - - struct sockaddr_in serv_addr; - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; // still listening from any interface - serv_addr.sin_port = htons( acceptAddr.port ); - - #endif - - if (bind(listen_sck, (struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0){ - error("Error binding\n"); - return -1; - } - - if (listen(listen_sck, MAXBACKLOG) < 0){ - error("Error listening\n"); - return -1; - } - - /*for (const auto& e : routingTable) - std::cout << "Entry: " << e.first << " -> " << e.second << std::endl; - */ - return 0; } @@ -103,16 +49,16 @@ class ff_dreceiverMPI: public ff_monode_t { */ message_t *svc(message_t* task) { MPI_Status status; - char headerBuff[sizeof(size_t)+2*sizeof(int)]; + int header[3]; while(neos < input_channels){ - MPI_Recv(headerBuff, sizeof(size_t)+2*sizeof(int), MPI_BYTE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); + MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); if (status.MPI_TAG == DFF_ROUTING_TABLE_TAG){ sendRoutingTable(status.MPI_SOURCE); continue; } - size_t sz = *reinterpret_cast(headerBuff); + int sz = header[0]; if (sz == 0){ neos++; @@ -126,74 +72,101 @@ class ff_dreceiverMPI: public ff_monode_t { message_t* out = new message_t(buff, sz, true); assert(out); - out->sender = *reinterpret_cast(headerBuff+sizeof(size_t)); - out->chid = *reinterpret_cast(headerBuff+sizeof(size_t)+sizeof(int)); + out->sender = header[1]; + out->chid = header[2]; //std::cout << "received something from " << sender << " directed to " << chid << std::endl; ff_send_out_to(out, this->routingTable[out->chid]); // assume the routing table is consistent WARNING!!! } - - - - /* here i should receive the task via socket */ - - /*fd_set set, tmpset; - // intialize both sets (master, temp) - FD_ZERO(&set); - FD_ZERO(&tmpset); + return this->EOS; + } + +private: + size_t neos = 0; + size_t input_channels; + std::map routingTable; + int coreid; +}; + +/** versione Ondemand */ + + +class ff_dreceiverMPIOD: public ff_monode_t { +private: + + int sendRoutingTable(int rank){ + dataBuffer buff; std::ostream oss(&buff); + cereal::PortableBinaryOutputArchive oarchive(oss); + std::vector reachableDestinations; + + for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); - // add the listen socket to the master set - FD_SET(this->listen_sck, &set); + oarchive << reachableDestinations; + + if (MPI_Send(buff.getPtr(), buff.getLen(), MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) + return -1; - // hold the greater descriptor - int fdmax = this->listen_sck; + return 0; + } +public: + ff_dreceiverMPIOD(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, bool onDemand_ = false, int coreid=-1) + : input_channels(input_channels), routingTable(routingTable), onDemand(onDemand_), coreid(coreid) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + return 0; + } + + /* + Here i should not care of input type nor input data since they come from a socket listener. + Everything will be handled inside a while true in the body of this node where data is pulled from network + */ + message_t *svc(message_t* task) { + MPI_Request tmpAckReq; + MPI_Status status; + int header[3]; while(neos < input_channels){ - // copy the master set to the temporary - tmpset = set; + MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - switch(select(fdmax+1, &tmpset, NULL, NULL, NULL)){ - case -1: error("Error on selecting socket\n"); return EOS; - case 0: continue; + if (status.MPI_TAG == DFF_ROUTING_TABLE_TAG){ + sendRoutingTable(status.MPI_SOURCE); + continue; } - // iterate over the file descriptor to see which one is active - for(int i=0; i <= fdmax; i++) - if (FD_ISSET(i, &tmpset)){ - if (i == this->listen_sck) { - int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); - if (connfd == -1){ - error("Error accepting client\n"); - } else { - FD_SET(connfd, &set); - if(connfd > fdmax) fdmax = connfd; - - this->sendRoutingTable(connfd); // here i should check the result of the call! and handle possible errors! - } - continue; - } - - // it is not a new connection, call receive and handle possible errors - if (this->handleRequest(i) < 0){ - close(i); - FD_CLR(i, &set); - - // update the maximum file descriptor - if (i == fdmax) - for(int i=(fdmax-1);i>=0;--i) - if (FD_ISSET(i, &set)){ - fdmax = i; - break; - } - - } - } + size_t sz = header[0]; - } + if (sz == 0){ + neos++; + continue; + } + + char* buff = new char [sz]; + assert(buff); + + MPI_Recv(buff,sz,MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + message_t* out = new message_t(buff, sz, true); + assert(out); + out->sender = header[1]; + out->chid = header[2]; - /* In theory i should never return because of the while true. In our first example this is necessary */ + //std::cout << "received something from " << sender << " directed to " << chid << std::endl; + if (out->chid != -1) + ff_send_out_to(out, this->routingTable[out->chid]); // assume the routing table is consistent WARNING!!! + else + ff_send_out(out); + + if (onDemand){ + MPI_Isend(&ACK, sizeof(ack_t), MPI_BYTE, status.MPI_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &tmpAckReq); + MPI_Request_free(&tmpAckReq); + } + } + return this->EOS; } @@ -201,7 +174,9 @@ class ff_dreceiverMPI: public ff_monode_t { size_t neos = 0; size_t input_channels; std::map routingTable; + bool onDemand; int coreid; + ack_t ACK; }; #endif diff --git a/ff/distributed/ff_dreceiverOD.hpp b/ff/distributed/ff_dreceiverOD.hpp deleted file mode 100644 index 5ca0c8d4..00000000 --- a/ff/distributed/ff_dreceiverOD.hpp +++ /dev/null @@ -1,246 +0,0 @@ -#ifndef FF_DRECEIVEROD_H -#define FF_DRECEIVEROD_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; - -class ff_dreceiverOD: public ff_monode_t { -private: - - int sendRoutingTable(int sck){ - dataBuffer buff; std::ostream oss(&buff); - cereal::PortableBinaryOutputArchive oarchive(oss); - std::vector reachableDestinations; - - for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); - - oarchive << reachableDestinations; - - size_t sz = htobe64(buff.getLen()); - struct iovec iov[1]; - iov[0].iov_base = &sz; - iov[0].iov_len = sizeof(sz); - - if (writevn(sck, iov, 1) < 0 || writen(sck, buff.getPtr(), buff.getLen()) < 0){ - error("Error writing on socket the routing Table\n"); - return -1; - } - - return 0; - } - - int handleRequest(int sck){ - int sender; - int chid; - size_t sz; - struct iovec iov[3]; - iov[0].iov_base = &sender; - iov[0].iov_len = sizeof(sender); - iov[1].iov_base = &chid; - iov[1].iov_len = sizeof(chid); - iov[2].iov_base = &sz; - iov[2].iov_len = sizeof(sz); - - switch (readvn(sck, iov, 3)) { - case -1: error("Error reading from socket\n"); // fatal error - case 0: return -1; // connection close - } - - // convert values to host byte order - sender = ntohl(sender); - chid = ntohl(chid); - sz = be64toh(sz); - - if (sz > 0){ - char* buff = new char [sz]; - assert(buff); - if(readn(sck, buff, sz) < 0){ - error("Error reading from socket\n"); - delete [] buff; - return -1; - } - message_t* out = new message_t(buff, sz, true); - assert(out); - out->sender = sender; - out->chid = chid; - - if (chid != -1) - ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! - else - ff_send_out(out); - - if (onDemand) { - if (writen(sck, reinterpret_cast(&ACK),sizeof(ack_t)) < 0){ - if (errno != ECONNRESET || errno != EPIPE) { - error("Error sending back ACK to the sender (errno=%d)\n",errno); - return -1; - } - } - } - - return 0; - } - - neos++; // increment the eos received - return -1; - } - -public: - ff_dreceiverOD(const int dGroup_id, ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, bool onDemand = false, int coreid=-1) - : onDemand(onDemand), input_channels(input_channels), acceptAddr(acceptAddr), routingTable(routingTable), - distributedGroupId(dGroup_id), coreid(coreid) {} - - int svc_init() { - if (coreid!=-1) - ff_mapThreadToCpu(coreid); - - #ifdef LOCAL - if ((listen_sck=socket(AF_LOCAL, SOCK_STREAM, 0)) < 0){ - error("Error creating the socket\n"); - return -1; - } - - struct sockaddr_un serv_addr; - memset(&serv_addr, '0', sizeof(serv_addr)); - serv_addr.sun_family = AF_LOCAL; - strncpy(serv_addr.sun_path, acceptAddr.address.c_str(), acceptAddr.address.size()+1); - #endif - - #ifdef REMOTE - if ((listen_sck=socket(AF_INET, SOCK_STREAM, 0)) < 0){ - error("Error creating the socket\n"); - return -1; - } - - int enable = 1; - // enable the reuse of the address - if (setsockopt(listen_sck, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - error("setsockopt(SO_REUSEADDR) failed\n"); - - struct sockaddr_in serv_addr; - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; // still listening from any interface - serv_addr.sin_port = htons( acceptAddr.port ); - - #endif - - if (bind(listen_sck, (struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0){ - error("Error binding\n"); - return -1; - } - - if (listen(listen_sck, MAXBACKLOG) < 0){ - error("Error listening\n"); - return -1; - } - - /*for (const auto& e : routingTable) - std::cout << "Entry: " << e.first << " -> " << e.second << std::endl; - */ - - return 0; - } - void svc_end() { - close(this->listen_sck); - - #ifdef LOCAL - unlink(this->acceptAddr.address.c_str()); - #endif - } - /* - Here i should not care of input type nor input data since they come from a socket listener. - Everything will be handled inside a while true in the body of this node where data is pulled from network - */ - message_t *svc(message_t* task) { - /* here i should receive the task via socket */ - - fd_set set, tmpset; - // intialize both sets (master, temp) - FD_ZERO(&set); - FD_ZERO(&tmpset); - - // add the listen socket to the master set - FD_SET(this->listen_sck, &set); - - // hold the greater descriptor - int fdmax = this->listen_sck; - - while(neos < input_channels){ - // copy the master set to the temporary - tmpset = set; - - switch(select(fdmax+1, &tmpset, NULL, NULL, NULL)){ - case -1: error("Error on selecting socket\n"); return EOS; - case 0: continue; - } - - // iterate over the file descriptor to see which one is active - int fixed_last = this->last_receive_fd + 1; - for(int i=0; i <= fdmax; i++){ - int actualFD = (fixed_last + i) % (fdmax +1); - if (FD_ISSET(actualFD, &tmpset)){ - if (actualFD == this->listen_sck) { - int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); - if (connfd == -1){ - error("Error accepting client\n"); - } else { - FD_SET(connfd, &set); - if(connfd > fdmax) fdmax = connfd; - - this->sendRoutingTable(connfd); // here i should check the result of the call! and handle possible errors! - } - continue; - } - - // it is not a new connection, call receive and handle possible errors - if (this->handleRequest(actualFD) < 0){ - close(actualFD); - FD_CLR(actualFD, &set); - - // update the maximum file descriptor - if (actualFD == fdmax) - for(int ii=(fdmax-1);ii>=0;--ii) - if (FD_ISSET(ii, &set)){ - fdmax = i; - break; - } - - } - // save the last socket i - this->last_receive_fd = actualFD; - } - } - } - - /* In theory i should never return because of the while true. In our first example this is necessary */ - return this->EOS; - } - -private: - const bool onDemand; - size_t neos = 0; - size_t input_channels; - int listen_sck; - ff_endpoint acceptAddr; - std::map routingTable; - int distributedGroupId; - int coreid; - int last_receive_fd = 0; - ack_t ACK; -}; - -#endif diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index d02ba944..d0ee840d 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -23,20 +23,27 @@ using namespace ff; class ff_dsender: public ff_minode_t { -private: +protected: size_t neos=0; int next_rr_destination = 0; //next destiation to send for round robin policy std::vector dest_endpoints; std::map dest2Socket; - std::map sockets; + std::vector sockets; int coreid; int receiveReachableDestinations(int sck){ + size_t sz; + ssize_t r; - //while (readvn(sck, iov, 1) != sizeof(sz)) // wait untill a size is received! - recv(sck, &sz, sizeof(sz), MSG_WAITALL); - + if ((r=readn(sck, (char*)&sz, sizeof(sz)))!=sizeof(sz)) { + if (r==0) + error("Error unexpected connection closed by receiver\n"); + else + error("Error reading size (errno=%d)"); + return -1; + } + sz = be64toh(sz); std::cout << "Receiving routing table (" << sz << " bytes)" << std::endl; @@ -114,14 +121,15 @@ class ff_dsender: public ff_minode_t { #endif // receive the reachable destination from this sockets - receiveReachableDestinations(socketFD); + if (receiveReachableDestinations(socketFD) < 0) + return -1; return socketFD; } int tryConnect(const ff_endpoint &destination){ - int fd, retries = 0; + int fd = -1, retries = 0; while((fd = this->create_connect(destination)) < 0 && ++retries < MAX_RETRIES) if (retries < AGGRESSIVE_TRESHOLD) @@ -134,7 +142,6 @@ class ff_dsender: public ff_minode_t { } int sendToSck(int sck, message_t* task){ - //std::cout << "received something from " << task->sender << " directed to " << task->chid << std::endl; task->sender = htonl(task->sender); task->chid = htonl(task->chid); @@ -171,6 +178,7 @@ class ff_dsender: public ff_minode_t { if (coreid!=-1) ff_mapThreadToCpu(coreid); + sockets.resize(this->dest_endpoints.size()); for(size_t i=0; i < this->dest_endpoints.size(); i++) if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; @@ -180,8 +188,9 @@ class ff_dsender: public ff_minode_t { void svc_end() { // close the socket not matter if local or remote for(size_t i=0; i < this->sockets.size(); i++) - close(sockets[i]); + close(sockets[i]); } + message_t *svc(message_t* task) { /* here i should send the task via socket */ if (task->chid == -1){ // roundrobin over the destinations @@ -190,7 +199,6 @@ class ff_dsender: public ff_minode_t { } sendToSck(dest2Socket[task->chid], task); - delete task; return this->GO_ON; } @@ -200,13 +208,163 @@ class ff_dsender: public ff_minode_t { message_t * E_O_S = new message_t; E_O_S->chid = 0; E_O_S->sender = 0; - for(const auto& pair : sockets) - sendToSck(pair.second, E_O_S); + for(const auto& sck : sockets) + sendToSck(sck, E_O_S); delete E_O_S; } } +}; + + + +/* + ONDEMAND specification +*/ + +class ff_dsenderOD: public ff_dsender { +private: + int last_rr_socket = 0; //next destiation to send for round robin policy + std::map sockCounters; + const int queueDim; + fd_set set, tmpset; + int fdmax = -1; + + +public: + ff_dsenderOD(ff_endpoint dest_endpoint, int queueDim = 1, int coreid=-1) + : ff_dsender(dest_endpoint, coreid), queueDim(queueDim) {} + + ff_dsenderOD(std::vector dest_endpoints_, int queueDim = 1, int coreid=-1) + : ff_dsender(dest_endpoints_, coreid), queueDim(queueDim) {} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + FD_ZERO(&set); + FD_ZERO(&tmpset); + + sockets.resize(this->dest_endpoints.size()); + for(size_t i=0; i < this->dest_endpoints.size(); i++){ + if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; + + // execute the following block only if the scheduling is onDemand + + sockCounters[sockets[i]] = queueDim; + FD_SET(sockets[i], &set); + if (sockets[i] > fdmax) + fdmax = sockets[i]; + + } + + return 0; + } + + int waitAckFrom(int sck){ + while (sockCounters[sck] == 0){ + for (size_t i = 0; i < this->sockets.size(); ++i){ + int r; ack_t a; + if ((r = recvnnb(sockets[i], reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ + if (errno == EWOULDBLOCK){ + assert(r == -1); + continue; + } + perror("recvnnb ack"); + return -1; + } else + //printf("received ACK from conn %d\n", i); + sockCounters[sockets[i]]++; + + } + + if (sockCounters[sck] == 0){ + tmpset = set; + if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ + perror("select"); + return -1; + } + } + } + return 1; + } + + int waitAckFromAny() { + tmpset = set; + if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ + perror("select"); + return -1; + } + // try to receive from all connections in a non blocking way + for (size_t i = 0; i < this->sockets.size(); ++i){ + int r; ack_t a; + int sck = sockets[i]; + if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ + if (errno == EWOULDBLOCK){ + assert(r == -1); + continue; + } + perror("recvnnb ack"); + return -1; + } else { + sockCounters[sck]++; + last_rr_socket = i; + return sck; + } + } + assert(1==0); + return -1; + } + + int getNextReady(){ + for(size_t i = 0; i < this->sockets.size(); i++){ + int actualSocket = (last_rr_socket + 1 + i) % this->sockets.size(); + int sck = sockets[actualSocket]; + if (sockCounters[sck] > 0) { + last_rr_socket = actualSocket; + return sck; + } + } + return waitAckFromAny(); + } + + + void svc_end() { + long totalack = sockets.size()*queueDim; + long currack = 0; + for(const auto& pair : sockCounters) + currack += pair.second; + while(curracksockets.size(); i++) { + close(sockets[i]); + } + } + message_t *svc(message_t* task) { + int sck; + if (task->chid != -1){ + sck = dest2Socket[task->chid]; + if (sockCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand + error("Error waiting Ack from....\n"); + delete task; return this->GO_ON; + } + } else + sck = getNextReady(); // blocking call if scheduling is ondemand + + sendToSck(sck, task); + + // update the counters + sockCounters[sck]--; + + delete task; + return this->GO_ON; + } + }; diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 796a1c21..c89a4fcb 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -21,7 +20,7 @@ using namespace ff; class ff_dsenderMPI: public ff_minode_t { -private: +protected: size_t neos=0; int next_rr_destination = 0; //next destiation to send for round robin policy std::map dest2Rank; @@ -51,25 +50,6 @@ class ff_dsenderMPI: public ff_minode_t { return 0; } - - - int sendToSck(int rank, message_t* task){ - //std::cout << "received something from " << task->sender << " directed to " << task->chid << std::endl; - size_t sz = task->data.getLen(); - - char headerBuff[sizeof(size_t)+2*sizeof(int)]; - memcpy(headerBuff, &sz, sizeof(size_t)); - memcpy(headerBuff+sizeof(size_t), &task->sender, sizeof(int)); - memcpy(headerBuff+sizeof(int)+sizeof(size_t), &task->chid, sizeof(int)); - - if (MPI_Send(headerBuff, sizeof(size_t)+2*sizeof(int), MPI_BYTE, rank, DFF_HEADER_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) - return -1; - if (sz > 0) - if (MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) - return -1; - - return 0; - } public: @@ -99,7 +79,15 @@ class ff_dsenderMPI: public ff_minode_t { next_rr_destination = (next_rr_destination + 1) % dest2Rank.size(); } - sendToSck(dest2Rank[task->chid], task); + size_t sz = task->data.getLen(); + int rank =dest2Rank[task->chid]; + + int header[3] = {sz, task->sender, task->chid}; + + MPI_Send(header, 3, MPI_INT, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + + MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); + delete task; return this->GO_ON; @@ -107,17 +95,113 @@ class ff_dsenderMPI: public ff_minode_t { void eosnotify(ssize_t) { if (++neos >= this->get_num_inchannels()){ - message_t * E_O_S = new message_t; - E_O_S->chid = 0; - E_O_S->sender = 0; + int header[3] = {0,0,0}; + for(const auto& rank : destRanks) - sendToSck(rank, E_O_S); + MPI_Send(header, 3, MPI_INT, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + + } + } +}; + + +/* versione Ondemand */ + +class ff_dsenderMPIOD: public ff_dsenderMPI { +private: + int last_rr_rank = 0; //next destiation to send for round robin policy + std::map rankCounters; + int queueDim; + +public: + ff_dsenderMPIOD(int destRank, int queueDim_ = 1, int coreid=-1) + : ff_dsenderMPI(destRank, coreid), queueDim(queueDim_) {} + + ff_dsenderMPIOD( std::vector destRanks_, int queueDim_ = 1, int coreid=-1) + : ff_dsenderMPI(destRanks_, coreid), queueDim(queueDim_){} + + int svc_init() { + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + for(const int& rank: this->destRanks){ + receiveReachableDestinations(rank); + rankCounters[rank] = queueDim; + } + + return 0; + } + + void svc_end(){ + long totalack = destRanks.size()*queueDim; + long currack = 0; + + for(const auto& pair : rankCounters) currack += pair.second; + + while(currackdestRanks.size(); i++){ + int rankIndex = (last_rr_rank + 1 + i) % this->destRanks.size(); + int rank = destRanks[rankIndex]; + if (rankCounters[rank] > 0) { + last_rr_rank = rankIndex; + return rank; + } + } + return waitAckFromAny(); + } + + + message_t *svc(message_t* task) { + int rank; + if (task->chid != -1){ + rank = dest2Rank[task->chid]; + if (rankCounters[rank] == 0 && waitAckFrom(rank) == -1){ + error("Error waiting ACK\n"); + delete task; return this->GO_ON; + } + } else { + rank = getNextReady(); + } + + size_t sz = task->data.getLen(); + + int header[3] = {sz, task->sender, task->chid}; + + MPI_Send(header, 3, MPI_INT, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + + MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); + + rankCounters[rank]--; + delete task; + return this->GO_ON; + } }; #endif diff --git a/ff/distributed/ff_dsenderOD.hpp b/ff/distributed/ff_dsenderOD.hpp deleted file mode 100644 index 9a5c6985..00000000 --- a/ff/distributed/ff_dsenderOD.hpp +++ /dev/null @@ -1,323 +0,0 @@ -#ifndef FF_DSENDEROD_H -#define FF_DSENDEROD_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -using namespace ff; - -class ff_dsenderOD: public ff_minode_t { -private: - size_t neos=0; - int last_rr_socket = 0; //next destiation to send for round robin policy - const int queueDim; - std::vector dest_endpoints; - std::map dest2Socket; - std::map sockCounters; - std::vector sockets; - int coreid; - fd_set set, tmpset; - int fdmax = -1; - - int receiveReachableDestinations(int sck){ - size_t sz; - - // wait until the size is received! - //recv(sck, &sz, sizeof(sz), MSG_WAITALL); - long r; - if ((r=readn(sck, (char*)&sz, sizeof(sz)))!=sizeof(sz)) { - if (r==0) - error("Error unexpected connection closed by receiver\n"); - else - error("Error reading size (errno=%d)"); - return -1; - } - - sz = be64toh(sz); - - std::cout << "Receiving routing table (" << sz << " bytes)" << std::endl; - char* buff = new char [sz]; - assert(buff); - - if((r=readn(sck, buff, sz)) != (long)sz){ - if (r==0) error("Error unexpected connection closed by receiver\n"); - else error("Error reading from socket\n"); - delete [] buff; - return -1; - } - - dataBuffer dbuff(buff, sz, true); - std::istream iss(&dbuff); - cereal::PortableBinaryInputArchive iarchive(iss); - std::vector destinationsList; - - iarchive >> destinationsList; - - for (int d : destinationsList) dest2Socket[d] = sck; - - return 0; - } - - int create_connect(const ff_endpoint& destination){ - int socketFD; - - #ifdef LOCAL - socketFD = socket(AF_LOCAL, SOCK_STREAM, 0); - if (socketFD < 0){ - error("\nError creating socket \n"); - return socketFD; - } - struct sockaddr_un serv_addr; - memset(&serv_addr, '0', sizeof(serv_addr)); - serv_addr.sun_family = AF_LOCAL; - - strncpy(serv_addr.sun_path, destination.address.c_str(), destination.address.size()+1); - - if (connect(socketFD, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){ - close(socketFD); - return -1; - } - #endif - - #ifdef REMOTE - struct addrinfo hints; - struct addrinfo *result, *rp; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - hints.ai_socktype = SOCK_STREAM; /* Stream socket */ - hints.ai_flags = 0; - hints.ai_protocol = IPPROTO_TCP; /* Allow only TCP */ - - // resolve the address - if (getaddrinfo(destination.address.c_str() , std::to_string(destination.port).c_str() , &hints, &result) != 0) - return -1; - - // try to connect to a possible one of the resolution results - for (rp = result; rp != NULL; rp = rp->ai_next) { - socketFD = socket(rp->ai_family, rp->ai_socktype, - rp->ai_protocol); - if (socketFD == -1) - continue; - - if (connect(socketFD, rp->ai_addr, rp->ai_addrlen) != -1) - break; /* Success */ - - close(socketFD); - } - - if (rp == NULL) /* No address succeeded */ - return -1; - #endif - - // receive the reachable destination from this sockets - receiveReachableDestinations(socketFD); - - - return socketFD; - } - - int tryConnect(const ff_endpoint &destination){ - int fd, retries = 0; - - while((fd = this->create_connect(destination)) < 0 && ++retries < MAX_RETRIES) - if (retries < AGGRESSIVE_TRESHOLD) - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - else - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - //std::this_thread::sleep_for(std::chrono::milliseconds((long)std::pow(2, retries - AGGRESSIVE_TRESHOLD))); - - return fd; - } - - int sendToSck(int sck, message_t* task){ - //std::cout << "received something from " << task->sender << " directed to " << task->chid << std::endl; - task->sender = htonl(task->sender); - task->chid = htonl(task->chid); - - size_t sz = htobe64(task->data.getLen()); - struct iovec iov[4]; - iov[0].iov_base = &task->sender; - iov[0].iov_len = sizeof(task->sender); - iov[1].iov_base = &task->chid; - iov[1].iov_len = sizeof(task->chid); - iov[2].iov_base = &sz; - iov[2].iov_len = sizeof(sz); - iov[3].iov_base = task->data.getPtr(); - iov[3].iov_len = task->data.getLen(); - - if (writevn(sck, iov, 4) < 0){ - error("Error writing on socket\n"); - return -1; - } - - return 0; - } - - -public: - ff_dsenderOD(ff_endpoint dest_endpoint, int queueDim = 1, int coreid=-1) - : queueDim(queueDim), coreid(coreid) { - this->dest_endpoints.push_back(std::move(dest_endpoint)); - } - - ff_dsenderOD( std::vector dest_endpoints_, int queueDim = 1, int coreid=-1) - : queueDim(queueDim), dest_endpoints(std::move(dest_endpoints_)),coreid(coreid) {} - - int svc_init() { - if (coreid!=-1) - ff_mapThreadToCpu(coreid); - - FD_ZERO(&set); - FD_ZERO(&tmpset); - - sockets.resize(this->dest_endpoints.size()); - for(size_t i=0; i < this->dest_endpoints.size(); i++){ - if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; - - // execute the following block only if the scheduling is onDemand - - sockCounters[sockets[i]] = queueDim; - FD_SET(sockets[i], &set); - if (sockets[i] > fdmax) - fdmax = sockets[i]; - - } - - return 0; - } - - int waitAckFrom(int sck){ - while (sockCounters[sck] == 0){ - for (size_t i = 0; i < this->sockets.size(); ++i){ - int r; ack_t a; - if ((r = recvnnb(sockets[i], reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ - if (errno == EWOULDBLOCK){ - assert(r == -1); - continue; - } - perror("recvnnb ack"); - return -1; - } else - //printf("received ACK from conn %d\n", i); - sockCounters[sockets[i]]++; - - } - - if (sockCounters[sck] == 0){ - tmpset = set; - if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ - perror("select"); - return -1; - } - } - } - return 1; - } - - int waitAckFromAny() { - tmpset = set; - if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ - perror("select"); - return -1; - } - // try to receive from all connections in a non blocking way - for (size_t i = 0; i < this->sockets.size(); ++i){ - int r; ack_t a; - int sck = sockets[i]; - if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ - if (errno == EWOULDBLOCK){ - assert(r == -1); - continue; - } - perror("recvnnb ack"); - return -1; - } else { - sockCounters[sck]++; - last_rr_socket = i; - return sck; - } - } - assert(1==0); - return -1; - } - - int getNextReady(){ - for(size_t i = 0; i < this->sockets.size(); i++){ - int actualSocket = (last_rr_socket + 1 + i) % this->sockets.size(); - int sck = sockets[actualSocket]; - if (sockCounters[sck] > 0) { - last_rr_socket = actualSocket; - return sck; - } - } - return waitAckFromAny(); - } - - - void svc_end() { - long totalack = sockets.size()*queueDim; - long currack = 0; - for(const auto& pair : sockCounters) - currack += pair.second; - while(curracksockets.size(); i++) { - close(sockets[i]); - } - } - message_t *svc(message_t* task) { - int sck; - if (task->chid != -1){ - sck = dest2Socket[task->chid]; - if (sockCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand - error("Error waiting Ack from....\n"); - delete task; return this->GO_ON; - } - } else - sck = getNextReady(); // blocking call if scheduling is ondemand - - sendToSck(sck, task); - - // update the counters - sockCounters[sck]--; - - delete task; - return this->GO_ON; - } - - void eosnotify(ssize_t) { - if (++neos >= this->get_num_inchannels()){ - message_t * E_O_S = new message_t; - E_O_S->chid = 0; - E_O_S->sender = 0; - for(const auto& fd : sockets) - sendToSck(fd, E_O_S); - - delete E_O_S; - } - } - -}; - -#endif diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index a0401121..4df047a1 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -12,24 +12,6 @@ #include #include -#include -#include - -// define the type my_MPI_SIZE_T -#if SIZE_MAX == UCHAR_MAX - #define my_MPI_SIZE_T MPI_UNSIGNED_CHAR -#elif SIZE_MAX == USHRT_MAX - #define my_MPI_SIZE_T MPI_UNSIGNED_SHORT -#elif SIZE_MAX == UINT_MAX - #define my_MPI_SIZE_T MPI_UNSIGNED -#elif SIZE_MAX == ULONG_MAX - #define my_MPI_SIZE_T MPI_UNSIGNED_LONG -#elif SIZE_MAX == ULLONG_MAX - #define my_MPI_SIZE_T MPI_UNSIGNED_LONG_LONG -#else - #error "what is happening here?" -#endif - #define REMOTE #ifdef __APPLE__ @@ -184,6 +166,7 @@ static inline ssize_t recvnnb(int fd, char *buf, size_t size) { #define DFF_ROUTING_TABLE_TAG 2 #define DFF_TASK_TAG 3 #define DFF_HEADER_TAG 4 +#define DFF_ACK_TAG 5 #define DFF_REQUEST_ROUTING_TABLE 10 diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index f4e0ded5..24058786 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -204,4 +204,4 @@ int main(int argc, char** argv) { return 0; -} +} \ No newline at end of file From 70201932dadc46d28052991b9373ce044a1908c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 22 Jul 2021 17:55:13 +0200 Subject: [PATCH 066/202] First full working version of DFF with MPI --- ff/dff.hpp | 2 +- ff/distributed/ff_dgroup.hpp | 135 +++++++++++++++++--------- ff/distributed/ff_dgroups.hpp | 82 +++++++++++++--- ff/distributed/ff_dreceiverMPI.hpp | 90 ++++++----------- ff/distributed/ff_dsenderMPI.hpp | 41 ++++---- ff/distributed/ff_network.hpp | 22 +++-- tests/distributed/test_complete1.json | 1 + tests/distributed/test_complete5.json | 1 + 8 files changed, 228 insertions(+), 146 deletions(-) diff --git a/ff/dff.hpp b/ff/dff.hpp index 422733f2..046a1732 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -25,8 +25,8 @@ #define DFF_ENABLED #include +#include #include #include -#include #endif /* FF_DFF_HPP */ diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index a87b0d80..dc5d1cec 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -11,11 +11,18 @@ #include #include #include + +#ifdef DFF_MPI +#include +#include +#endif + #include #include // used for operators constexpr evaulations + namespace ff{ class dGroup; @@ -291,20 +298,43 @@ class dGroup : public ff_farm { return -1; // create receiver + Proto currentProto = dGroups::Instance()->usedProtocol; if (!isSource()){ - //std::cout << "Creating the receiver!" << std::endl; - if (onDemandReceiver) - this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! - else - this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); + if (currentProto == Proto::TCP){ + if (onDemandReceiver) + this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! + else + this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); + } + + #ifdef DFF_MPI + if (currentProto == Proto::MPI){ + if (onDemandReceiver) + this->add_emitter(new ff_dreceiverMPIOD(this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! + else + this->add_emitter(new ff_dreceiverMPI(this->expectedInputConnections, buildRoutingTable(level1BB))); + } + #endif + } // create sender if (!isSink()){ - //std::cout << "Creating the sender!" << std::endl; - if (onDemandSender) - this->add_collector(new ff_dsenderOD(this->destinations, onDemandQueueLength), true); - else - this->add_collector(new ff_dsender(this->destinations), true); + + if (currentProto == Proto::TCP){ + if (onDemandSender) + this->add_collector(new ff_dsenderOD(this->destinations, onDemandQueueLength), true); + else + this->add_collector(new ff_dsender(this->destinations), true); + } + + #ifdef DFF_MPI + if (currentProto == Proto::MPI){ + if (onDemandSender) + this->add_collector(new ff_dsenderMPIOD(this->destinations, onDemandQueueLength), true); + else + this->add_collector(new ff_dsenderMPI(this->destinations), true); + } + #endif } @@ -323,21 +353,14 @@ class dGroup : public ff_farm { int run(bool skip_init=false) override {return 0;} int run(ff_node* baseBB, bool skip_init=false) override { - - dGroups* groups_ = dGroups::Instance(); - groups_->parseConfig(); - buildFarm(reinterpret_cast(baseBB)); return ff_farm::run(skip_init); } - //int wait() {return ff_farm::wait();} - - void setEndpoint(const std::string address, const int port){ - this->endpoint.address = address; - this->endpoint.port = port; + void setEndpoint(ff_endpoint e){ + this->endpoint = e; } ff_endpoint getEndpoint(){return this->endpoint;} @@ -734,46 +757,70 @@ bool MySet::check_inout(ff_node* node){ return this->group->inout_.find(node) != this->group->inout_.end(); } - -void dGroups::parseConfig(){ - if (this->configFilePath.empty()) throw FF_Exception("Config file not defined!"); - - std::ifstream is(this->configFilePath); - - if (!is) throw FF_Exception("Unable to open configuration file for the program!"); - - std::vector parsedGroups; - - try { - cereal::JSONInputArchive ari(is); - ari(cereal::make_nvp("groups", parsedGroups)); - } catch (const cereal::Exception& e){ - std::cerr << "Error parsing the JSON config file. Check syntax and structure of the file and retry!" << std::endl; - exit(EXIT_FAILURE); +void dGroups::consolidateGroups(){ + for(int i = 0; i < parsedGroups.size(); i++){ + const G & g = parsedGroups[i]; + if (groups.find(g.name) != groups.end()) + switch (this->usedProtocol){ + case Proto::TCP : reinterpret_cast(groups[g.name])->setEndpoint(ff_endpoint(g.address, g.port)); break; + case Proto::MPI : reinterpret_cast(groups[g.name])->setEndpoint(ff_endpoint(i)); break; + } + else { + std::cout << "Cannot find group: " << g.name << std::endl; + throw FF_Exception("A specified group in the configuration file has not been implemented! :("); } + } - for(G& g : parsedGroups) - if (groups.find(g.name) != groups.end()) - reinterpret_cast(groups[g.name])->setEndpoint(g.address, g.port); - else { - std::cout << "Cannot find group: " << g.name << std::endl; - throw FF_Exception("A specified group in the configuration file has not been implemented! :("); - } - for(G& g : parsedGroups){ + for(G& g : parsedGroups){ dGroup* groupRef = reinterpret_cast(groups[g.name]); for(std::string& conn : g.Oconn) if (groups.find(conn) != groups.end()) groupRef->setDestination(reinterpret_cast(groups[conn])->getEndpoint()); else throw FF_Exception("A specified destination has a wrong name! :("); - groupRef->setExpectedInputConnections(expectedInputConnections(g.name, parsedGroups)); + groupRef->setExpectedInputConnections(this->expectedInputConnections(g.name)); } +} + +void dGroups::parseConfig(std::string configFile){ + + std::ifstream is(configFile); + + if (!is) throw FF_Exception("Unable to open configuration file for the program!"); + + try { + cereal::JSONInputArchive ari(is); + ari(cereal::make_nvp("groups", parsedGroups)); + + // get the protocol to be used from the configuration file + try { + std::string tmpProtocol; + ari(cereal::make_nvp("protocol", tmpProtocol)); + if (tmpProtocol == "MPI") + #ifdef DFF_MPI + this->usedProtocol = Proto::MPI; + #else + std::cout << "NO MPI support! Falling back to TCP\n"; + this->usedProtocol = Proto::TCP; + #endif + else this->usedProtocol = Proto::TCP; + } catch (cereal::Exception&) { + ari.setNextName(nullptr); + this->usedProtocol = Proto::TCP; + } + + } catch (const cereal::Exception& e){ + std::cerr << "Error parsing the JSON config file. Check syntax and structure of the file and retry!" << std::endl; + exit(EXIT_FAILURE); + } } } + + // redefinition of createGroup methods for ff_a2a and ff_pipeline ff::dGroup& ff_a2a::createGroup(std::string name){ dGroup * g = new dGroup(this, std::move(name)); diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 9e7ce1ce..95e12639 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -11,12 +11,21 @@ #include #include + + #include #include #include #include + +#ifdef DFF_MPI +#include +#endif + namespace ff { +enum Proto {TCP , MPI}; + class dGroups { public: @@ -26,19 +35,29 @@ class dGroups { return i; } - void parseConfig(); + Proto usedProtocol; + + void parseConfig(std::string); + + void consolidateGroups(); void addGroup(std::string label, ff_node* g){ groups.insert(make_pair(label, g));} int size(){ return groups.size();} - void setConfigFile(std::string f){this->configFilePath = f;} - void setRunningGroup(std::string g){this->runningGroup = g;} + + void setRunningGroupByRank(int rank){ + this->runningGroup = parsedGroups[rank].name; + } - const std::string& getRunningGroup() const { return runningGroup; } + const std::string& getRunningGroup() const { return runningGroup; } int run_and_wait_end(ff_node* parent){ + + // perfrom connection between groups + this->consolidateGroups(); + if (groups.find(runningGroup) == groups.end()){ ff::error("The group specified is not found nor implemented!\n"); return -1; @@ -48,17 +67,22 @@ class dGroups { if (runningGroup->run(parent) < 0) return -1; if (runningGroup->wait() < 0) return -1; + + #ifdef DFF_MPI + if (usedProtocol == Proto::MPI) + if (MPI_Finalize() != MPI_SUCCESS) abort(); + #endif + return 0; } protected: - dGroups() : groups(), configFilePath(), runningGroup() { + dGroups() : groups(), runningGroup() { // costruttore } private: inline static dGroups* i = nullptr; std::map groups; - std::string configFilePath; std::string runningGroup; // helper class to parse config file Json @@ -84,6 +108,8 @@ class dGroups { } }; + std::vector parsedGroups; + static inline std::vector split (const std::string &s, char delim) { std::vector result; std::stringstream ss (s); @@ -96,9 +122,9 @@ class dGroups { } - static int expectedInputConnections(std::string groupName, std::vector& groups){ + int expectedInputConnections(std::string groupName){ int result = 0; - for (const G& g : groups) + for (const G& g : parsedGroups) if (g.name != groupName) for (const std::string& conn : g.Oconn) if (conn == groupName) result++; @@ -107,8 +133,7 @@ class dGroups { }; static inline int DFF_Init(int& argc, char**& argv){ - - std::string configFile, groupName; + std::string configFile, groupName; for(int i = 0; i < argc; i++){ if (strstr(argv[i], "--DFF_Config") != NULL){ @@ -126,6 +151,7 @@ static inline int DFF_Init(int& argc, char**& argv){ continue; } + if (strstr(argv[i], "--DFF_GName") != NULL){ char * equalPosition = strchr(argv[i], '='); if (equalPosition == NULL){ @@ -146,15 +172,41 @@ static inline int DFF_Init(int& argc, char**& argv){ ff::error("Config file not passed as argument!\nUse option --DFF_Config=\"config-file-name\"\n"); return -1; } + + dGroups::Instance()->parseConfig(configFile); - if (groupName.empty()){ - ff::error("Group not passed as argument!\nUse option --DFF_GName=\"group-name\"\n"); - return -1; + if (dGroups::Instance()->usedProtocol == Proto::TCP){ + if (groupName.empty()){ + ff::error("Group not passed as argument!\nUse option --DFF_GName=\"group-name\"\n"); + return -1; + } + dGroups::Instance()->setRunningGroup(groupName); } - dGroups::Instance()->setRunningGroup(groupName); - dGroups::Instance()->setConfigFile(configFile); + #ifdef DFF_MPI + if (dGroups::Instance()->usedProtocol == Proto::MPI){ + //MPI_Init(&argc, &argv); + int provided; + + if (MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided) != MPI_SUCCESS) + return -1; + + + // no thread support + if (provided < MPI_THREAD_MULTIPLE){ + error("No thread support by MPI\n"); + return -1; + } + + int myrank; + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); + dGroups::Instance()->setRunningGroupByRank(myrank); + + std::cout << "Running group: " << dGroups::Instance()->getRunningGroup() << " on rank: " << myrank << "\n"; + } + #endif + // recompact the argv array int j = 0; diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 85eac84e..4fef8eee 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -15,7 +15,7 @@ using namespace ff; class ff_dreceiverMPI: public ff_monode_t { -private: +protected: int sendRoutingTable(int rank){ dataBuffer buff; std::ostream oss(&buff); @@ -26,8 +26,9 @@ class ff_dreceiverMPI: public ff_monode_t { oarchive << reachableDestinations; - if (MPI_Send(buff.getPtr(), buff.getLen(), MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) - return -1; + if (MPI_Send(buff.getPtr(), buff.getLen(), MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD) != MPI_SUCCESS){ + error("Something went wrong sending the routing table!\n"); + } return 0; } @@ -39,6 +40,14 @@ class ff_dreceiverMPI: public ff_monode_t { int svc_init() { if (coreid!=-1) ff_mapThreadToCpu(coreid); + + int r; + + MPI_Status status; + for(size_t i = 0; i < input_channels; i++){ + MPI_Recv(&r, 1, MPI_INT, MPI_ANY_SOURCE, DFF_ROUTING_TABLE_REQUEST_TAG, MPI_COMM_WORLD, &status); + sendRoutingTable(status.MPI_SOURCE); + } return 0; } @@ -51,13 +60,11 @@ class ff_dreceiverMPI: public ff_monode_t { MPI_Status status; int header[3]; while(neos < input_channels){ - MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - - if (status.MPI_TAG == DFF_ROUTING_TABLE_TAG){ - sendRoutingTable(status.MPI_SOURCE); - continue; - } + if (MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) + error("Error on Recv Receiver primo in alto\n"); + + int sz = header[0]; if (sz == 0){ @@ -65,25 +72,27 @@ class ff_dreceiverMPI: public ff_monode_t { continue; } - char* buff = new char [sz]; - assert(buff); + char* buff = new char[sz]; + assert(buff); - MPI_Recv(buff,sz,MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + if (MPI_Recv(buff,sz,MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE) != MPI_SUCCESS) + error("Error on Recv Receiver Payload\n"); message_t* out = new message_t(buff, sz, true); - assert(out); - out->sender = header[1]; - out->chid = header[2]; + assert(out); + out->sender = header[1]; + out->chid = header[2]; //std::cout << "received something from " << sender << " directed to " << chid << std::endl; ff_send_out_to(out, this->routingTable[out->chid]); // assume the routing table is consistent WARNING!!! + } return this->EOS; } -private: +protected: size_t neos = 0; size_t input_channels; std::map routingTable; @@ -93,35 +102,10 @@ class ff_dreceiverMPI: public ff_monode_t { /** versione Ondemand */ -class ff_dreceiverMPIOD: public ff_monode_t { -private: - - int sendRoutingTable(int rank){ - dataBuffer buff; std::ostream oss(&buff); - cereal::PortableBinaryOutputArchive oarchive(oss); - std::vector reachableDestinations; - - for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); - - oarchive << reachableDestinations; - - if (MPI_Send(buff.getPtr(), buff.getLen(), MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD) != MPI_SUCCESS) - return -1; - - return 0; - } - +class ff_dreceiverMPIOD: public ff_dreceiverMPI { public: - ff_dreceiverMPIOD(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, bool onDemand_ = false, int coreid=-1) - : input_channels(input_channels), routingTable(routingTable), onDemand(onDemand_), coreid(coreid) {} - - int svc_init() { - if (coreid!=-1) - ff_mapThreadToCpu(coreid); - - return 0; - } - + ff_dreceiverMPIOD(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : ff_dreceiverMPI(input_channels, routingTable, coreid) {} /* Here i should not care of input type nor input data since they come from a socket listener. Everything will be handled inside a while true in the body of this node where data is pulled from network @@ -131,12 +115,7 @@ class ff_dreceiverMPIOD: public ff_monode_t { MPI_Status status; int header[3]; while(neos < input_channels){ - MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - - if (status.MPI_TAG == DFF_ROUTING_TABLE_TAG){ - sendRoutingTable(status.MPI_SOURCE); - continue; - } + MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status); size_t sz = header[0]; @@ -161,21 +140,14 @@ class ff_dreceiverMPIOD: public ff_monode_t { else ff_send_out(out); - if (onDemand){ - MPI_Isend(&ACK, sizeof(ack_t), MPI_BYTE, status.MPI_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &tmpAckReq); - MPI_Request_free(&tmpAckReq); - } + MPI_Isend(&ACK, sizeof(ack_t), MPI_BYTE, status.MPI_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &tmpAckReq); + MPI_Request_free(&tmpAckReq); } return this->EOS; } private: - size_t neos = 0; - size_t input_channels; - std::map routingTable; - bool onDemand; - int coreid; ack_t ACK; }; diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index c89a4fcb..3cf44230 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -24,21 +24,22 @@ class ff_dsenderMPI: public ff_minode_t { size_t neos=0; int next_rr_destination = 0; //next destiation to send for round robin policy std::map dest2Rank; - std::vector destRanks; + std::vector destRanks; int coreid; int receiveReachableDestinations(int rank){ int sz; int cmd = DFF_REQUEST_ROUTING_TABLE; - char* buff = new char [1000]; - + MPI_Status status; - MPI_Sendrecv(&cmd, 1, MPI_INT, rank, DFF_ROUTING_TABLE_TAG, buff, 1000, MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD, &status); - + MPI_Send(&cmd, 1, MPI_INT, rank, DFF_ROUTING_TABLE_REQUEST_TAG, MPI_COMM_WORLD); + MPI_Probe(rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD, &status); MPI_Get_count(&status,MPI_BYTE, &sz); - + char* buff = new char [sz]; std::cout << "Received routing table (" << sz << " bytes)" << std::endl; - + MPI_Recv(buff, sz, MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + dataBuffer dbuff(buff, sz, true); std::istream iss(&dbuff); cereal::PortableBinaryInputArchive iarchive(iss); @@ -53,20 +54,20 @@ class ff_dsenderMPI: public ff_minode_t { public: - ff_dsenderMPI(int destRank, int coreid=-1) + ff_dsenderMPI(ff_endpoint destRank, int coreid=-1) : coreid(coreid) { this->destRanks.push_back(std::move(destRank)); } - ff_dsenderMPI( std::vector destRanks_, int coreid=-1) + ff_dsenderMPI( std::vector destRanks_, int coreid=-1) : destRanks(std::move(destRanks_)),coreid(coreid) {} int svc_init() { if (coreid!=-1) ff_mapThreadToCpu(coreid); - for(const int& rank: this->destRanks) - receiveReachableDestinations(rank); + for(ff_endpoint& ep: this->destRanks) + receiveReachableDestinations(ep.getRank()); return 0; } @@ -97,8 +98,8 @@ class ff_dsenderMPI: public ff_minode_t { if (++neos >= this->get_num_inchannels()){ int header[3] = {0,0,0}; - for(const auto& rank : destRanks) - MPI_Send(header, 3, MPI_INT, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + for(auto& ep : destRanks) + MPI_Send(header, 3, MPI_INT, ep.getRank(), DFF_HEADER_TAG, MPI_COMM_WORLD); } } @@ -114,19 +115,21 @@ class ff_dsenderMPIOD: public ff_dsenderMPI { int queueDim; public: - ff_dsenderMPIOD(int destRank, int queueDim_ = 1, int coreid=-1) + ff_dsenderMPIOD(ff_endpoint destRank, int queueDim_ = 1, int coreid=-1) : ff_dsenderMPI(destRank, coreid), queueDim(queueDim_) {} - ff_dsenderMPIOD( std::vector destRanks_, int queueDim_ = 1, int coreid=-1) + ff_dsenderMPIOD( std::vector destRanks_, int queueDim_ = 1, int coreid=-1) : ff_dsenderMPI(destRanks_, coreid), queueDim(queueDim_){} int svc_init() { + std::cout << "instantiating the ondemand mpi sender!\n"; + if (coreid!=-1) ff_mapThreadToCpu(coreid); - for(const int& rank: this->destRanks){ - receiveReachableDestinations(rank); - rankCounters[rank] = queueDim; + for(ff_endpoint& ep: this->destRanks){ + receiveReachableDestinations(ep.getRank()); + rankCounters[ep.getRank()] = queueDim; } return 0; @@ -167,7 +170,7 @@ class ff_dsenderMPIOD: public ff_dsenderMPI { int getNextReady(){ for(size_t i = 0; i < this->destRanks.size(); i++){ int rankIndex = (last_rr_rank + 1 + i) % this->destRanks.size(); - int rank = destRanks[rankIndex]; + int rank = destRanks[rankIndex].getRank(); if (rankCounters[rank] > 0) { last_rr_rank = rankIndex; return rank; diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 4df047a1..b362a0e5 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -76,6 +76,10 @@ struct ack_t { }; struct ff_endpoint { + ff_endpoint(){} + ff_endpoint(std::string addr, int port) : address(addr), port(port) {} + ff_endpoint(int rank) : port(rank) {} + const int getRank() {return port;} std::string address; int port; }; @@ -152,7 +156,7 @@ static inline ssize_t recvnnb(int fd, char *buf, size_t size) { } if (r == 0) return 0; // EOF - left -= r; + left -= r; buf += r; } return (size-left); @@ -162,12 +166,14 @@ static inline ssize_t recvnnb(int fd, char *buf, size_t size) { /* MPI DEFINES */ - -#define DFF_ROUTING_TABLE_TAG 2 -#define DFF_TASK_TAG 3 -#define DFF_HEADER_TAG 4 -#define DFF_ACK_TAG 5 - -#define DFF_REQUEST_ROUTING_TABLE 10 +#ifdef DFF_MPI + #define DFF_ROUTING_TABLE_REQUEST_TAG 9 + #define DFF_ROUTING_TABLE_TAG 2 + #define DFF_TASK_TAG 3 + #define DFF_HEADER_TAG 4 + #define DFF_ACK_TAG 5 + + #define DFF_REQUEST_ROUTING_TABLE 10 +#endif #endif diff --git a/tests/distributed/test_complete1.json b/tests/distributed/test_complete1.json index beae1ddf..63d2e24d 100644 --- a/tests/distributed/test_complete1.json +++ b/tests/distributed/test_complete1.json @@ -1,4 +1,5 @@ { + "protocol" : "MPI", "groups" : [ { "endpoint" : "localhost:8004", diff --git a/tests/distributed/test_complete5.json b/tests/distributed/test_complete5.json index 9330dfd6..52be0e32 100644 --- a/tests/distributed/test_complete5.json +++ b/tests/distributed/test_complete5.json @@ -1,4 +1,5 @@ { + "protocol" : "MPI", "groups" : [ { "endpoint" : "localhost:8004", From aba074e75fd09432f93317e1efd0aae4273825ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 22 Jul 2021 18:01:11 +0200 Subject: [PATCH 067/202] Fixed issues with MPI support, added default define in dff.hpp to enable MPI by default --- ff/dff.hpp | 2 +- ff/distributed/ff_dgroup.hpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ff/dff.hpp b/ff/dff.hpp index 046a1732..89373b1d 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -23,7 +23,7 @@ #define FF_DFF_HPP #define DFF_ENABLED - +#define DFF_MPI #include #include #include diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index dc5d1cec..774fee01 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -798,14 +798,15 @@ void dGroups::parseConfig(std::string configFile){ try { std::string tmpProtocol; ari(cereal::make_nvp("protocol", tmpProtocol)); - if (tmpProtocol == "MPI") + if (tmpProtocol == "MPI"){ #ifdef DFF_MPI this->usedProtocol = Proto::MPI; #else std::cout << "NO MPI support! Falling back to TCP\n"; this->usedProtocol = Proto::TCP; #endif - else this->usedProtocol = Proto::TCP; + + } else this->usedProtocol = Proto::TCP; } catch (cereal::Exception&) { ari.setNextName(nullptr); this->usedProtocol = Proto::TCP; From 7b77ed9dc841988b0e327ab2dcbeaec3c29944fb Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 24 Jul 2021 09:29:28 +0200 Subject: [PATCH 068/202] improved BLOCKING_MODE support --- ff/combine.hpp | 3 +- ff/farm.hpp | 12 +- ff/lb.hpp | 10 +- ff/multinode.hpp | 3 + ff/node.hpp | 11 +- ff/pipeline.hpp | 2 +- tests/test_multi_output5.cpp | 26 +- tests/test_multi_output6.cpp | 18 +- tests/wordcount/Makefile | 56 +++++ tests/wordcount/wordcount.cpp | 434 ++++++++++++++++++++++++++++++++++ 10 files changed, 549 insertions(+), 26 deletions(-) create mode 100644 tests/wordcount/Makefile create mode 100644 tests/wordcount/wordcount.cpp diff --git a/ff/combine.hpp b/ff/combine.hpp index 5dea0665..0e7910c4 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -693,7 +693,8 @@ class ff_comb: public ff_minode { // cond variable. This is due to the put_done method in the lb // (i.e. the prev node is a multi-output or an emitter node) assert(n->cons_m == nullptr); - n->cons_c = c; n->cons_m = nullptr; + n->set_cons_c(c); + //n->cons_c = c; n->cons_m = nullptr; <---- TOGLIERE return true; } // producer diff --git a/ff/farm.hpp b/ff/farm.hpp index 9dcdf4bc..6d5349e5 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -369,12 +369,14 @@ class ff_farm: public ff_node { if (W2[i]->set_output(outputNodes[i])<0) return -1; } } else { - // NOTE: the following call might fail because the buffers were already created for example by - // the pipeline that contains this stage - if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) { - if (lb->masterworker()) return -1; // something went wrong - } + // TODO: for the moment we support only feedback channels and not both feedback and forward channels + // for the last stages of the last all-to-all if (lb->masterworker()) { + if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) { + error("FARM failed to create feedback channels\n"); + return -1; + } + for(size_t i=0;i w(1); W2[i]->get_out_nodes(w); diff --git a/ff/lb.hpp b/ff/lb.hpp index ebc63162..be715d64 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -78,6 +78,7 @@ class ff_loadbalancer: public ff_thread { protected: inline void put_done(int id) { + // here we access the cond variable of the worker, that must be initialized pthread_cond_signal(&workers[id]->get_cons_c()); } @@ -115,7 +116,12 @@ class ff_loadbalancer: public ff_thread { bool /*canoverwrite*/=false) { assert(1==0); } - + + virtual inline void set_cons_c(pthread_cond_t *c) { + assert(cons_c == nullptr); + assert(cons_m == nullptr); + cons_c = c; + } virtual inline pthread_cond_t &get_cons_c() { return *cons_c;} /** @@ -522,7 +528,7 @@ class ff_loadbalancer: public ff_thread { free(cons_m); cons_m = nullptr; } - if (cons_c) { + if (cons_c && cons_m) { pthread_cond_destroy(cons_c); free(cons_c); cons_c = nullptr; diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 7361db54..fc6217a8 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -517,6 +517,9 @@ class ff_monode: public ff_node { ff_node::set_output_blocking(m,c, canoverwrite); } + virtual inline void set_cons_c(pthread_cond_t *c) { + lb->set_cons_c(c); + } virtual inline pthread_cond_t &get_cons_c() { return lb->get_cons_c();} public: diff --git a/ff/node.hpp b/ff/node.hpp index 1bebfd2d..4813ad34 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -623,6 +623,14 @@ class ff_node { FF_IGNORE_UNUSED(m); p_cons_c = c; } + + // this function is used mainly for combined node where the cond variable must + // be shared with the first internal node + virtual inline void set_cons_c(pthread_cond_t *c) { + assert(cons_c == nullptr); + assert(cons_m == nullptr); + cons_c = c; + } virtual inline pthread_cond_t &get_cons_c() { return *cons_c;} /** @@ -1603,6 +1611,8 @@ struct ff_buffernode: ff_node { } return 0; } + + void reset_blocking_out() { blocking_out = false; } bool ff_send_out(void *ptr, int id=-1, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { @@ -1619,7 +1629,6 @@ struct ff_buffernode: ff_node { } - protected: void* svc(void*){return NULL;} diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 18e5eea9..b631d7eb 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -187,7 +187,7 @@ class ff_pipeline: public ff_node { error("PIPE, init input blocking mode for node %d\n", i); return -1; } - if (!skip_set_output_blocking) // we do not wait to overwrite previous setting + if (!skip_set_output_blocking) // we do not want to overwrite previous setting nodes_list[i-1]->set_output_blocking(m,c); if (!nodes_list[i-1]->init_output_blocking(m,c,false)) { error("PIPE, init output blocking mode for node %d\n", i-1); diff --git a/tests/test_multi_output5.cpp b/tests/test_multi_output5.cpp index 722dd48f..6ab45066 100644 --- a/tests/test_multi_output5.cpp +++ b/tests/test_multi_output5.cpp @@ -294,9 +294,22 @@ struct Manager: ff_node_t { Manager(): channel1(100, true, MAX_NUM_THREADS+100), - channel2(100, true, MAX_NUM_THREADS+200) {} + channel2(100, true, MAX_NUM_THREADS+200) { + + // The following is needed if the program runs in BLOCKING_MODE + // In this case, channel1 and channel2 will not be automatically + // initialized so we have to do it manually. Moreover, even if we + // want to run in BLOCKING_MODE, channel1 and channel2 cannot be + // configured in blocking mode for the output, that is, ff_send_out + // has to be non-blocking!!!! + channel1.init_blocking_stuff(); + channel2.init_blocking_stuff(); + channel1.reset_blocking_out(); + channel2.reset_blocking_out(); + } Command_t* svc(Command_t *) { + Command_t *cmd1 = new Command_t(0, REMOVE); channel1.ff_send_out(cmd1); Command_t *cmd2 = new Command_t(1, REMOVE); @@ -328,7 +341,9 @@ struct Manager: ff_node_t { } - int run(bool=false) { return ff_node_t::run(); } + int run(bool=false) { + return ff_node_t::run(); + } int wait() { return ff_node_t::wait(); } @@ -342,13 +357,6 @@ struct Manager: ff_node_t { int main(int argc, char* argv[]) { -#if defined(BLOCKING_MODE) - - //TODO: in blocking mode the manager channel does not work!!! - return 0; -#endif - - unsigned nworkers = 3; int ntasks = 1000; if (argc>1) { diff --git a/tests/test_multi_output6.cpp b/tests/test_multi_output6.cpp index 8cbf1a5b..e6d1d6f7 100644 --- a/tests/test_multi_output6.cpp +++ b/tests/test_multi_output6.cpp @@ -244,7 +244,17 @@ struct Collector: ff_minode_t { struct Manager: ff_node_t { Manager(): - channel(100, true, MANAGERID) {} + channel(100, true, MANAGERID) { + + // The following is needed if the program runs in BLOCKING_MODE + // In this case, channel will not be automatically initialized + // so we have to do it manually. Moreover, even if we + // want to run in BLOCKING_MODE, channel cannot be + // configured in blocking mode for the output, that is, ff_send_out + // has to be non-blocking!!!! + channel.init_blocking_stuff(); + channel.reset_blocking_out(); + } Command_t* svc(Command_t *) { @@ -294,12 +304,6 @@ struct Manager: ff_node_t { int main(int argc, char* argv[]) { -#if defined(BLOCKING_MODE) - //TODO: in blocking mode the manager channel does not work!!! - return 0; -#endif - - unsigned nworkers = 3; int ntasks = 1000; if (argc>1) { diff --git a/tests/wordcount/Makefile b/tests/wordcount/Makefile new file mode 100644 index 00000000..d7a2308b --- /dev/null +++ b/tests/wordcount/Makefile @@ -0,0 +1,56 @@ +# --------------------------------------------------------------------------- +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception, you may use this file as part of a free software +# library without restriction. Specifically, if other files instantiate +# templates or use macros or inline functions from this file, or you compile +# this file and link it with other files to produce an executable, this +# file does not by itself cause the resulting executable to be covered by +# the GNU General Public License. This exception does not however +# invalidate any other reasons why the executable file might be covered by +# the GNU General Public License. +# +# --------------------------------------------------------------------------- + +CXXFLAGS += -std=c++17 -O3 -finline-functions -DNDEBUG +LIBS = -pthread +INCLUDES = -I ../../ + +SOURCES = $(wildcard *.cpp) +TARGET = $(SOURCES:.cpp=) + +.PHONY: all clean cleanall +.SUFFIXES: .c .cpp .o + +%.d: %.cpp + set -e; $(CXX) -MM $(INCLUDES) $(CXXFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.d: %.c + set -e; $(CC) -MM $(INCLUDES) $(CFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< +%: %.cpp + $(CXX) $(INCLUDES) $(CXXFLAGS) $(OPTIMIZE_FLAGS) -o $@ $< $(LDFLAGS) $(LIBS) + +all: $(TARGET) + +clean: + -rm -fr *.o *~ +cleanall: clean + -rm -fr $(TARGET) *.d + +include $(OBJS:.o=.d) diff --git a/tests/wordcount/wordcount.cpp b/tests/wordcount/wordcount.cpp new file mode 100644 index 00000000..0b977401 --- /dev/null +++ b/tests/wordcount/wordcount.cpp @@ -0,0 +1,434 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + +/* Simple test for the StaticAllocator (to use instead the standard allocator + * use command line option '-a'). + * + * + * The Source produces a continuous stream of lines of a text file. + * The Splitter tokenizes each line producing a new output item for each + * word extracted. The Counter receives single words from the line + * splitter and counts how many occurrences of the same word appeared + * on the stream until that moment. The Sink node receives every result + * produced by the word counter and counts the total number of words. + * + * One possible FastFlow graph is the following: + * + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * + * /<---- pipe ---->/ /<-- pipe -->/ + * /<---------------- a2a ---------------->/ + * + * Source and FlatMap produce more outputs for a single input, + * the data items flowing into the output streams are allocated + * using the StaticAllocator (one for each Source and FlatMap). + * + * The StaticAllocator in each Source node uses the following amount of memory: + * 1*(qlen+2) + * + * The StaticAllocator in each FlatMap node uses the following amount of memory: + * (1 + 1) * (qlen+2) * nSink + * + * + * Command line options: + * -f file : text file + * -p n,m : defines the number of replicas of the pipeline Source --> Splitter (n) + * and of the pipeline Counter --> Sink (m) + * -a : if set, the program will use the standard allocator (optional) + * -t time : running time in seconds in the range 1-100 (optional, default value 15s) + * + * + */ + +#define FF_BOUNDED_BUFFER +#if !defined(DEFAULT_BUFFER_CAPACITY) +#define DEFAULT_BUFFER_CAPACITY 2048 +#endif +#define BYKEY true + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ff; +using namespace std; + +const size_t qlen = DEFAULT_BUFFER_CAPACITY; +const int MAXLINE=128; +const int MAXWORD=32; +struct tuple_t { + char text_line[MAXLINE]; // line of the parsed dataset (text, book, ...) + size_t key; // line number + uint64_t id; // id set to zero + uint64_t ts; // timestamp +}; +struct result_t { + char key[MAXWORD]; // key word + uint64_t id; // id that indicates the current number of occurrences of the key word + uint64_t ts; // timestamp +}; + + +vector dataset; // contains all the input tuples in memory +atomic total_lines=0; // total number of lines processed by the system +atomic total_bytes=0; // total number of bytes processed by the system + +/// application run time (source generates the stream for app_run_time seconds, then sends out EOS) +unsigned long app_run_time = 15; // time in seconds + +static inline unsigned long current_time_usecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000L + (t.tv_nsec / 1000); +} +static inline unsigned long current_time_nsecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000000L + t.tv_nsec; +} + + +struct Source: ff_monode_t { + StaticAllocator* SAlloc=nullptr; + size_t next_tuple_idx = 0; // index of the next tuple to be sent + int generations = 0; // counts the times the file is generated + long generated_tuples = 0; // tuples counter + long generated_bytes = 0; // bytes counter + + // time variables + unsigned long app_start_time; // application start time + unsigned long current_time; + + Source(StaticAllocator* SAlloc, + const unsigned long _app_start_time): + SAlloc(SAlloc), app_start_time(_app_start_time),current_time(_app_start_time) { + } + + int svc_init() { + return (SAlloc?SAlloc->init():0); + } + tuple_t* svc(tuple_t*) { + while(1) { + tuple_t* t; + if (SAlloc) { + SAlloc->alloc(t); + } else { + t = new tuple_t; + } + assert(t); + + if (generated_tuples > 0) current_time = current_time_nsecs(); + if (next_tuple_idx == 0) generations++; // file generations counter + generated_tuples++; // tuples counter + // put a timestamp and send the tuple + *t = dataset.at(next_tuple_idx); + generated_bytes += sizeof(tuple_t); + t->ts = current_time - app_start_time; + ff_send_out(t); + ++next_tuple_idx; +#if defined(ONESHOT) // define it for a one shot run + if (next_tuple_idx == dataset.size()) break; +#endif + next_tuple_idx %= dataset.size(); + // EOS reached + if (current_time - app_start_time >= (app_run_time*1000000000L) && next_tuple_idx == 0) + break; + } + total_lines.fetch_add(generated_tuples); + total_bytes.fetch_add(generated_bytes); + return EOS; + } +}; +struct Splitter: ff_monode_t { + Splitter(StaticAllocator* SAlloc): SAlloc(SAlloc) { + } + + int svc_init() { + noutch=get_num_outchannels(); + return (SAlloc?SAlloc->init():0); + } + + result_t* svc(tuple_t* in) { + char *tmpstr; + char *token = strtok_r(in->text_line, " ", &tmpstr); + while (token) { +#if defined(BYKEY) + int ch = std::hash()(std::string(token)) % noutch; +#else + int ch = ++idx % noutch; +#endif + result_t* r; + if (SAlloc) { + SAlloc->alloc(r,ch); + } else { + r = new result_t; + } + assert(r); + strncpy(r->key, token, MAXWORD-1); + r->key[MAXWORD-1]='\0'; + r->ts = in->ts; + + ff_send_out_to(r, ch); + token = strtok_r(NULL, " ", &tmpstr); + } + + if (SAlloc) + StaticAllocator::dealloc(in); + else + delete in; + + return GO_ON; + } + StaticAllocator* SAlloc=nullptr; + long noutch=0; + long idx=-1; +}; + +struct Counter: ff_minode_t { + result_t* svc(result_t* in) { + ++M[std::string(in->key)]; + // number of occurrences of the string word up to now + in->id = M[std::string(in->key)]; + return in; + } + size_t unique() { + // std::cout << "Counter:\n"; + // for (const auto& kv : M) { + // std::cout << kv.first << " --> " << kv.second << "\n"; + // } + return M.size(); + } + std::map M; +}; + +struct Sink: ff_node_t { + Sink(bool usestd):usestd(usestd) {} + + result_t* svc(result_t* in) { + ++words; + if (usestd) delete in; + else + StaticAllocator::dealloc(in); + return GO_ON; + } + size_t words=0; + bool usestd; +}; + + +/** + * @brief Parse the input file and create all the tuples + * + * The created tuples are maintained in memory. The source node will generate the stream by + * reading all the tuples from main memory. + * + * @param file_path the path of the input dataset file + */ +int parse_dataset_and_create_tuples(const string& file_path) { + ifstream file(file_path); + if (file.is_open()) { + size_t all_records = 0; // counter of all records (dataset line) read + string line; + while (getline(file, line)) { + // process file line + if (!line.empty()) { + if (line.length() > MAXLINE) { + std::cerr << "ERROR INCREASE MAXLINE\n"; + return -1; + } + tuple_t t; + strncpy(t.text_line, line.c_str(), MAXLINE-1); + t.text_line[MAXLINE-1]='\0'; + t.key = all_records; + t.id = 0; + t.ts = 0; + all_records++; + dataset.push_back(t); + } + } + file.close(); + } else { + std::cerr << "Unable to open the file " << file_path << "\n"; + return -1; + } + return 0; +} + + +int main(int argc, char* argv[]) { + /// parse arguments from command line + std::string file_path(""); + size_t source_par_deg = 0; + size_t sink_par_deg = 0; + bool usestd = false; + + if (argc >= 3) { + int option = 0; + while ((option = getopt(argc, argv, "f:p:t:a")) != -1) { + switch (option) { + case 'f': file_path=string(optarg); break; + case 'p': { + vector par_degs; + string pars(optarg); + stringstream ss(pars); + for (size_t i; ss >> i;) { + par_degs.push_back(i); + if (ss.peek() == ',') + ss.ignore(); + } + if (par_degs.size() != 2) { + std::cerr << "Error in parsing the input arguments -p, the format is n,m\n"; + return -1; + } + else { + source_par_deg = par_degs[0]; + sink_par_deg = par_degs[1]; + } + break; + } + case 'a': { usestd=true; } break; + case 't': { + long t = stol(optarg); + if (t<=0 || t > 100) { + std::cerr << "Wrong value for the '-t' option, it should be in the range [1,100]\n"; + return -1; + } + app_run_time = t; + } break; + default: { + std::cerr << "Error in parsing the input arguments\n"; + return -1; + } + } + } + } else { + std::cerr << "Parameters: -p -f [-a -t n]\n"; + return -1; + } + if (file_path.length()==0) { + std::cerr << "The file path is empty, please use option -f \n"; + return -1; + } + if (source_par_deg == 0 || sink_par_deg==0) { + std::cerr << "Wrong values for the parallelism degree, please use option -p \n"; + return -1; + } + + cout << "Executing WordCount with parameters:" << endl; + cout << " * queues length : " << DEFAULT_BUFFER_CAPACITY << endl; + cout << " * source/splitter: " << source_par_deg << endl; + cout << " * counter/sink : " << sink_par_deg << endl; + cout << " * running time : " << app_run_time << " (s)\n"; + if (usestd) cout << " USING STANDARD ALLOCATOR\n"; + cout << " * topology: source -> splitter -> counter -> sink" << endl; + + /// data pre-processing + parse_dataset_and_create_tuples(file_path); + /// application starting time + unsigned long app_start_time = current_time_nsecs(); + + std::vector C(sink_par_deg); + std::vector S(sink_par_deg); + std::vector L; + std::vector R; + + for (size_t i=0;iadd_stage(new Source((usestd?nullptr:SourceAlloc), app_start_time), true); + pipe0->add_stage(new Splitter((usestd?nullptr:SplitterAlloc)), true); + L.push_back(pipe0); + } + for (size_t i=0;iadd_stage(C[i]); + pipe1->add_stage(S[i]); + R.push_back(pipe1); + } + + ff_a2a a2a(false, qlen, qlen, true); + a2a.add_firstset(L, 0, true); + a2a.add_secondset(R, true); + ff_pipeline pipeMain(false, qlen, qlen, true); + pipeMain.add_stage(&a2a); + + std::cout << "Starting " << pipeMain.numThreads() << " threads\n"; + cout << "Executing topology" << endl; + /// evaluate topology execution time + volatile unsigned long start_time_main_usecs = current_time_usecs(); + if (pipeMain.run_and_wait_end()<0) { + error("running pipeMain\n"); + return -1; + } + volatile unsigned long end_time_main_usecs = current_time_usecs(); + cout << "Exiting" << endl; + double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); + cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; + double throughput = total_lines / elapsed_time_seconds; + double mbs = (double)((total_bytes / 1048576) / elapsed_time_seconds); + cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; + cout << "total_lines sent : " << total_lines << "\n"; + cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; + + +#if 1 + size_t words=0; + size_t unique=0; + for(size_t i=0;iwords; + unique+= C[i]->unique(); + delete S[i]; + delete C[i]; + } + cout << "words : " << words << "\n"; + cout << "unique : " << unique<< "\n"; +#endif + return 0; +} From 6c2597d51a4a61b453ce7612f100b5c9262fbeb2 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 24 Jul 2021 10:44:07 +0200 Subject: [PATCH 069/202] enabled BLOCKING_MODE by default for distributed FastFlow programs (fixed some issues related to blocking mode) --- ff/dff.hpp | 8 ++++++++ ff/distributed/ff_wrappers.hpp | 16 ++++++++++++++-- tests/distributed/Makefile | 9 ++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/ff/dff.hpp b/ff/dff.hpp index 89373b1d..b03ba5e9 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -23,7 +23,15 @@ #define FF_DFF_HPP #define DFF_ENABLED +#if !defined(DFF_EXCLUDE_MPI) #define DFF_MPI +#endif +#if !defined(DFF_EXCLUDE_BLOCKING) +#define BLOCKING_MODE +#else +#undef BLOCKING_MODE +#endif + #include #include #include diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index f52edc23..b7d4340f 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -191,8 +191,7 @@ class WrapperOUT: public internal_mo_transformer { int run(bool skip_init=false) { return internal_mo_transformer::run(skip_init); } - - + ff_serialize_F_t transform_; ff_serialize_F_t getTransform() { return this->transform_;} @@ -323,6 +322,19 @@ class WrapperINOUT: public internal_mi_transformer { serialize(reinterpret_cast(out), -1); return GO_ON; } + + bool init_output_blocking(pthread_mutex_t *&m, + pthread_cond_t *&c, + bool feedback=true) { + return ff_node::init_output_blocking(m,c,feedback); + } + + void set_output_blocking(pthread_mutex_t *&m, + pthread_cond_t *&c, + bool canoverwrite=false) { + ff_node::set_output_blocking(m,c,canoverwrite); + } + ff_deserialize_F_t finalizer_; std::function(Tout*)> transform_; diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index 4e0f56ec..8728b709 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -23,14 +23,17 @@ # # --------------------------------------------------------------------------- -CXXFLAGS += -std=c++17 +CXXFLAGS += -std=c++17 ifdef DEBUG OPTIMIZE_FLAGS += -g -fno-inline-functions else OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG endif -ifdef BLOCKING_MODE - CXXFLAGS += -DBLOCKING_MODE +ifdef EXCLUDE_BLOCKING + CXXFLAGS += -DDFF_EXCLUDE_BLOCKING +endif +ifdef EXCLUDE_MPI + CXXFLAGS += -DDFF_EXCLUDE_MPI endif ifdef FF_HOME INCS += -I$(FF_HOME) From f22fd0a3af46a5dc503a46a3701b4cc2d66d50e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 23 Aug 2021 15:51:02 +0200 Subject: [PATCH 070/202] - Added script to automatize the parametric perf test on clusters - Loader (dff_run) has now the ability to force the distributed protocol: MPI and TCP - Added distributed printer to tag the output of stdout when multiple processes print on screen. To use this feature ff::cout << something << ff::endl; - Loader (dff_run) has now the ability to build an host file for mpi based on the directives described in the dff configuration file. Now the execution of MPI application can be triggered correctly directly from the loader. --- ff/dff.hpp | 2 + ff/distributed/ff_dgroup.hpp | 3 +- ff/distributed/ff_dgroups.hpp | 12 ++ ff/distributed/ff_dreceiver.hpp | 2 - ff/distributed/loader/dff_run.cpp | 167 +++++++++++++++++----- tests/distributed/test_complete5.cpp | 2 +- tests/distributed/test_parametricPerf.cpp | 63 ++++---- 7 files changed, 178 insertions(+), 73 deletions(-) diff --git a/ff/dff.hpp b/ff/dff.hpp index b03ba5e9..6fc9ceb9 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -23,9 +23,11 @@ #define FF_DFF_HPP #define DFF_ENABLED + #if !defined(DFF_EXCLUDE_MPI) #define DFF_MPI #endif + #if !defined(DFF_EXCLUDE_BLOCKING) #define BLOCKING_MODE #else diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 774fee01..034120ed 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -354,7 +354,6 @@ class dGroup : public ff_farm { int run(ff_node* baseBB, bool skip_init=false) override { buildFarm(reinterpret_cast(baseBB)); - return ff_farm::run(skip_init); } @@ -758,7 +757,7 @@ bool MySet::check_inout(ff_node* node){ } void dGroups::consolidateGroups(){ - for(int i = 0; i < parsedGroups.size(); i++){ + for(size_t i = 0; i < parsedGroups.size(); i++){ const G & g = parsedGroups[i]; if (groups.find(g.name) != groups.end()) switch (this->usedProtocol){ diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 95e12639..6677f5d5 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -11,6 +11,8 @@ #include #include +#include + #include @@ -52,6 +54,8 @@ class dGroups { } const std::string& getRunningGroup() const { return runningGroup; } + + void forceProtocol(Proto p){this->usedProtocol = p;} int run_and_wait_end(ff_node* parent){ @@ -175,6 +179,10 @@ static inline int DFF_Init(int& argc, char**& argv){ dGroups::Instance()->parseConfig(configFile); + if (!groupName.empty()){ + dGroups::Instance()->forceProtocol(Proto::TCP); + } + if (dGroups::Instance()->usedProtocol == Proto::TCP){ if (groupName.empty()){ ff::error("Group not passed as argument!\nUse option --DFF_GName=\"group-name\"\n"); @@ -204,6 +212,10 @@ static inline int DFF_Init(int& argc, char**& argv){ std::cout << "Running group: " << dGroups::Instance()->getRunningGroup() << " on rank: " << myrank << "\n"; } + + // set the name for the printer + ff::cout.setPrefix(dGroups::Instance()->getRunningGroup()); + #endif diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 3911eca1..a695df65 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -78,8 +78,6 @@ class ff_dreceiver: public ff_monode_t { out->sender = sender; out->chid = chid; - //std::cout << "received something from " << sender << " directed to " << chid << std::endl; - ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! return 0; } diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 24058786..8489b897 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -5,7 +5,8 @@ #include #include #include - +#include +#include #include #include @@ -20,6 +21,7 @@ namespace n_fs = std::experimental::filesystem; #endif +enum Proto {TCP = 1 , MPI}; static inline unsigned long getusec() { struct timeval tv; @@ -27,13 +29,20 @@ static inline unsigned long getusec() { return (unsigned long)(tv.tv_sec*1e6+tv.tv_usec); } +Proto usedProtocol; +bool seeAll = false; +std::vector viewGroups; char hostname[100]; std::string configFile(""); std::string executable; -inline std::vector split (const std::string &s, char delim) { +bool toBePrinted(std::string gName){ + return (seeAll || (find(viewGroups.begin(), viewGroups.end(), gName) != viewGroups.end())); +} + +std::vector split (const std::string &s, char delim) { std::vector result; - std::stringstream ss (s); + std::stringstream ss(s); std::string item; while (getline (ss, item, delim)) @@ -44,7 +53,8 @@ inline std::vector split (const std::string &s, char delim) { struct G { std::string name, host, preCmd; - FILE* fd = nullptr; + int fd = 0; + FILE* file = nullptr; template void load( Archive & ar ){ @@ -67,15 +77,21 @@ struct G { } void run(){ - char b[350]; // ssh -t - sprintf(b, " %s %s %s %s --DFF_Config=%s --DFF_GName=%s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : "") , this->preCmd.c_str(), executable.c_str(), configFile.c_str(), this->name.c_str()); + char b[1024]; // ssh -t // trovare MAX ARGV + + sprintf(b, " %s %s %s %s --DFF_Config=%s --DFF_GName=%s %s 2>&1 %s", (isRemote() ? "ssh -t '" : ""), (isRemote() ? host.c_str() : "") , this->preCmd.c_str(), executable.c_str(), configFile.c_str(), this->name.c_str(), toBePrinted(this->name) ? "" : "> /dev/null", (isRemote() ? "'" : "")); std::cout << "Executing the following command: " << b << std::endl; - fd = popen(b, "r"); - - if (fd == NULL) { + file = popen(b, "r"); + fd = fileno(file); + + if (fd == -1) { printf("Failed to run command\n" ); exit(1); } + + int flags = fcntl(fd, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(fd, F_SETFL, flags); } bool isRemote(){return !(!host.compare("127.0.0.1") || !host.compare("localhost") || !host.compare(hostname));} @@ -85,7 +101,7 @@ struct G { bool allTerminated(std::vector& groups){ for (G& g: groups) - if (g.fd != nullptr) + if (g.file != nullptr) return false; return true; } @@ -98,6 +114,19 @@ static inline void usage(char* progname) { } +std::string generateHostFile(std::vector& parsedGroups){ + std::string name = "/tmp/dffHostfile" + std::to_string(getpid()); + + std::ofstream tmpFile(name, std::ofstream::out); + + for (const G& group : parsedGroups) + tmpFile << group.host << std::endl; + + tmpFile.close(); + // return the name of the temporary file just created; remember to remove it after the usage + return name; +} + int main(int argc, char** argv) { if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-help") == 0 || strcmp(argv[0], "-h") == 0){ @@ -108,20 +137,31 @@ int main(int argc, char** argv) { // get the hostname gethostname(hostname, 100); - std::vector viewGroups; - bool seeAll = false; int optind=0; for(int i=1;i 0){ + std::cout << buff; + } else { + int code = pclose(g.file); + if (WEXITSTATUS(code) != 0) + std::cout << "[" << g.name << "][ERR] Report an return code: " << WEXITSTATUS(code) << std::endl; + g.file = nullptr; + } } } - } - std::this_thread::sleep_for(std::chrono::milliseconds(15)); + std::this_thread::sleep_for(std::chrono::milliseconds(15)); + } + std::cout << "Elapsed time: " << (getusec()-(Tstart))/1000 << " ms" << std::endl; } - - std::cout << "Elapsed time: " << (getusec()-(Tstart))/1000 << " ms" << std::endl; + if (usedProtocol == Proto::MPI){ + std::string hostFile = generateHostFile(parsedGroups); + std::cout << "Hostfile: " << hostFile << std::endl; + // invoke mpirun using the just created hostfile + + char command[350]; + + + sprintf(command, "mpirun --hostfile %s %s --DFF_Config=%s", hostFile.c_str(), executable.c_str(), configFile.c_str()); + + FILE *fp; + char buff[1024]; + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to run command\n" ); + exit(1); + } + + /* Read the output a line at a time - output it. */ + while (fgets(buff, sizeof(buff), fp) != NULL) { + std::cout << buff; + } + + pclose(fp); + std::remove(hostFile.c_str()); + } + + return 0; -} \ No newline at end of file +} diff --git a/tests/distributed/test_complete5.cpp b/tests/distributed/test_complete5.cpp index f8bfc701..b16b0d7b 100644 --- a/tests/distributed/test_complete5.cpp +++ b/tests/distributed/test_complete5.cpp @@ -78,7 +78,7 @@ struct Node4: ff_minode_t{ if (processed != ntasks) { abort(); } - std::cout << "RESULT OK\n"; + ff::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 9301bc86..6a3289cb 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -17,7 +17,7 @@ #include #include -#define MANUAL_SERIALIZATION 1 +//#define MANUAL_SERIALIZATION 1 // ------------------------------------------------------ std::mutex mtx; // used only for pretty printing @@ -106,7 +106,7 @@ struct MoNode : ff::ff_monode_t{ void svc_end(){ const std::lock_guard lock(mtx); - std::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << std::endl; + ff::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << ff::endl; } }; @@ -127,7 +127,7 @@ struct MiNode : ff::ff_minode_t{ myassert(in->C[100] == 'a'); if (in->clen>500) myassert(in->C[500] == 'o'); - std::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; + ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; } myassert(in->C[in->clen-1] == 'F'); delete in; @@ -136,7 +136,7 @@ struct MiNode : ff::ff_minode_t{ void svc_end(){ const std::lock_guard lock(mtx); - std::cout << "[MiNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + ff::cout << "[MiNode" << this->get_my_id() << "] Processed Items: " << processedItems << ff::endl; } }; @@ -144,8 +144,8 @@ int main(int argc, char*argv[]){ DFF_Init(argc, argv); - if (argc < 7){ - std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #nw_sx #nw_dx" << std::endl; + if (argc < 8){ + std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_sx #np_dx #nwXp" << std::endl; return -1; } bool check = false; @@ -153,9 +153,9 @@ int main(int argc, char*argv[]){ long bytexItem = atol(argv[2]); int execTimeSource = atoi(argv[3]); int execTimeSink = atoi(argv[4]); - int numWorkerSx = atoi(argv[5]); - int numWorkerDx = atoi(argv[6]); - + int numProcSx = atoi(argv[5]); + int numProcDx = atoi(argv[6]); + int numWorkerXProcess = atoi(argv[7]); char* p=nullptr; if ((p=getenv("CHECK_DATA"))!=nullptr) check=true; printf("chackdata = %s\n", p); @@ -168,40 +168,41 @@ int main(int argc, char*argv[]){ std::vector sxWorkers; std::vector dxWorkers; - for(int i = 0; i < numWorkerSx; i++) - sxWorkers.push_back(new MoNode(ceil((double)items/numWorkerSx), execTimeSource, bytexItem, check)); + for(int i = 0; i < (numProcSx*numWorkerXProcess); i++) + sxWorkers.push_back(new MoNode(ceil((double)items/(numProcSx*numWorkerXProcess)), execTimeSource, bytexItem, check)); - for(int i = 0; i < numWorkerDx; i++) + for(int i = 0; i < (numProcDx*numWorkerXProcess); i++) dxWorkers.push_back(new MiNode(execTimeSink, check)); a2a.add_firstset(sxWorkers); a2a.add_secondset(dxWorkers); - //mainPipe.run_and_wait_end(); - - auto g1 = a2a.createGroup("G1"); - auto g2 = a2a.createGroup("G2"); - + for(int i = 0; i < numProcSx; i++){ + auto& g = a2a.createGroup(std::string("S")+std::to_string(i)); + for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ + #if defined(MANUAL_SERIALIZATION) + g.out <<= packup(sxWorkers[j], [](ExcType* in) -> std::pair {return std::make_pair((char*)in, in->clen+sizeof(ExcType)); }); + #else + g.out << sxWorkers[j]; + #endif + } + } - for(int i = 0; i < numWorkerSx; i++) { -#if defined(MANUAL_SERIALIZATION) - g1.out <<= packup(sxWorkers[i], [](ExcType* in) -> std::pair {return std::make_pair((char*)in, in->clen+sizeof(ExcType)); }); -#else - g1.out << sxWorkers[i]; -#endif - } - for(int i = 0; i < numWorkerDx; i++) { -#if defined(MANUAL_SERIALIZATION) - g2.in <<= packup(dxWorkers[i], [](char* in, size_t len) -> ExcType* { + for(int i = 0; i < numProcDx; i++){ + auto& g = a2a.createGroup(std::string("D")+std::to_string(i)); + for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ + #if defined(MANUAL_SERIALIZATION) + g.in <<= packup(dxWorkers[j], [](char* in, size_t len) -> ExcType* { ExcType* p = new (in) ExcType(true); p->C = in + sizeof(ExcType); p->clen = len - sizeof(ExcType); return p; }); -#else - g2.in << dxWorkers[i]; -#endif - } + #else + g.in << dxWorkers[j]; + #endif + } + } if (mainPipe.run_and_wait_end()<0) { error("running mainPipe\n"); From ed35aae33e46b86a58c1dc5d3941145b06dbf987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 23 Aug 2021 16:37:03 +0200 Subject: [PATCH 071/202] Missing files now added. See previous commit for description --- ff/distributed/ff_dprinter.hpp | 44 +++++++++++++++ tests/distributed/runParametricPerf.sh | 74 ++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 ff/distributed/ff_dprinter.hpp create mode 100755 tests/distributed/runParametricPerf.sh diff --git a/ff/distributed/ff_dprinter.hpp b/ff/distributed/ff_dprinter.hpp new file mode 100644 index 00000000..f9db8d02 --- /dev/null +++ b/ff/distributed/ff_dprinter.hpp @@ -0,0 +1,44 @@ +#ifndef FF_DPRINTER_H +#define FF_DPRINTER_H + +#include +#include + +namespace ff { + class prefixbuf : public std::streambuf + { + std::string prefix; + std::streambuf* sbuf; + bool need_prefix = true; + + int sync() { return this->sbuf->pubsync();} + int overflow(int c) { + if (c != std::char_traits::eof()) { + if (this->need_prefix + && !this->prefix.empty() + && this->prefix.size() != this->sbuf->sputn(&this->prefix[0], this->prefix.size())) { + return std::char_traits::eof(); + } + this->need_prefix = c == '\n'; + } + return this->sbuf->sputc(c); + } + + public: + prefixbuf(std::string const& p, std::streambuf* sbuf) : prefix(std::string("[" + p + "] ")), sbuf(sbuf) {} + void setPrefix(std::string const& p){prefix = std::string("[" + p + "] ");} + }; + + class Printer : private virtual prefixbuf, public std::ostream { + public: + Printer(std::string const& p, std::ostream& out) : prefixbuf(p, out.rdbuf()), std::ios(static_cast(this)), std::ostream(static_cast(this)) {} + void setPrefix(std::string const& p){prefixbuf::setPrefix(p);} + }; + + template + auto& endl(std::basic_ostream& os){return std::endl(os);} + + Printer cout("undefined", std::cout); +} + +#endif \ No newline at end of file diff --git a/tests/distributed/runParametricPerf.sh b/tests/distributed/runParametricPerf.sh new file mode 100755 index 00000000..1d67e989 --- /dev/null +++ b/tests/distributed/runParametricPerf.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +## Configure the parametric perf +ITEMS=1000 +BYTExITEM=1000 +EXECTIMESOURCE=10 +EXECTIMESINK=10 +WORKERSXPROCESS=1 + + + +## this scripts allow to run a solution in the cluster automatically assigning addresses and ports ## + +if [ "$#" -lt 3 ]; then + echo "Illegal number of parameters" + echo "usege: $0 #processesSx #processesDx #startingnode(Optional)" + exit -1 +fi + +start_node=1 + +if [ -z ${4+x} ]; then start_node=1; else start_node=$4; fi + +echo "Running $1 with $2 Sx processes and $3 Dx processes starting from node $start_node" + +rm tmpFilePP.json +connString="" + +for (( i=0; i<$3-1; i++)) +do + connString+="\"D${i}\", " +done + +connString+="\"D$(($3-1))\"" + +echo "{ + \"groups\" : [" >> tmpFilePP.json + +# printing the dx nodes in the configuration file +for (( i=0; i<$2; i++)) +do + echo " { + \"endpoint\" : \"compute$(($start_node+$i)):8000\", + \"name\" : \"S${i}\", + \"OConn\" : [${connString}] + } + ," >> tmpFilePP.json +done + +# printing the dx nodes in the configuration file +for (( i=0; i<$3; i++)) +do + echo " { + \"endpoint\" : \"compute$(($start_node+$2+$i)):8000\", + \"name\" : \"D${i}\" + }" >> tmpFilePP.json + if [[ $i -lt ${3}-1 ]]; then + echo " ," >> tmpFilePP.json + fi +done + +echo " ] +}" >> tmpFilePP.json + + +### parametric_perf Usage +## #items #byteXitem #execTimeSource #execTimeSink #nw_sx #nw_dx +### + +dff_run -V -f tmpFilePP.json $1 $ITEMS $BYTExITEM $EXECTIMESOURCE $EXECTIMESINK $2 $3 $WORKERSXPROCESS + +rm tmpFilePP.json +#exiting justo for testin purpose +exit 0 \ No newline at end of file From 381086339390746e9b7515cb6e6f92075b8b765b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 27 Sep 2021 15:40:57 +0200 Subject: [PATCH 072/202] First proposal set of tools to distribute the farm pattern. Added test_farm1 to test those tools. --- ff/distributed/ff_dadapters.hpp | 205 +++++++++++++++++++++++++++++++ ff/distributed/ff_dgroup.hpp | 18 +-- ff/distributed/ff_wrappers.hpp | 16 ++- tests/distributed/test_farm1.cpp | 90 ++++++++++++++ 4 files changed, 314 insertions(+), 15 deletions(-) create mode 100644 ff/distributed/ff_dadapters.hpp create mode 100644 tests/distributed/test_farm1.cpp diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp new file mode 100644 index 00000000..c347cb6f --- /dev/null +++ b/ff/distributed/ff_dadapters.hpp @@ -0,0 +1,205 @@ +#ifndef ADAPTERS_H +#define ADAPTERS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FARM_GATEWAY -10 + +using namespace ff; +template +struct TaskWrapper { + T* task; + int destination; + TaskWrapper(T* task, int d) : task(task), destination(d) {} +}; + +template +struct ResultWrapper { + T* result; + int source; + ResultWrapper(T* result, int s): result(result), source(s) {} +}; + +template +class SquareBoxEmitter : public ff_monode_t, T> { + T* svc(TaskWrapper* in) { + this->ff_send_out_to(in->task, in->destination); + return this->GO_ON; + } +}; + +template +class SquareBoxCollector : public ff_minode_t> { + ResultWrapper* svc(T* in){ + return new ResultWrapper(in, ff_minode::get_channel_id()); + } +}; + + +template +class EmitterAdapter: public internal_mo_transformer { +private: + int totalWorkers, localWorkers; + int nextDestination; +public: + + typedef Tout T_out; + + EmitterAdapter(ff_node_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + EmitterAdapter(ff_node* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + EmitterAdapter(ff_monode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + EmitterAdapter(ff_minode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + template + EmitterAdapter(ff_comb_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); + } + + void * svc(void* in) { + void* out = n->svc(in); + if (out > FF_TAG_MIN) return out; + + this->forward(out, -1); + + return GO_ON; + } + + bool forward(void* task, int destination){ + if (destination == -1) destination = nextDestination; + + if (destination < localWorkers) ff_send_out_to(task, destination); + else ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkers); + + nextDestination = (nextDestination + 1) % totalWorkers; + return true; + } + + int svc_init() { + if (this->n->isMultiOutput()) { + ff_monode* mo = reinterpret_cast(this->n); + mo->set_running(localWorkers + 1); // the last worker is the forwarder to the remote workers + } + return n->svc_init(); + } + + void svc_end(){n->svc_end();} + + int run(bool skip_init=false) { + return internal_mo_transformer::run(skip_init); + } + + ff::ff_node* getOriginal(){return this->n;} + + static inline bool ff_send_out_to_cbk(void* task, int id, + unsigned long retry, + unsigned long ticks, void* obj) { + return ((EmitterAdapter*)obj)->forward(task, id); + } +}; + + +template +class CollectorAdapter: public internal_mi_transformer { + +private: + int localWorkers, indexFirstLocal; // number of input channels the wrapped node is supposed to have +public: + + CollectorAdapter(ff_node_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + this->n = n; + this->cleanup = cleanup; + } + + CollectorAdapter(ff_node* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + this->n = n; + this->cleanup = cleanup; + } + + CollectorAdapter(ff_minode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + this->n = n; + this->cleanup = cleanup; + } + + CollectorAdapter(ff_monode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + this->n = n; + this->cleanup = cleanup; + } + + template + CollectorAdapter(ff_comb_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + this->n = n; + this->cleanup = cleanup; + } + + void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { + internal_mi_transformer::registerCallback(cb,arg); + } + + int svc_init() { + if (this->n->isMultiOutput()) { + ff_minode* mi = reinterpret_cast(this->n); + mi->set_running(localWorkers + indexFirstLocal); + } + return n->svc_init(); + } + + void svc_end(){this->n->svc_end();} + + void * svc(void* in) { + Tin * task; + ssize_t channel; + + // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from + if (get_channel_id() == 0){ + ResultWrapper * tw = reinterpret_cast*>(in); + task = tw->result; + channel = tw->source; + } else { // the result come from a local worker, just pass it to collector and compute the right worker id + task = reinterpret_cast(in); + channel = get_channel_id() + indexFirstLocal - 1; + } + + + if (this->n->isMultiInput()) { + ff_minode* mi = reinterpret_cast(this->n); + mi->set_input_channelid(channel, true); + } + return n->svc(task); + } + + ff_node* getOriginal(){ return this->n; } +}; + + + + + +#endif \ No newline at end of file diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 034120ed..6c525ce3 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -462,7 +462,7 @@ MySet& MySet::operator<<(ff_node_t* node){ } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else - this->group->out_.insert({node, {new WrapperOUT(node, false), true}}); + this->group->out_.insert({node, {new WrapperOUT(node, 1, false, -1), true}}); return *this; } @@ -481,7 +481,7 @@ MySet& MySet::operator<<(ff_minode_t* node){ } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else { - ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, true), false, true); + ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, 1, true, -1), false, true); this->group->out_.insert({node, {combine, true}}); } @@ -502,7 +502,7 @@ MySet& MySet::operator<<(ff_monode_t* node){ } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); } else - this->group->out_.insert({node, {new WrapperOUT(node, false), true}}); + this->group->out_.insert({node, {new WrapperOUT(node, 1, false, -1), true}}); return *this; } @@ -645,7 +645,7 @@ MySet& MySet::operator<<=(ff_node_t* node){ } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else - this->group->out_.insert({node, {new WrapperOUT(node, true), false}}); + this->group->out_.insert({node, {new WrapperOUT(node, 1, true, -1), false}}); return *this; } @@ -664,7 +664,7 @@ MySet& MySet::operator<<=(std::pair*, Function> n } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); } else - this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, true, nodeFun.second), false}}); + this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, -1, nodeFun.second), false}}); return *this; } @@ -685,7 +685,7 @@ MySet& MySet::operator<<=(ff_minode_t* node){ } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else { - ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, true), false, true); + ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, 1, true, -1), false, true); this->group->out_.insert({node, {combine, false}}); } @@ -706,7 +706,7 @@ MySet& MySet::operator<<=(std::pair*, Function> } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); } else { - ff_comb* combine = new ff_comb(nodeFun.first, new WrapperOUT(new ForwarderNode, true, nodeFun.second), false, true); + ff_comb* combine = new ff_comb(nodeFun.first, new WrapperOUT(new ForwarderNode, 1, true, -1, nodeFun.second), false, true); this->group->out_.insert({nodeFun.first, {combine, false}}); } @@ -727,7 +727,7 @@ MySet& MySet::operator<<=(ff_monode_t* node){ } else this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); } else - this->group->out_.insert({node, {new WrapperOUT(node, true), false}}); + this->group->out_.insert({node, {new WrapperOUT(node, 1, true, -1), false}}); return *this; } @@ -746,7 +746,7 @@ MySet& MySet::operator<<=(std::pair*, Function> } else this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer(), nodeFun.second)}); } else - this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, nodeFun.second), false}}); + this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, -1, nodeFun.second), false}}); return *this; } diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index b7d4340f..bdaca2d8 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -28,10 +28,12 @@ class WrapperIN: public internal_mi_transformer { this->n = n; this->cleanup = cleanup; } + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; } + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup = cleanup; @@ -109,36 +111,38 @@ template class WrapperOUT: public internal_mo_transformer { private: int outchannels; // number of output channels the wrapped node is supposed to have + int defaultDestination; public: typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; - WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), transform_(transform) { + WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -174,7 +178,7 @@ class WrapperOUT: public internal_mo_transformer { void * svc(void* in) { void* out = n->svc(in); if (out > FF_TAG_MIN) return out; - serialize(reinterpret_cast(out), -1); + serialize(reinterpret_cast(out), defaultDestination); return GO_ON; } diff --git a/tests/distributed/test_farm1.cpp b/tests/distributed/test_farm1.cpp new file mode 100644 index 00000000..53655198 --- /dev/null +++ b/tests/distributed/test_farm1.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include + + +using namespace ff; + +#define WORKERS 10 + +struct Source : ff_monode_t{ + int numWorker; + Source(int numWorker) : numWorker(numWorker) {} + + std::string* svc(std::string* in){ + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task generated for " + std::to_string(i) + " passed to"), i); + return EOS; + } +}; + +struct Worker : ff_node_t{ + int numWorker; + Worker(int numWorker) : numWorker(numWorker) {} + + std::string* svc(std::string * in){ + std::string * output = new std::string(*in + " " + std::to_string(numWorker)); + delete in; + return output; + } +}; + +struct Sink : ff_minode_t{ + std::string* svc(std::string* in){ + std::cout << *in << " received by Collector from " << get_channel_id() << std::endl; + delete in; + return this->GO_ON; + } +}; + + +int main(int argc, char*argv[]){ + + if (argc != 2){ + std::cerr << "Execute with the index of process!" << std::endl; + return 1; + } + + if (atoi(argv[1]) == 0){ + ff_farm f; + f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, WORKERS/2, true)); + std::vector workers; + for(int i = 0; i < WORKERS/2; i++) + workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); + workers.push_back(new WrapperOUT(new SquareBoxEmitter(), 1, true)); + f.add_workers(workers); + f.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001))); + f.run_and_wait_end(); + + } else { + ff_farm f; + std::map routingTable; + std::vector workers; + int j = 1; + workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); + routingTable.emplace(FARM_GATEWAY, 0); + for(int i = WORKERS / 2; i < WORKERS; i++){ + workers.push_back(new WrapperIN(new Worker(i), 1, true)); + routingTable.emplace(i, j++); + } + + f.add_workers(workers); + f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); + f.add_collector(new CollectorAdapter(new Sink, 5, 5, true), true); + + f.run_and_wait_end(); + } + /* + ff_farm farm; + farm.add_emitter(new Source(WORKERS)); + farm.add_collector(new Sink, true); + std::vector workers; + for(int i = 0; i < WORKERS; i++) + workers.push_back(new Worker(i)); + farm.add_workers(workers); + + farm.run_and_wait_end(); + */ +} \ No newline at end of file From 78b464d7b77dcf798def30321a0573119ee8e06d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 30 Sep 2021 12:20:28 +0200 Subject: [PATCH 073/202] Added another farm example using also WrapperINOUT, hence some workers are deployed alone (i.e. withouth emitter or collector). Added defaultDestination parameter also to WrapperINOUT. --- ff/distributed/ff_dgroup.hpp | 48 ++++++++--------- ff/distributed/ff_wrappers.hpp | 11 ++-- tests/distributed/Makefile | 7 ++- tests/distributed/test_Farm2.cpp | 92 ++++++++++++++++++++++++++++++++ tests/distributed/test_farm1.cpp | 11 ---- 5 files changed, 127 insertions(+), 42 deletions(-) create mode 100644 tests/distributed/test_Farm2.cpp diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 6c525ce3..9359cf76 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -395,7 +395,7 @@ MySet& MySet::operator<<(ff_node_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else this->group->in_.insert({node, {new WrapperIN(node, false), true}}); @@ -419,7 +419,7 @@ MySet& MySet::operator<<(ff_minode_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); } else this->group->in_.insert({node, {new WrapperIN(node, false), true}}); @@ -439,7 +439,7 @@ MySet& MySet::operator<<(ff_monode_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else { ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true, false); this->group->in_.insert({node, {combine, true}}); @@ -460,7 +460,7 @@ MySet& MySet::operator<<(ff_node_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else this->group->out_.insert({node, {new WrapperOUT(node, 1, false, -1), true}}); @@ -479,7 +479,7 @@ MySet& MySet::operator<<(ff_minode_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else { ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, 1, true, -1), false, true); this->group->out_.insert({node, {combine, true}}); @@ -500,7 +500,7 @@ MySet& MySet::operator<<(ff_monode_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); } else this->group->out_.insert({node, {new WrapperOUT(node, 1, false, -1), true}}); @@ -520,7 +520,7 @@ MySet& MySet::operator<<=(ff_node_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else this->group->in_.insert({node, {new WrapperIN(node, true), false}}); @@ -536,10 +536,10 @@ MySet& MySet::operator<<=(std::pair*, Function> nod if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second)}); } } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, true, nodeFun.second), false}}); @@ -559,7 +559,7 @@ MySet& MySet::operator<<=(ff_minode_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); } else this->group->in_.insert({node, {new WrapperIN(node, true), false}}); @@ -576,10 +576,10 @@ MySet& MySet::operator<<=(std::pair*, Function> n if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second)}); } } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); } else this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, 1, true, nodeFun.second), false}}); @@ -599,7 +599,7 @@ MySet& MySet::operator<<=(ff_monode_t* node){ this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else { ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true); @@ -618,10 +618,10 @@ MySet& MySet::operator<<=(std::pair*, Function> n if (!handle.empty()){ // the node is edge also in its output if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second)}); } } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); } else { ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true, nodeFun.second), nodeFun.first, true); @@ -643,7 +643,7 @@ MySet& MySet::operator<<=(ff_node_t* node){ this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else this->group->out_.insert({node, {new WrapperOUT(node, 1, true, -1), false}}); @@ -659,10 +659,10 @@ MySet& MySet::operator<<=(std::pair*, Function> n if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, nullptr, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, -1, nullptr, nodeFun.second)}); } } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); } else this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, -1, nodeFun.second), false}}); @@ -683,7 +683,7 @@ MySet& MySet::operator<<=(ff_minode_t* node){ this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); } else { ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, 1, true, -1), false, true); this->group->out_.insert({node, {combine, false}}); @@ -701,10 +701,10 @@ MySet& MySet::operator<<=(std::pair*, Function> if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, nullptr, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, -1, nullptr, nodeFun.second)}); } } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); } else { ff_comb* combine = new ff_comb(nodeFun.first, new WrapperOUT(new ForwarderNode, 1, true, -1, nodeFun.second), false, true); this->group->out_.insert({nodeFun.first, {combine, false}}); @@ -725,7 +725,7 @@ MySet& MySet::operator<<=(ff_monode_t* node){ this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); } } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); + this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); } else this->group->out_.insert({node, {new WrapperOUT(node, 1, true, -1), false}}); @@ -741,10 +741,10 @@ MySet& MySet::operator<<=(std::pair*, Function> if (!handle.empty()){ // the node is edge also in its input if (handle.mapped().second){ if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, nullptr, nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, -1, nullptr, nodeFun.second)}); } } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer(), nodeFun.second)}); + this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer(), nodeFun.second)}); } else this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, -1, nodeFun.second), false}}); diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index bdaca2d8..6a637e09 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -218,32 +218,33 @@ class WrapperINOUT: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have int outchannels; // number of output channels the wrapped node is supposed to have + int defaultDestination; public: typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -323,7 +324,7 @@ class WrapperINOUT: public internal_mi_transformer { void* out = n->svc(deserialize(in)); if (out > FF_TAG_MIN) return out; - serialize(reinterpret_cast(out), -1); + serialize(reinterpret_cast(out), defaultDestination); return GO_ON; } diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index 8728b709..9f8e0fc0 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -34,16 +34,19 @@ ifdef EXCLUDE_BLOCKING endif ifdef EXCLUDE_MPI CXXFLAGS += -DDFF_EXCLUDE_MPI +else + INCS += -I/usr/local/Cellar/open-mpi/4.1.1_2/include + CXXFLAGS += -L/usr/local/opt/libevent/lib -L/usr/local/Cellar/open-mpi/4.1.1_2/lib -lmpi endif ifdef FF_HOME INCS += -I$(FF_HOME) else - INCS += -I ~/fastflow + INCS += -I ~/SPM/fastflow endif ifdef CEREAL_HOME INCS += -I$(CEREAL_HOME) else - INCS += -I ~/cereal + INCS += -I ~/SPM/cereal endif diff --git a/tests/distributed/test_Farm2.cpp b/tests/distributed/test_Farm2.cpp new file mode 100644 index 00000000..8e4a6d1d --- /dev/null +++ b/tests/distributed/test_Farm2.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + + +using namespace ff; + +#define WORKERS 9 + +struct Source : ff_monode_t{ + int numWorker; + Source(int numWorker) : numWorker(numWorker) {} + + std::string* svc(std::string* in){ + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task generated for " + std::to_string(i) + " passed to"), i); + return EOS; + } +}; + +struct Worker : ff_node_t{ + int numWorker; + Worker(int numWorker) : numWorker(numWorker) {} + + std::string* svc(std::string * in){ + std::string * output = new std::string(*in + " " + std::to_string(numWorker)); + delete in; + return output; + } +}; + +struct Sink : ff_minode_t{ + std::string* svc(std::string* in){ + std::cout << *in << " received by Collector from " << get_channel_id() << std::endl; + delete in; + return this->GO_ON; + } +}; + + +int main(int argc, char*argv[]){ + + if (argc != 2){ + std::cerr << "Execute with the index of process!" << std::endl; + return 1; + } + + if (atoi(argv[1]) == 0){ + ff_farm f; + f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, 3, true)); + std::vector workers; + for(int i = 0; i < 3; i++) + workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); + workers.push_back(new WrapperOUT(new SquareBoxEmitter(), 1, true)); + f.add_workers(workers); + f.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001), ff_endpoint("127.0.0.1", 8002)})); + f.run_and_wait_end(); + + } else if (atoi(argv[1]) == 1) { + ff_farm f; + std::map routingTable; + std::vector workers; + int j = 0; + for(int i = 3; i < 6; i++){ + workers.push_back(new WrapperINOUT(new Worker(i), 1, true, FARM_GATEWAY)); + routingTable.emplace(i, j++); + } + f.add_workers(workers); + f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, routingTable)); + f.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001))); + f.run_and_wait_end(); + }else { + ff_farm f; + std::map routingTable; + std::vector workers; + int j = 1; + workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); + routingTable.emplace(FARM_GATEWAY, 0); + for(int i = 6; i < WORKERS; i++){ + workers.push_back(new WrapperIN(new Worker(i), 1, true)); + routingTable.emplace(i, j++); + } + + f.add_workers(workers); + f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); + f.add_collector(new CollectorAdapter(new Sink, 6, 3, true), true); + + f.run_and_wait_end(); + } +} \ No newline at end of file diff --git a/tests/distributed/test_farm1.cpp b/tests/distributed/test_farm1.cpp index 53655198..b31ba5ef 100644 --- a/tests/distributed/test_farm1.cpp +++ b/tests/distributed/test_farm1.cpp @@ -76,15 +76,4 @@ int main(int argc, char*argv[]){ f.run_and_wait_end(); } - /* - ff_farm farm; - farm.add_emitter(new Source(WORKERS)); - farm.add_collector(new Sink, true); - std::vector workers; - for(int i = 0; i < WORKERS; i++) - workers.push_back(new Worker(i)); - farm.add_workers(workers); - - farm.run_and_wait_end(); - */ } \ No newline at end of file From 0f805cc9b8583dcf046f9d18fc69fd6b2eea42f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 18 Oct 2021 11:18:26 +0200 Subject: [PATCH 074/202] Added first manual example of a distributed a2a horizontal splitted --- ff/distributed/ff_dadapters.hpp | 98 ++++++++++++++---------- ff/distributed/ff_dgroup.hpp | 44 +++++++++++ ff/distributed/ff_dreceiver.hpp | 3 +- ff/distributed/ff_network.hpp | 10 +++ ff/distributed/ff_wrappers.hpp | 122 ++++++++++++++++-------------- ff/lb.hpp | 8 ++ ff/multinode.hpp | 7 ++ ff/node.hpp | 8 ++ tests/distributed/test_Farm2.cpp | 27 +++++-- tests/distributed/test_a2a_h1.cpp | 66 ++++++++++++++++ tests/distributed/test_farm1.cpp | 13 ++-- 11 files changed, 296 insertions(+), 110 deletions(-) create mode 100644 tests/distributed/test_a2a_h1.cpp diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index c347cb6f..ac6d3a47 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -28,56 +28,67 @@ struct ResultWrapper { }; template -class SquareBoxEmitter : public ff_monode_t, T> { - T* svc(TaskWrapper* in) { - this->ff_send_out_to(in->task, in->destination); - return this->GO_ON; +class SquareBoxEmitter : public ff_minode_t, SMmessage_t> { + std::vector sources; +public: + SquareBoxEmitter(const std::vector localSources) : sources(localSources) {} + + SMmessage_t* svc(TaskWrapper* in) { + int chId = ff_minode::get_channel_id(); + SMmessage_t* o = new SMmessage_t(in->task, chId < sources.size() ? sources[chId] : -1 , -100 - in->destination); + delete in; + return o; } }; template -class SquareBoxCollector : public ff_minode_t> { - ResultWrapper* svc(T* in){ - return new ResultWrapper(in, ff_minode::get_channel_id()); +class SquareBoxCollector : public ff_monode_t> { + std::unordered_map destinations; +public: + SquareBoxCollector(const std::unordered_map localDestinations) : destinations(localDestinations) {} + + ResultWrapper* svc(SMmessage_t* in){ + this->ff_send_out_to(new ResultWrapper(reinterpret_cast(in->task), in->sender), destinations[in->dst]); + delete in; return this->GO_ON; } }; - template class EmitterAdapter: public internal_mo_transformer { private: - int totalWorkers, localWorkers; + int totalWorkers; + std::unordered_map localWorkersMap; int nextDestination; public: typedef Tout T_out; - EmitterAdapter(ff_node_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_node_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_node* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_node* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_monode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_monode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_minode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_minode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - EmitterAdapter(ff_comb_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_comb_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -93,19 +104,27 @@ class EmitterAdapter: public internal_mo_transformer { } bool forward(void* task, int destination){ - if (destination == -1) destination = nextDestination; - - if (destination < localWorkers) ff_send_out_to(task, destination); - else ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkers); + if (destination == -1) { + destination = nextDestination; + nextDestination = (nextDestination + 1) % totalWorkers; + } - nextDestination = (nextDestination + 1) % totalWorkers; + + + auto pyshicalDestination = localWorkersMap.find(destination); + if (pyshicalDestination != localWorkersMap.end()) { + ff_send_out_to(task, pyshicalDestination->second); + } else { + ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkersMap.size()); + } + return true; } int svc_init() { if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); - mo->set_running(localWorkers + 1); // the last worker is the forwarder to the remote workers + mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers } return n->svc_init(); } @@ -130,31 +149,31 @@ template class CollectorAdapter: public internal_mi_transformer { private: - int localWorkers, indexFirstLocal; // number of input channels the wrapped node is supposed to have + std::vector localWorkers; public: - CollectorAdapter(ff_node_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_node_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_node* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_node* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_minode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_minode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_monode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_monode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } template - CollectorAdapter(ff_comb_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_comb_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } @@ -164,9 +183,9 @@ class CollectorAdapter: public internal_mi_transformer { } int svc_init() { - if (this->n->isMultiOutput()) { + if (this->n->isMultiInput()) { ////???????? MASSIMO??? ff_minode* mi = reinterpret_cast(this->n); - mi->set_running(localWorkers + indexFirstLocal); + mi->set_running(localWorkers.size() + 1); } return n->svc_init(); } @@ -177,29 +196,26 @@ class CollectorAdapter: public internal_mi_transformer { Tin * task; ssize_t channel; - // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from - if (get_channel_id() == 0){ - ResultWrapper * tw = reinterpret_cast*>(in); + // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from + if (get_channel_id() == localWorkers.size()){ + ResultWrapper * tw = reinterpret_cast*>(in); task = tw->result; channel = tw->source; - } else { // the result come from a local worker, just pass it to collector and compute the right worker id - task = reinterpret_cast(in); - channel = get_channel_id() + indexFirstLocal - 1; - } - + } else { // the result come from a local worker, just pass it to collector and compute the right worker id + task = reinterpret_cast(in); + channel = localWorkers.at(get_channel_id()); + } + // update the input channel id field only if the wrapped node is a multi input if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channel, true); - } + } + return n->svc(task); } ff_node* getOriginal(){ return this->n; } }; - - - - #endif \ No newline at end of file diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 9359cf76..b108b0b7 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -183,6 +183,39 @@ class dGroup : public ff_farm { return false; } + static int getOutputIndexOfNode(ff_node*bb, ff_node* wrapper, ff_node* original = nullptr){ + if (bb->isAll2All()){ + ff_a2a* a2a = (ff_a2a*) bb; + int index = 0; + for (ff_node* n : a2a->getFirstSet()){ + ff::svector outputs; n->get_out_nodes(outputs); + for (const ff_node* output : outputs){ + if (output == wrapper || output == original) + return index; + index++; + } + } + + index = 0; + for (ff_node* n : a2a->getSecondSet()){ + ff::svector outputs; n->get_out_nodes(outputs); + for (ff_node* output : outputs) + if (output == wrapper || output == original) + return index; + else index++; + } + } + + int index = 0; + ff::svector outputs; bb->get_out_nodes(outputs); + for (ff_node* output : outputs) + if (output == wrapper || output == original) + return index; + else index++; + + return 0; + } + static int getInputIndexOfNode(ff_node* bb, ff_node* wrapper, ff_node* original){ if (bb->isAll2All()){ ff_a2a* a2a = (ff_a2a*) bb; @@ -296,6 +329,7 @@ class dGroup : public ff_farm { if (this->getNWorkers() == 0) return -1; + // create receiver Proto currentProto = dGroups::Instance()->usedProtocol; @@ -319,6 +353,16 @@ class dGroup : public ff_farm { } // create sender if (!isSink()){ + + // set the senderID to each wrapperOUT (or WrapperINOUT), meaningful only when the the group is not a sink + for (ff_node* bb : this->getWorkers()){ + ff::svector outputs; bb->get_out_nodes(outputs); + for (ff_node* output : outputs) + if (dynamic_cast(output) != NULL) + dynamic_cast(output)->setMyId(getOutputIndexOfNode(parentStructure, output)); + else + ff::cout << "Else branch dynamic cast" << ff::endl; + } if (currentProto == Proto::TCP){ if (onDemandSender) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index a695df65..72993434 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -78,6 +78,8 @@ class ff_dreceiver: public ff_monode_t { out->sender = sender; out->chid = chid; + std::cout << "Receiver recevied something!\n"; + ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! return 0; } @@ -285,7 +287,6 @@ class ff_dreceiverOD: public ff_dreceiver { } } - return 0; } diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index b362a0e5..0e669ead 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -62,6 +62,15 @@ class dataBuffer: public std::stringbuf { bool cleanup = false; }; +struct SMmessage_t { + SMmessage_t(){} + SMmessage_t(void* t, int s, int d) : task(t), sender(s), dst(d) {} + void * task; + int sender; + int dst; +}; + + struct message_t { message_t(){} message_t(char *rd, size_t size, bool cleanup=true) : data(rd,size,cleanup){} @@ -87,6 +96,7 @@ struct ff_endpoint { struct FF_Exception: public std::runtime_error {FF_Exception(const char* err) throw() : std::runtime_error(err) {}}; + ssize_t readn(int fd, char *ptr, size_t n) { size_t nleft = n; ssize_t nread; diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 6a637e09..2cf427ab 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -12,6 +12,19 @@ using namespace ff; +template +struct DummyNode : public ff_node_t { + Tout* svc(Tin* i){} +}; + +class Wrapper { +protected: + int myID; // used to populate sender field of the header + +public: + Wrapper() : myID(-1){} + void setMyId(int id){myID = id;} +}; /* Wrapper IN class @@ -24,43 +37,27 @@ class WrapperIN: public internal_mi_transformer { public: using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} - WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} - WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} template - WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } - Tin* deserialize(void* buffer) { - message_t* msg = (message_t*)buffer; + Tin* deserialize(dataBuffer& dbuffer) { Tin* data = nullptr; if constexpr (Serialization) { // deserialize the buffer into a heap allocated buffer - std::istream iss(&msg->data); + std::istream iss(&dbuffer); cereal::PortableBinaryInputArchive iarchive(iss); data = new Tin; @@ -69,11 +66,10 @@ class WrapperIN: public internal_mi_transformer { } else { // deserialize the buffer into a heap allocated buffer - msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - data = finalizer_(msg->data.getPtr(), msg->data.getLen()); + dbuffer.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper + data = finalizer_(dbuffer.getPtr(), dbuffer.getLen()); } - delete msg; return data; } @@ -93,8 +89,10 @@ class WrapperIN: public internal_mi_transformer { if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); - } - return n->svc(deserialize(in)); + } + Tin* inputData = deserialize(msg->data); + delete msg; + return n->svc(inputData); } ff_node* getOriginal(){ return this->n; } @@ -103,12 +101,22 @@ class WrapperIN: public internal_mi_transformer { ff_deserialize_F_t getFinalizer() {return this->finalizer_;} }; +template +struct WrapperINCustom : public WrapperIN { + WrapperINCustom() : WrapperIN(new DummyNode(), 1, true){} + + void * svc(void* in) { + message_t* msg = (message_t*)in; + return new SMmessage_t(this->deserialize(msg->data), msg->sender, msg->chid); + } +}; + /* Wrapper OUT class */ template -class WrapperOUT: public internal_mo_transformer { +class WrapperOUT: public Wrapper, public internal_mo_transformer { private: int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; @@ -117,34 +125,24 @@ class WrapperOUT: public internal_mo_transformer { typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; - WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; + WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + registerCallback(ff_send_out_to_cbk, this); //maybe useless?? } - WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); + WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + registerCallback(ff_send_out_to_cbk, this); // maybe useless?? } - WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; + WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { registerCallback(ff_send_out_to_cbk, this); } template - WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; + WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { registerCallback(ff_send_out_to_cbk, this); } @@ -156,7 +154,7 @@ class WrapperOUT: public internal_mo_transformer { if constexpr (Serialization){ msg = new message_t; assert(msg); - msg->sender = this->n->get_my_id(); + msg->sender = this->myID; msg->chid = id; std::ostream oss(&msg->data); cereal::PortableBinaryOutputArchive oarchive(oss); @@ -167,7 +165,7 @@ class WrapperOUT: public internal_mo_transformer { msg = new message_t(raw_data.first, raw_data.second, true); assert(msg); - msg->sender = this->n->get_my_id(); + msg->sender = this->myID; msg->chid = id; } @@ -187,6 +185,7 @@ class WrapperOUT: public internal_mo_transformer { ff_monode* mo = reinterpret_cast(this->n); mo->set_running(outchannels); } + return n->svc_init(); } @@ -208,43 +207,56 @@ class WrapperOUT: public internal_mo_transformer { } }; +template +struct WrapperOUTCustom : public WrapperOUT { + WrapperOUTCustom() : WrapperOUT(new DummyNode(), 1, true){} + + void* svc(void* in){ + SMmessage_t * input = reinterpret_cast(in); + Wrapper::myID = input->sender; + this->serialize(reinterpret_cast(input->task), input->dst); + return this->GO_ON; + } +}; + /* Wrapper INOUT class */ template -class WrapperINOUT: public internal_mi_transformer { +class WrapperINOUT: public Wrapper, public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; + public: typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -286,7 +298,7 @@ class WrapperINOUT: public internal_mi_transformer { if constexpr (SerializationOUT){ msg = new message_t; assert(msg); - msg->sender = this->get_my_id(); + msg->sender = this->myID; msg->chid = id; std::ostream oss(&msg->data); cereal::PortableBinaryOutputArchive oarchive(oss); @@ -297,7 +309,7 @@ class WrapperINOUT: public internal_mi_transformer { msg = new message_t(raw_data.first, raw_data.second, true); assert(msg); - msg->sender = this->get_my_id(); + msg->sender = this->myID; msg->chid = id; } diff --git a/ff/lb.hpp b/ff/lb.hpp index ebc63162..31838aab 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -889,7 +889,11 @@ class ff_loadbalancer: public ff_thread { int neos = filter?filter->neos:1; do { +#ifdef DFF_ENABLED + if (!skipallpop && inpresent){ +#else if (inpresent) { +#endif if (!skipfirstpop) pop(&task); else skipfirstpop=false; @@ -1471,6 +1475,10 @@ class ff_loadbalancer: public ff_thread { bool blocking_in; bool blocking_out; +#ifdef DFF_ENABLED + bool skipallpop; +#endif + #if defined(TRACE_FASTFLOW) unsigned long taskcnt; ticks lostpushticks; diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 7361db54..5a5dfb74 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -650,6 +650,13 @@ class ff_monode: public ff_node { ff_node::skipfirstpop(sk); } +#ifdef DFF_ENABLED + inline void skipallpop(bool sk) { + lb->skipallpop = sk; + ff_node::skipallpop(sk); + } +#endif + /** * \brief Provides the next channel id that will be selected for sending out the task * diff --git a/ff/node.hpp b/ff/node.hpp index 1bebfd2d..8e0bb4a3 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -508,6 +508,10 @@ class ff_node { bool myoutbuffer; bool myinbuffer; bool skip1pop; +#ifdef DFF_ENABLED + bool _skipallpop; +#endif + bool in_active; // allows to disable/enable input tasks receiving bool my_own_thread; @@ -637,6 +641,10 @@ class ff_node { */ virtual inline void skipfirstpop(bool sk) { skip1pop=sk;} +#ifdef DFF_ENABLED + virtual inline void skipallpop(bool sk) {_skipallpop = sk;} +#endif + /** * \brief Gets the status of spontaneous start * diff --git a/tests/distributed/test_Farm2.cpp b/tests/distributed/test_Farm2.cpp index 8e4a6d1d..8cfb01c3 100644 --- a/tests/distributed/test_Farm2.cpp +++ b/tests/distributed/test_Farm2.cpp @@ -22,13 +22,19 @@ struct Source : ff_monode_t{ struct Worker : ff_node_t{ int numWorker; + int tasks = 0; Worker(int numWorker) : numWorker(numWorker) {} std::string* svc(std::string * in){ std::string * output = new std::string(*in + " " + std::to_string(numWorker)); delete in; + tasks++; return output; } + + void svc_end(){ + std::cout << "Worker " << numWorker << " processed " << tasks << "tasks\n"; + } }; struct Sink : ff_minode_t{ @@ -49,11 +55,14 @@ int main(int argc, char*argv[]){ if (atoi(argv[1]) == 0){ ff_farm f; - f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, 3, true)); + f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, {{0,0}, {1,1}, {2,2}}, true)); std::vector workers; - for(int i = 0; i < 3; i++) + for(int i = 0; i < 3; i++){ workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); - workers.push_back(new WrapperOUT(new SquareBoxEmitter(), 1, true)); + //dynamic_cast(workers.back())->setMyId(i); + } + workers.push_back(new WrapperOUT(new SquareBoxEmitter({0}), 1, true)); + f.add_workers(workers); f.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001), ff_endpoint("127.0.0.1", 8002)})); f.run_and_wait_end(); @@ -65,6 +74,7 @@ int main(int argc, char*argv[]){ int j = 0; for(int i = 3; i < 6; i++){ workers.push_back(new WrapperINOUT(new Worker(i), 1, true, FARM_GATEWAY)); + //dynamic_cast(workers.back())->setMyId(i); routingTable.emplace(i, j++); } f.add_workers(workers); @@ -75,17 +85,18 @@ int main(int argc, char*argv[]){ ff_farm f; std::map routingTable; std::vector workers; - int j = 1; - workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); - routingTable.emplace(FARM_GATEWAY, 0); + int j = 0; + + routingTable.emplace(FARM_GATEWAY, 3); for(int i = 6; i < WORKERS; i++){ workers.push_back(new WrapperIN(new Worker(i), 1, true)); routingTable.emplace(i, j++); } + workers.push_back(new WrapperIN(new SquareBoxCollector({std::make_pair(0,0)}), 1, true)); f.add_workers(workers); - f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); - f.add_collector(new CollectorAdapter(new Sink, 6, 3, true), true); + f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 2, routingTable)); + f.add_collector(new CollectorAdapter(new Sink, {6, 7, 8}, true), true); f.run_and_wait_end(); } diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp new file mode 100644 index 00000000..e73253c5 --- /dev/null +++ b/tests/distributed/test_a2a_h1.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct Source : ff_monode_t{ + int numWorker, generatorID; + Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + std::string* svc(std::string* in){ + std::cout << "Source starting generating tasks!" << std::endl; + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + + std::cout << "Source generated all task sending now EOS!" << std::endl; + return EOS; + } +}; + + +struct Sink : ff_minode_t{ + int sinkID; + Sink(int id): sinkID(id) {} + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + + if (argc != 2){ + std::cerr << "Execute with the index of process!" << std::endl; + return 1; + } + + ff_farm gFarm; + ff_a2a a2a; + if (atoi(argv[1]) == 0){ + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, {{-100, 1}})); + gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8002))); + + auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); ea->skipallpop(true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); + a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); + + } else { + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, {{-101, 1}})); + gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001))); + + auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); ea->skipallpop(true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); + a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); + + } + gFarm.add_workers({&a2a}); + gFarm.run_and_wait_end(); +} \ No newline at end of file diff --git a/tests/distributed/test_farm1.cpp b/tests/distributed/test_farm1.cpp index b31ba5ef..3ca10e3a 100644 --- a/tests/distributed/test_farm1.cpp +++ b/tests/distributed/test_farm1.cpp @@ -9,6 +9,8 @@ using namespace ff; #define WORKERS 10 +std::mutex mtx; + struct Source : ff_monode_t{ int numWorker; Source(int numWorker) : numWorker(numWorker) {} @@ -33,6 +35,7 @@ struct Worker : ff_node_t{ struct Sink : ff_minode_t{ std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); std::cout << *in << " received by Collector from " << get_channel_id() << std::endl; delete in; return this->GO_ON; @@ -49,7 +52,7 @@ int main(int argc, char*argv[]){ if (atoi(argv[1]) == 0){ ff_farm f; - f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, WORKERS/2, true)); + f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, {{0,0}, {1,1}, {2,2}, {3,3}, {4,4}}, true)); std::vector workers; for(int i = 0; i < WORKERS/2; i++) workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); @@ -62,17 +65,17 @@ int main(int argc, char*argv[]){ ff_farm f; std::map routingTable; std::vector workers; - int j = 1; - workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); - routingTable.emplace(FARM_GATEWAY, 0); + int j = 0; + routingTable.emplace(FARM_GATEWAY, 5); for(int i = WORKERS / 2; i < WORKERS; i++){ workers.push_back(new WrapperIN(new Worker(i), 1, true)); routingTable.emplace(i, j++); } + workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); f.add_workers(workers); f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); - f.add_collector(new CollectorAdapter(new Sink, 5, 5, true), true); + f.add_collector(new CollectorAdapter(new Sink, {5,6,7,8,9}, true), true); f.run_and_wait_end(); } From e5d9ab9802f59c18e4f0fd4564deb6cac2de16db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 28 Oct 2021 11:51:09 +0200 Subject: [PATCH 075/202] Temporary commit. Not working solutions --- ff/distributed/ff_dgroup.hpp | 4 +- ff/distributed/ff_dreceiver.hpp | 66 +++++++++++++++++++++++++-------- ff/distributed/ff_network.hpp | 7 +++- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index b108b0b7..b20280ee 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -336,9 +336,9 @@ class dGroup : public ff_farm { if (!isSource()){ if (currentProto == Proto::TCP){ if (onDemandReceiver) - this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! + this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, 0, buildRoutingTable(level1BB))); // set right parameters HERE!! else - this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); + this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, 0, buildRoutingTable(level1BB))); } #ifdef DFF_MPI diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 72993434..d89a9ddf 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -21,12 +21,17 @@ using namespace ff; class ff_dreceiver: public ff_monode_t { protected: - int sendRoutingTable(int sck){ + int sendRoutingTable(int sck, ConnectionType type){ dataBuffer buff; std::ostream oss(&buff); cereal::PortableBinaryOutputArchive oarchive(oss); std::vector reachableDestinations; - for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); + for(auto const& p : this->routingTable){ + if (type == ConnectionType::EXTERNAL && p.first > 0) + reachableDestinations.push_back(p.first); + else if (type == ConnectionType::INTERNAL && p.first <= 100) + reachableDestinations.push_back(p.first); + } oarchive << reachableDestinations; @@ -43,6 +48,37 @@ class ff_dreceiver: public ff_monode_t { return 0; } + int handshakeHandler(int sck){ + // ricevo l'handshake e mi salvo che tipo di connessione è + ConnectionType type; + + struct iovec iov; iov.iov_base = &type; iov.iov_len = sizeof(type); + switch (readvn(sck, &iov, 1)) { + case -1: error("Error reading from socket\n"); // fatal error + case 0: return -1; // connection close + } + + type = (ConnectionType) ntohl(type); + + // save the connection type locally + connectionsTypes[sck] = type; + + return this->sendRoutingTable(sck, type); + } + + void registerEOS(int sck){ + switch(connectionsTypes[sck]){ + case ConnectionType::EXTERNAL: + if (Eneos++ == Einput_channels) + for(auto& c : routingTable) if (c.first > 0) ff_send_out_to(this->EOS, c.second); + break; + case ConnectionType::INTERNAL: + if (Ineos++ == Iinput_channels) + for(auto & c : routingTable) if (c.first <= -100) ff_send_out(this->EOS, c.second); + break; + } + } + virtual int handleRequest(int sck){ int sender; int chid; @@ -78,19 +114,18 @@ class ff_dreceiver: public ff_monode_t { out->sender = sender; out->chid = chid; - std::cout << "Receiver recevied something!\n"; - ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! return 0; } - neos++; // increment the eos received + registerEOS(sck); + return -1; } public: - ff_dreceiver(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) - : input_channels(input_channels), acceptAddr(acceptAddr), routingTable(routingTable), coreid(coreid) {} + ff_dreceiver(ff_endpoint acceptAddr, size_t Einput_channels, size_t Iinput_channels = 0, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : Einput_channels(Einput_channels), Iinput_channels(Iinput_channels), acceptAddr(acceptAddr), routingTable(routingTable), coreid(coreid) {} int svc_init() { if (coreid!=-1) @@ -167,7 +202,7 @@ class ff_dreceiver: public ff_monode_t { // hold the greater descriptor int fdmax = this->listen_sck; - while(neos < input_channels){ + while((Ineos + Eneos) < (Einput_channels + Iinput_channels)){ // copy the master set to the temporary tmpset = set; @@ -189,7 +224,7 @@ class ff_dreceiver: public ff_monode_t { FD_SET(connfd, &set); if(connfd > fdmax) fdmax = connfd; - this->sendRoutingTable(connfd); // here i should check the result of the call! and handle possible errors! + this->handshakeHandler(connfd); } continue; } @@ -218,16 +253,16 @@ class ff_dreceiver: public ff_monode_t { } } - /* In theory i should never return because of the while true. In our first example this is necessary */ return this->EOS; } protected: - size_t neos = 0; - size_t input_channels; + size_t Eneos = 0, Ineos = 0; + size_t Einput_channels, Iinput_channels; int listen_sck; ff_endpoint acceptAddr; std::map routingTable; + std::map connectionsTypes; int last_receive_fd = -1; int coreid; }; @@ -290,13 +325,14 @@ class ff_dreceiverOD: public ff_dreceiver { return 0; } - neos++; // increment the eos received + registerEOS(sck); + return -1; } public: - ff_dreceiverOD(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) - : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid) {} + ff_dreceiverOD(ff_endpoint acceptAddr, size_t Einput_channels, size_t Iinput_channels = 0, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : ff_dreceiver(acceptAddr, Einput_channels, Iinput_channels, routingTable, coreid) {} private: ack_t ACK; diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 0e669ead..95ca1b1f 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -84,13 +84,16 @@ struct ack_t { char ack = 'A'; }; +enum ConnectionType : int {EXTERNAL, INTERNAL}; + struct ff_endpoint { ff_endpoint(){} - ff_endpoint(std::string addr, int port) : address(addr), port(port) {} - ff_endpoint(int rank) : port(rank) {} + ff_endpoint(std::string addr, int port, ConnectionType t = EXTERNAL) : address(addr), port(port), typ(t) {} + ff_endpoint(int rank, ConnectionType t = EXTERNAL) : port(rank), typ(t) {} const int getRank() {return port;} std::string address; int port; + ConnectionType typ; }; From 4f93b5cdf674a2fac7d72a2bcbf6a9dae2a4aee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 28 Oct 2021 11:51:39 +0200 Subject: [PATCH 076/202] Temporary commit. Not working solutions --- ff/distributed/ff_dsender.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index d0ee840d..a371a935 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -120,6 +120,10 @@ class ff_dsender: public ff_minode_t { return -1; #endif + // TODO: send the hanshake information + + + // receive the reachable destination from this sockets if (receiveReachableDestinations(socketFD) < 0) return -1; From 217092ac7b2f94cd35af85f88594da5078a0cb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 28 Oct 2021 11:55:04 +0200 Subject: [PATCH 077/202] Revert "Added first manual example of a distributed a2a horizontal splitted" This reverts commit 0f805cc9b8583dcf046f9d18fc69fd6b2eea42f7. --- ff/distributed/ff_dadapters.hpp | 98 ++++++++++-------------- ff/distributed/ff_dgroup.hpp | 44 ----------- ff/distributed/ff_dreceiver.hpp | 1 + ff/distributed/ff_network.hpp | 10 --- ff/distributed/ff_wrappers.hpp | 122 ++++++++++++++---------------- ff/lb.hpp | 8 -- ff/multinode.hpp | 7 -- ff/node.hpp | 8 -- tests/distributed/test_Farm2.cpp | 27 ++----- tests/distributed/test_a2a_h1.cpp | 66 ---------------- tests/distributed/test_farm1.cpp | 13 ++-- 11 files changed, 110 insertions(+), 294 deletions(-) delete mode 100644 tests/distributed/test_a2a_h1.cpp diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index ac6d3a47..c347cb6f 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -28,67 +28,56 @@ struct ResultWrapper { }; template -class SquareBoxEmitter : public ff_minode_t, SMmessage_t> { - std::vector sources; -public: - SquareBoxEmitter(const std::vector localSources) : sources(localSources) {} - - SMmessage_t* svc(TaskWrapper* in) { - int chId = ff_minode::get_channel_id(); - SMmessage_t* o = new SMmessage_t(in->task, chId < sources.size() ? sources[chId] : -1 , -100 - in->destination); - delete in; - return o; +class SquareBoxEmitter : public ff_monode_t, T> { + T* svc(TaskWrapper* in) { + this->ff_send_out_to(in->task, in->destination); + return this->GO_ON; } }; template -class SquareBoxCollector : public ff_monode_t> { - std::unordered_map destinations; -public: - SquareBoxCollector(const std::unordered_map localDestinations) : destinations(localDestinations) {} - - ResultWrapper* svc(SMmessage_t* in){ - this->ff_send_out_to(new ResultWrapper(reinterpret_cast(in->task), in->sender), destinations[in->dst]); - delete in; return this->GO_ON; +class SquareBoxCollector : public ff_minode_t> { + ResultWrapper* svc(T* in){ + return new ResultWrapper(in, ff_minode::get_channel_id()); } }; + template class EmitterAdapter: public internal_mo_transformer { private: - int totalWorkers; - std::unordered_map localWorkersMap; + int totalWorkers, localWorkers; int nextDestination; public: typedef Tout T_out; - EmitterAdapter(ff_node_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { + EmitterAdapter(ff_node_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_node* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { + EmitterAdapter(ff_node* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_monode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { + EmitterAdapter(ff_monode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_minode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { + EmitterAdapter(ff_minode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - EmitterAdapter(ff_comb_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { + EmitterAdapter(ff_comb_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -104,27 +93,19 @@ class EmitterAdapter: public internal_mo_transformer { } bool forward(void* task, int destination){ - if (destination == -1) { - destination = nextDestination; - nextDestination = (nextDestination + 1) % totalWorkers; - } - - - - auto pyshicalDestination = localWorkersMap.find(destination); - if (pyshicalDestination != localWorkersMap.end()) { - ff_send_out_to(task, pyshicalDestination->second); - } else { - ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkersMap.size()); - } + if (destination == -1) destination = nextDestination; + if (destination < localWorkers) ff_send_out_to(task, destination); + else ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkers); + + nextDestination = (nextDestination + 1) % totalWorkers; return true; } int svc_init() { if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); - mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers + mo->set_running(localWorkers + 1); // the last worker is the forwarder to the remote workers } return n->svc_init(); } @@ -149,31 +130,31 @@ template class CollectorAdapter: public internal_mi_transformer { private: - std::vector localWorkers; + int localWorkers, indexFirstLocal; // number of input channels the wrapped node is supposed to have public: - CollectorAdapter(ff_node_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { + CollectorAdapter(ff_node_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_node* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { + CollectorAdapter(ff_node* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_minode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { + CollectorAdapter(ff_minode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_monode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { + CollectorAdapter(ff_monode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { this->n = n; this->cleanup = cleanup; } template - CollectorAdapter(ff_comb_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { + CollectorAdapter(ff_comb_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { this->n = n; this->cleanup = cleanup; } @@ -183,9 +164,9 @@ class CollectorAdapter: public internal_mi_transformer { } int svc_init() { - if (this->n->isMultiInput()) { ////???????? MASSIMO??? + if (this->n->isMultiOutput()) { ff_minode* mi = reinterpret_cast(this->n); - mi->set_running(localWorkers.size() + 1); + mi->set_running(localWorkers + indexFirstLocal); } return n->svc_init(); } @@ -196,26 +177,29 @@ class CollectorAdapter: public internal_mi_transformer { Tin * task; ssize_t channel; - // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from - if (get_channel_id() == localWorkers.size()){ - ResultWrapper * tw = reinterpret_cast*>(in); + // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from + if (get_channel_id() == 0){ + ResultWrapper * tw = reinterpret_cast*>(in); task = tw->result; channel = tw->source; - } else { // the result come from a local worker, just pass it to collector and compute the right worker id - task = reinterpret_cast(in); - channel = localWorkers.at(get_channel_id()); - } + } else { // the result come from a local worker, just pass it to collector and compute the right worker id + task = reinterpret_cast(in); + channel = get_channel_id() + indexFirstLocal - 1; + } + - // update the input channel id field only if the wrapped node is a multi input if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channel, true); - } - + } return n->svc(task); } ff_node* getOriginal(){ return this->n; } }; + + + + #endif \ No newline at end of file diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index b20280ee..e63fe940 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -183,39 +183,6 @@ class dGroup : public ff_farm { return false; } - static int getOutputIndexOfNode(ff_node*bb, ff_node* wrapper, ff_node* original = nullptr){ - if (bb->isAll2All()){ - ff_a2a* a2a = (ff_a2a*) bb; - int index = 0; - for (ff_node* n : a2a->getFirstSet()){ - ff::svector outputs; n->get_out_nodes(outputs); - for (const ff_node* output : outputs){ - if (output == wrapper || output == original) - return index; - index++; - } - } - - index = 0; - for (ff_node* n : a2a->getSecondSet()){ - ff::svector outputs; n->get_out_nodes(outputs); - for (ff_node* output : outputs) - if (output == wrapper || output == original) - return index; - else index++; - } - } - - int index = 0; - ff::svector outputs; bb->get_out_nodes(outputs); - for (ff_node* output : outputs) - if (output == wrapper || output == original) - return index; - else index++; - - return 0; - } - static int getInputIndexOfNode(ff_node* bb, ff_node* wrapper, ff_node* original){ if (bb->isAll2All()){ ff_a2a* a2a = (ff_a2a*) bb; @@ -329,7 +296,6 @@ class dGroup : public ff_farm { if (this->getNWorkers() == 0) return -1; - // create receiver Proto currentProto = dGroups::Instance()->usedProtocol; @@ -353,16 +319,6 @@ class dGroup : public ff_farm { } // create sender if (!isSink()){ - - // set the senderID to each wrapperOUT (or WrapperINOUT), meaningful only when the the group is not a sink - for (ff_node* bb : this->getWorkers()){ - ff::svector outputs; bb->get_out_nodes(outputs); - for (ff_node* output : outputs) - if (dynamic_cast(output) != NULL) - dynamic_cast(output)->setMyId(getOutputIndexOfNode(parentStructure, output)); - else - ff::cout << "Else branch dynamic cast" << ff::endl; - } if (currentProto == Proto::TCP){ if (onDemandSender) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index d89a9ddf..8cb9cbec 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -322,6 +322,7 @@ class ff_dreceiverOD: public ff_dreceiver { } } + return 0; } diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 95ca1b1f..acbc1c8c 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -62,15 +62,6 @@ class dataBuffer: public std::stringbuf { bool cleanup = false; }; -struct SMmessage_t { - SMmessage_t(){} - SMmessage_t(void* t, int s, int d) : task(t), sender(s), dst(d) {} - void * task; - int sender; - int dst; -}; - - struct message_t { message_t(){} message_t(char *rd, size_t size, bool cleanup=true) : data(rd,size,cleanup){} @@ -99,7 +90,6 @@ struct ff_endpoint { struct FF_Exception: public std::runtime_error {FF_Exception(const char* err) throw() : std::runtime_error(err) {}}; - ssize_t readn(int fd, char *ptr, size_t n) { size_t nleft = n; ssize_t nread; diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 2cf427ab..6a637e09 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -12,19 +12,6 @@ using namespace ff; -template -struct DummyNode : public ff_node_t { - Tout* svc(Tin* i){} -}; - -class Wrapper { -protected: - int myID; // used to populate sender field of the header - -public: - Wrapper() : myID(-1){} - void setMyId(int id){myID = id;} -}; /* Wrapper IN class @@ -37,27 +24,43 @@ class WrapperIN: public internal_mi_transformer { public: using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } - WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } - WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } template - WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { + this->n = n; + this->cleanup = cleanup; + } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } - Tin* deserialize(dataBuffer& dbuffer) { + Tin* deserialize(void* buffer) { + message_t* msg = (message_t*)buffer; Tin* data = nullptr; if constexpr (Serialization) { // deserialize the buffer into a heap allocated buffer - std::istream iss(&dbuffer); + std::istream iss(&msg->data); cereal::PortableBinaryInputArchive iarchive(iss); data = new Tin; @@ -66,10 +69,11 @@ class WrapperIN: public internal_mi_transformer { } else { // deserialize the buffer into a heap allocated buffer - dbuffer.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - data = finalizer_(dbuffer.getPtr(), dbuffer.getLen()); + msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper + data = finalizer_(msg->data.getPtr(), msg->data.getLen()); } + delete msg; return data; } @@ -89,10 +93,8 @@ class WrapperIN: public internal_mi_transformer { if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); - } - Tin* inputData = deserialize(msg->data); - delete msg; - return n->svc(inputData); + } + return n->svc(deserialize(in)); } ff_node* getOriginal(){ return this->n; } @@ -101,22 +103,12 @@ class WrapperIN: public internal_mi_transformer { ff_deserialize_F_t getFinalizer() {return this->finalizer_;} }; -template -struct WrapperINCustom : public WrapperIN { - WrapperINCustom() : WrapperIN(new DummyNode(), 1, true){} - - void * svc(void* in) { - message_t* msg = (message_t*)in; - return new SMmessage_t(this->deserialize(msg->data), msg->sender, msg->chid); - } -}; - /* Wrapper OUT class */ template -class WrapperOUT: public Wrapper, public internal_mo_transformer { +class WrapperOUT: public internal_mo_transformer { private: int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; @@ -125,24 +117,34 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; - WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - registerCallback(ff_send_out_to_cbk, this); //maybe useless?? + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - registerCallback(ff_send_out_to_cbk, this); // maybe useless?? + WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup = cleanup; + registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } @@ -154,7 +156,7 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { if constexpr (Serialization){ msg = new message_t; assert(msg); - msg->sender = this->myID; + msg->sender = this->n->get_my_id(); msg->chid = id; std::ostream oss(&msg->data); cereal::PortableBinaryOutputArchive oarchive(oss); @@ -165,7 +167,7 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { msg = new message_t(raw_data.first, raw_data.second, true); assert(msg); - msg->sender = this->myID; + msg->sender = this->n->get_my_id(); msg->chid = id; } @@ -185,7 +187,6 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { ff_monode* mo = reinterpret_cast(this->n); mo->set_running(outchannels); } - return n->svc_init(); } @@ -207,56 +208,43 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { } }; -template -struct WrapperOUTCustom : public WrapperOUT { - WrapperOUTCustom() : WrapperOUT(new DummyNode(), 1, true){} - - void* svc(void* in){ - SMmessage_t * input = reinterpret_cast(in); - Wrapper::myID = input->sender; - this->serialize(reinterpret_cast(input->task), input->dst); - return this->GO_ON; - } -}; - /* Wrapper INOUT class */ template -class WrapperINOUT: public Wrapper, public internal_mi_transformer { +class WrapperINOUT: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; - public: typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -298,7 +286,7 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { if constexpr (SerializationOUT){ msg = new message_t; assert(msg); - msg->sender = this->myID; + msg->sender = this->get_my_id(); msg->chid = id; std::ostream oss(&msg->data); cereal::PortableBinaryOutputArchive oarchive(oss); @@ -309,7 +297,7 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { msg = new message_t(raw_data.first, raw_data.second, true); assert(msg); - msg->sender = this->myID; + msg->sender = this->get_my_id(); msg->chid = id; } diff --git a/ff/lb.hpp b/ff/lb.hpp index 31838aab..ebc63162 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -889,11 +889,7 @@ class ff_loadbalancer: public ff_thread { int neos = filter?filter->neos:1; do { -#ifdef DFF_ENABLED - if (!skipallpop && inpresent){ -#else if (inpresent) { -#endif if (!skipfirstpop) pop(&task); else skipfirstpop=false; @@ -1475,10 +1471,6 @@ class ff_loadbalancer: public ff_thread { bool blocking_in; bool blocking_out; -#ifdef DFF_ENABLED - bool skipallpop; -#endif - #if defined(TRACE_FASTFLOW) unsigned long taskcnt; ticks lostpushticks; diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 5a5dfb74..7361db54 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -650,13 +650,6 @@ class ff_monode: public ff_node { ff_node::skipfirstpop(sk); } -#ifdef DFF_ENABLED - inline void skipallpop(bool sk) { - lb->skipallpop = sk; - ff_node::skipallpop(sk); - } -#endif - /** * \brief Provides the next channel id that will be selected for sending out the task * diff --git a/ff/node.hpp b/ff/node.hpp index 8e0bb4a3..1bebfd2d 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -508,10 +508,6 @@ class ff_node { bool myoutbuffer; bool myinbuffer; bool skip1pop; -#ifdef DFF_ENABLED - bool _skipallpop; -#endif - bool in_active; // allows to disable/enable input tasks receiving bool my_own_thread; @@ -641,10 +637,6 @@ class ff_node { */ virtual inline void skipfirstpop(bool sk) { skip1pop=sk;} -#ifdef DFF_ENABLED - virtual inline void skipallpop(bool sk) {_skipallpop = sk;} -#endif - /** * \brief Gets the status of spontaneous start * diff --git a/tests/distributed/test_Farm2.cpp b/tests/distributed/test_Farm2.cpp index 8cfb01c3..8e4a6d1d 100644 --- a/tests/distributed/test_Farm2.cpp +++ b/tests/distributed/test_Farm2.cpp @@ -22,19 +22,13 @@ struct Source : ff_monode_t{ struct Worker : ff_node_t{ int numWorker; - int tasks = 0; Worker(int numWorker) : numWorker(numWorker) {} std::string* svc(std::string * in){ std::string * output = new std::string(*in + " " + std::to_string(numWorker)); delete in; - tasks++; return output; } - - void svc_end(){ - std::cout << "Worker " << numWorker << " processed " << tasks << "tasks\n"; - } }; struct Sink : ff_minode_t{ @@ -55,14 +49,11 @@ int main(int argc, char*argv[]){ if (atoi(argv[1]) == 0){ ff_farm f; - f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, {{0,0}, {1,1}, {2,2}}, true)); + f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, 3, true)); std::vector workers; - for(int i = 0; i < 3; i++){ + for(int i = 0; i < 3; i++) workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); - //dynamic_cast(workers.back())->setMyId(i); - } - workers.push_back(new WrapperOUT(new SquareBoxEmitter({0}), 1, true)); - + workers.push_back(new WrapperOUT(new SquareBoxEmitter(), 1, true)); f.add_workers(workers); f.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001), ff_endpoint("127.0.0.1", 8002)})); f.run_and_wait_end(); @@ -74,7 +65,6 @@ int main(int argc, char*argv[]){ int j = 0; for(int i = 3; i < 6; i++){ workers.push_back(new WrapperINOUT(new Worker(i), 1, true, FARM_GATEWAY)); - //dynamic_cast(workers.back())->setMyId(i); routingTable.emplace(i, j++); } f.add_workers(workers); @@ -85,18 +75,17 @@ int main(int argc, char*argv[]){ ff_farm f; std::map routingTable; std::vector workers; - int j = 0; - - routingTable.emplace(FARM_GATEWAY, 3); + int j = 1; + workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); + routingTable.emplace(FARM_GATEWAY, 0); for(int i = 6; i < WORKERS; i++){ workers.push_back(new WrapperIN(new Worker(i), 1, true)); routingTable.emplace(i, j++); } - workers.push_back(new WrapperIN(new SquareBoxCollector({std::make_pair(0,0)}), 1, true)); f.add_workers(workers); - f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 2, routingTable)); - f.add_collector(new CollectorAdapter(new Sink, {6, 7, 8}, true), true); + f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); + f.add_collector(new CollectorAdapter(new Sink, 6, 3, true), true); f.run_and_wait_end(); } diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp deleted file mode 100644 index e73253c5..00000000 --- a/tests/distributed/test_a2a_h1.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace ff; -std::mutex mtx; - -struct Source : ff_monode_t{ - int numWorker, generatorID; - Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} - - std::string* svc(std::string* in){ - std::cout << "Source starting generating tasks!" << std::endl; - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); - - std::cout << "Source generated all task sending now EOS!" << std::endl; - return EOS; - } -}; - - -struct Sink : ff_minode_t{ - int sinkID; - Sink(int id): sinkID(id) {} - std::string* svc(std::string* in){ - const std::lock_guard lock(mtx); - std::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << std::endl; - delete in; - return this->GO_ON; - } -}; - -int main(int argc, char*argv[]){ - - if (argc != 2){ - std::cerr << "Execute with the index of process!" << std::endl; - return 1; - } - - ff_farm gFarm; - ff_a2a a2a; - if (atoi(argv[1]) == 0){ - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, {{-100, 1}})); - gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8002))); - - auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); ea->skipallpop(true); - - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); - a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); - - } else { - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, {{-101, 1}})); - gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001))); - - auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); ea->skipallpop(true); - - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); - a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); - - } - gFarm.add_workers({&a2a}); - gFarm.run_and_wait_end(); -} \ No newline at end of file diff --git a/tests/distributed/test_farm1.cpp b/tests/distributed/test_farm1.cpp index 3ca10e3a..b31ba5ef 100644 --- a/tests/distributed/test_farm1.cpp +++ b/tests/distributed/test_farm1.cpp @@ -9,8 +9,6 @@ using namespace ff; #define WORKERS 10 -std::mutex mtx; - struct Source : ff_monode_t{ int numWorker; Source(int numWorker) : numWorker(numWorker) {} @@ -35,7 +33,6 @@ struct Worker : ff_node_t{ struct Sink : ff_minode_t{ std::string* svc(std::string* in){ - const std::lock_guard lock(mtx); std::cout << *in << " received by Collector from " << get_channel_id() << std::endl; delete in; return this->GO_ON; @@ -52,7 +49,7 @@ int main(int argc, char*argv[]){ if (atoi(argv[1]) == 0){ ff_farm f; - f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, {{0,0}, {1,1}, {2,2}, {3,3}, {4,4}}, true)); + f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, WORKERS/2, true)); std::vector workers; for(int i = 0; i < WORKERS/2; i++) workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); @@ -65,17 +62,17 @@ int main(int argc, char*argv[]){ ff_farm f; std::map routingTable; std::vector workers; - int j = 0; - routingTable.emplace(FARM_GATEWAY, 5); + int j = 1; + workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); + routingTable.emplace(FARM_GATEWAY, 0); for(int i = WORKERS / 2; i < WORKERS; i++){ workers.push_back(new WrapperIN(new Worker(i), 1, true)); routingTable.emplace(i, j++); } - workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); f.add_workers(workers); f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); - f.add_collector(new CollectorAdapter(new Sink, {5,6,7,8,9}, true), true); + f.add_collector(new CollectorAdapter(new Sink, 5, 5, true), true); f.run_and_wait_end(); } From dc3e14a5deab88ff2008c9cb86db2476a670a98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 3 Nov 2021 11:21:51 +0100 Subject: [PATCH 078/202] Revert "Temporary commit. Not working solutions" This reverts commit 4f93b5cdf674a2fac7d72a2bcbf6a9dae2a4aee0. --- ff/distributed/ff_dsender.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index a371a935..d0ee840d 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -120,10 +120,6 @@ class ff_dsender: public ff_minode_t { return -1; #endif - // TODO: send the hanshake information - - - // receive the reachable destination from this sockets if (receiveReachableDestinations(socketFD) < 0) return -1; From 89073e58655d8aa654f2edbb9e078ada4f2ceb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 4 Nov 2021 18:00:12 +0100 Subject: [PATCH 079/202] Temporary commit to fix bug on eosnotify --- ff/distributed/ff_dadapters.hpp | 116 ++++++++++++++++++---------- ff/distributed/ff_dreceiver.hpp | 18 +++-- ff/distributed/ff_dsender.hpp | 44 +++++++---- ff/distributed/ff_network.hpp | 11 ++- ff/distributed/ff_wrappers.hpp | 124 ++++++++++++++++-------------- ff/lb.hpp | 10 ++- ff/multinode.hpp | 9 ++- ff/node.hpp | 10 ++- tests/distributed/test_a2a_h1.cpp | 81 +++++++++++++++++++ tests/distributed/test_a2a_h2.cpp | 80 +++++++++++++++++++ 10 files changed, 382 insertions(+), 121 deletions(-) create mode 100644 tests/distributed/test_a2a_h1.cpp create mode 100644 tests/distributed/test_a2a_h2.cpp diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index c347cb6f..f65cff4b 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -28,56 +28,85 @@ struct ResultWrapper { }; template -class SquareBoxEmitter : public ff_monode_t, T> { - T* svc(TaskWrapper* in) { - this->ff_send_out_to(in->task, in->destination); - return this->GO_ON; +class SquareBoxEmitter : public ff_minode_t, SMmessage_t> { + std::vector sources; + int neos = 0; +public: + SquareBoxEmitter(const std::vector localSources) : sources(localSources) {} + + SMmessage_t* svc(TaskWrapper* in) { + int chId = ff_minode::get_channel_id(); + SMmessage_t* o = new SMmessage_t(in->task, chId < sources.size() ? sources[chId] : -1 , -100 - in->destination); + delete in; + return o; } + + void eosnotify(ssize_t i) { + std::cout << "SquareBox - Received EOS! " << i <<"\n"; + if (i == sources.size()) return; + if (++neos == sources.size()){ + this->ff_send_out(this->EOS); + std::cout << "SquareBox - Sending out EOS!\n"; + } + } + + void svc_end() { + std::cout << "SQEmitter Exited!\n"; + } }; template -class SquareBoxCollector : public ff_minode_t> { - ResultWrapper* svc(T* in){ - return new ResultWrapper(in, ff_minode::get_channel_id()); +class SquareBoxCollector : public ff_monode_t> { + std::unordered_map destinations; +public: + SquareBoxCollector(const std::unordered_map localDestinations) : destinations(localDestinations) {} + + ResultWrapper* svc(SMmessage_t* in){ + this->ff_send_out_to(new ResultWrapper(reinterpret_cast(in->task), in->sender), destinations[in->dst]); + delete in; return this->GO_ON; } -}; + void svc_end() { + std::cout << "SQCollector Exited!\n"; + } +}; template class EmitterAdapter: public internal_mo_transformer { private: - int totalWorkers, localWorkers; + int totalWorkers; + std::unordered_map localWorkersMap; int nextDestination; public: typedef Tout T_out; - EmitterAdapter(ff_node_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_node_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_node* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_node* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_monode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_monode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_minode_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_minode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - EmitterAdapter(ff_comb_t* n, int totalWorkers, int localWorkers=1, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkers(localWorkers) { + EmitterAdapter(ff_comb_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -93,19 +122,27 @@ class EmitterAdapter: public internal_mo_transformer { } bool forward(void* task, int destination){ - if (destination == -1) destination = nextDestination; - - if (destination < localWorkers) ff_send_out_to(task, destination); - else ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkers); + if (destination == -1) { + destination = nextDestination; + nextDestination = (nextDestination + 1) % totalWorkers; + } - nextDestination = (nextDestination + 1) % totalWorkers; + + + auto pyshicalDestination = localWorkersMap.find(destination); + if (pyshicalDestination != localWorkersMap.end()) { + ff_send_out_to(task, pyshicalDestination->second); + } else { + ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkersMap.size()); + } + return true; } int svc_init() { if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); - mo->set_running(localWorkers + 1); // the last worker is the forwarder to the remote workers + mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers } return n->svc_init(); } @@ -130,31 +167,31 @@ template class CollectorAdapter: public internal_mi_transformer { private: - int localWorkers, indexFirstLocal; // number of input channels the wrapped node is supposed to have + std::vector localWorkers; public: - CollectorAdapter(ff_node_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_node_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_node* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_node* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_minode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_minode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_monode_t* n, int indexFirstLocal, int localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_monode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } template - CollectorAdapter(ff_comb_t* n, int indexFirstLocal, int localWorkers=1, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers), indexFirstLocal(indexFirstLocal) { + CollectorAdapter(ff_comb_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } @@ -164,9 +201,9 @@ class CollectorAdapter: public internal_mi_transformer { } int svc_init() { - if (this->n->isMultiOutput()) { + if (this->n->isMultiInput()) { ////???????? MASSIMO??? ff_minode* mi = reinterpret_cast(this->n); - mi->set_running(localWorkers + indexFirstLocal); + mi->set_running(localWorkers.size() + 1); } return n->svc_init(); } @@ -177,29 +214,26 @@ class CollectorAdapter: public internal_mi_transformer { Tin * task; ssize_t channel; - // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from - if (get_channel_id() == 0){ - ResultWrapper * tw = reinterpret_cast*>(in); + // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from + if (get_channel_id() == localWorkers.size()){ + ResultWrapper * tw = reinterpret_cast*>(in); task = tw->result; channel = tw->source; - } else { // the result come from a local worker, just pass it to collector and compute the right worker id - task = reinterpret_cast(in); - channel = get_channel_id() + indexFirstLocal - 1; - } - + } else { // the result come from a local worker, just pass it to collector and compute the right worker id + task = reinterpret_cast(in); + channel = localWorkers.at(get_channel_id()); + } + // update the input channel id field only if the wrapped node is a multi input if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channel, true); - } + } + return n->svc(task); } ff_node* getOriginal(){ return this->n; } }; - - - - #endif \ No newline at end of file diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 8cb9cbec..2e038357 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -27,9 +27,9 @@ class ff_dreceiver: public ff_monode_t { std::vector reachableDestinations; for(auto const& p : this->routingTable){ - if (type == ConnectionType::EXTERNAL && p.first > 0) + if (type == ConnectionType::EXTERNAL && p.first >= 0) reachableDestinations.push_back(p.first); - else if (type == ConnectionType::INTERNAL && p.first <= 100) + else if (type == ConnectionType::INTERNAL && p.first <= -100) reachableDestinations.push_back(p.first); } @@ -58,10 +58,12 @@ class ff_dreceiver: public ff_monode_t { case 0: return -1; // connection close } - type = (ConnectionType) ntohl(type); + // TODO da sistemare network byte order del connectiontype!!!!!!!!! + //type = (ConnectionType) type); // save the connection type locally connectionsTypes[sck] = type; + //std::cout << "i got a " << type << " connection" << std::endl; return this->sendRoutingTable(sck, type); } @@ -69,11 +71,11 @@ class ff_dreceiver: public ff_monode_t { void registerEOS(int sck){ switch(connectionsTypes[sck]){ case ConnectionType::EXTERNAL: - if (Eneos++ == Einput_channels) - for(auto& c : routingTable) if (c.first > 0) ff_send_out_to(this->EOS, c.second); + if (++Eneos == Einput_channels) + for(auto& c : routingTable) if (c.first >= 0) ff_send_out_to(this->EOS, c.second); break; case ConnectionType::INTERNAL: - if (Ineos++ == Iinput_channels) + if (++Ineos == Iinput_channels) for(auto & c : routingTable) if (c.first <= -100) ff_send_out(this->EOS, c.second); break; } @@ -118,6 +120,7 @@ class ff_dreceiver: public ff_monode_t { return 0; } + registerEOS(sck); return -1; @@ -183,6 +186,9 @@ class ff_dreceiver: public ff_monode_t { #ifdef LOCAL unlink(this->acceptAddr.address.c_str()); #endif + + std::cout << "Receiver Exited!\n"; + } /* Here i should not care of input type nor input data since they come from a socket listener. diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index d0ee840d..c209cd7f 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -25,11 +25,14 @@ using namespace ff; class ff_dsender: public ff_minode_t { protected: size_t neos=0; + size_t Ineos=0; int next_rr_destination = 0; //next destiation to send for round robin policy std::vector dest_endpoints; std::map dest2Socket; + std::unordered_map> type2sck; std::vector sockets; - int coreid; + int internalGateways; + int coreid; int receiveReachableDestinations(int sck){ @@ -120,6 +123,14 @@ class ff_dsender: public ff_minode_t { return -1; #endif + // store the connection type + type2sck[destination.typ].push_back(socketFD); + + if (writen(socketFD, (char*) &destination.typ, sizeof(destination.typ)) < 0){ + error("Error sending the connection type during handshaking!"); + return -1; + } + // receive the reachable destination from this sockets if (receiveReachableDestinations(socketFD) < 0) return -1; @@ -166,13 +177,11 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(ff_endpoint dest_endpoint, int coreid=-1) - : coreid(coreid) { + ff_dsender(ff_endpoint dest_endpoint, int internalGateways = 0, int coreid=-1): internalGateways(internalGateways), coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector dest_endpoints_, int coreid=-1) - : dest_endpoints(std::move(dest_endpoints_)),coreid(coreid) {} + ff_dsender( std::vector dest_endpoints_, int internalGateways = 0, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), internalGateways(internalGateways), coreid(coreid) {} int svc_init() { if (coreid!=-1) @@ -188,7 +197,9 @@ class ff_dsender: public ff_minode_t { void svc_end() { // close the socket not matter if local or remote for(size_t i=0; i < this->sockets.size(); i++) - close(sockets[i]); + close(sockets[i]); + + std::cout << "Sender Exited!\n"; } message_t *svc(message_t* task) { @@ -198,20 +209,25 @@ class ff_dsender: public ff_minode_t { next_rr_destination = (next_rr_destination + 1) % dest2Socket.size(); } + sendToSck(dest2Socket[task->chid], task); delete task; return this->GO_ON; } - void eosnotify(ssize_t) { - if (++neos >= this->get_num_inchannels()){ - message_t * E_O_S = new message_t; - E_O_S->chid = 0; - E_O_S->sender = 0; - for(const auto& sck : sockets) - sendToSck(sck, E_O_S); + void eosnotify(ssize_t i) { + // receive it from an internal gateway + std::cout << "Sender: getchannelid: " << this->get_channel_id() << " - i: " << i << std::endl; + if (internalGateways > 0 && this->get_channel_id() >= (this->get_num_inchannels() - internalGateways) && ++Ineos == internalGateways){ + message_t E_O_S(0,0); + for (const auto & sck : type2sck[ConnectionType::INTERNAL]) sendToSck(sck, &E_O_S); + } else + ++neos; - delete E_O_S; + if (neos > 0 && neos + Ineos >= this->get_num_inchannels()){ + // send to all the external + message_t E_O_S(0,0); + for(const auto& sck : type2sck[ConnectionType::EXTERNAL]) sendToSck(sck, &E_O_S); } } diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index acbc1c8c..f581b514 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -62,8 +62,17 @@ class dataBuffer: public std::stringbuf { bool cleanup = false; }; +struct SMmessage_t { + SMmessage_t(){} + SMmessage_t(void* t, int s, int d) : task(t), sender(s), dst(d) {} + void * task; + int sender; + int dst; +}; + struct message_t { message_t(){} + message_t(int sender, int chid) : sender(sender), chid(chid) {} message_t(char *rd, size_t size, bool cleanup=true) : data(rd,size,cleanup){} int sender; @@ -75,7 +84,7 @@ struct ack_t { char ack = 'A'; }; -enum ConnectionType : int {EXTERNAL, INTERNAL}; +enum ConnectionType : int {EXTERNAL = 0, INTERNAL = 1}; struct ff_endpoint { ff_endpoint(){} diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 6a637e09..354959ce 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -12,6 +12,19 @@ using namespace ff; +template +struct DummyNode : public ff_node_t { + Tout* svc(Tin* i){} +}; + +class Wrapper { +protected: + int myID; // used to populate sender field of the header + +public: + Wrapper() : myID(-1){} + void setMyId(int id){myID = id;} +}; /* Wrapper IN class @@ -24,43 +37,27 @@ class WrapperIN: public internal_mi_transformer { public: using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} - WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} - WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} template - WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { - this->n = n; - this->cleanup = cleanup; - } + WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } - Tin* deserialize(void* buffer) { - message_t* msg = (message_t*)buffer; + Tin* deserialize(dataBuffer& dbuffer) { Tin* data = nullptr; if constexpr (Serialization) { // deserialize the buffer into a heap allocated buffer - std::istream iss(&msg->data); + std::istream iss(&dbuffer); cereal::PortableBinaryInputArchive iarchive(iss); data = new Tin; @@ -69,11 +66,10 @@ class WrapperIN: public internal_mi_transformer { } else { // deserialize the buffer into a heap allocated buffer - msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - data = finalizer_(msg->data.getPtr(), msg->data.getLen()); + dbuffer.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper + data = finalizer_(dbuffer.getPtr(), dbuffer.getLen()); } - delete msg; return data; } @@ -93,8 +89,10 @@ class WrapperIN: public internal_mi_transformer { if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); - } - return n->svc(deserialize(in)); + } + Tin* inputData = deserialize(msg->data); + delete msg; + return n->svc(inputData); } ff_node* getOriginal(){ return this->n; } @@ -103,12 +101,22 @@ class WrapperIN: public internal_mi_transformer { ff_deserialize_F_t getFinalizer() {return this->finalizer_;} }; +template +struct WrapperINCustom : public WrapperIN { + WrapperINCustom() : WrapperIN(new DummyNode(), 1, true){} + + void * svc(void* in) { + message_t* msg = (message_t*)in; + return new SMmessage_t(this->deserialize(msg->data), msg->sender, msg->chid); + } +}; + /* Wrapper OUT class */ template -class WrapperOUT: public internal_mo_transformer { +class WrapperOUT: public Wrapper, public internal_mo_transformer { private: int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; @@ -117,34 +125,24 @@ class WrapperOUT: public internal_mo_transformer { typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; - WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; + WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + registerCallback(ff_send_out_to_cbk, this); //maybe useless?? } - WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); + WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + registerCallback(ff_send_out_to_cbk, this); // maybe useless?? } - WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; + WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { registerCallback(ff_send_out_to_cbk, this); } template - WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup = cleanup; + WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { registerCallback(ff_send_out_to_cbk, this); } @@ -156,7 +154,7 @@ class WrapperOUT: public internal_mo_transformer { if constexpr (Serialization){ msg = new message_t; assert(msg); - msg->sender = this->n->get_my_id(); + msg->sender = this->myID; msg->chid = id; std::ostream oss(&msg->data); cereal::PortableBinaryOutputArchive oarchive(oss); @@ -167,7 +165,7 @@ class WrapperOUT: public internal_mo_transformer { msg = new message_t(raw_data.first, raw_data.second, true); assert(msg); - msg->sender = this->n->get_my_id(); + msg->sender = this->myID; msg->chid = id; } @@ -187,6 +185,7 @@ class WrapperOUT: public internal_mo_transformer { ff_monode* mo = reinterpret_cast(this->n); mo->set_running(outchannels); } + return n->svc_init(); } @@ -208,43 +207,56 @@ class WrapperOUT: public internal_mo_transformer { } }; +template +struct WrapperOUTCustom : public WrapperOUT { + WrapperOUTCustom() : WrapperOUT(new DummyNode(), 1, true){} + + void* svc(void* in){ + SMmessage_t * input = reinterpret_cast(in); + Wrapper::myID = input->sender; + this->serialize(reinterpret_cast(input->task), input->dst); + return this->GO_ON; + } +}; + /* Wrapper INOUT class */ template -class WrapperINOUT: public internal_mi_transformer { +class WrapperINOUT: public Wrapper, public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; + public: typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { + WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -286,7 +298,7 @@ class WrapperINOUT: public internal_mi_transformer { if constexpr (SerializationOUT){ msg = new message_t; assert(msg); - msg->sender = this->get_my_id(); + msg->sender = this->myID; msg->chid = id; std::ostream oss(&msg->data); cereal::PortableBinaryOutputArchive oarchive(oss); @@ -297,7 +309,7 @@ class WrapperINOUT: public internal_mi_transformer { msg = new message_t(raw_data.first, raw_data.second, true); assert(msg); - msg->sender = this->get_my_id(); + msg->sender = this->myID; msg->chid = id; } @@ -353,4 +365,4 @@ class WrapperINOUT: public internal_mi_transformer { } }; -#endif +#endif \ No newline at end of file diff --git a/ff/lb.hpp b/ff/lb.hpp index ebc63162..ed2045e9 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -889,7 +889,11 @@ class ff_loadbalancer: public ff_thread { int neos = filter?filter->neos:1; do { +#ifdef DFF_ENABLED + if (!skipallpop && inpresent){ +#else if (inpresent) { +#endif if (!skipfirstpop) pop(&task); else skipfirstpop=false; @@ -1471,6 +1475,10 @@ class ff_loadbalancer: public ff_thread { bool blocking_in; bool blocking_out; +#ifdef DFF_ENABLED + bool skipallpop; +#endif + #if defined(TRACE_FASTFLOW) unsigned long taskcnt; ticks lostpushticks; @@ -1486,4 +1494,4 @@ class ff_loadbalancer: public ff_thread { } // namespace ff -#endif /* FF_LB_HPP */ +#endif /* FF_LB_HPP */ \ No newline at end of file diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 7361db54..23b7e4bb 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -650,6 +650,13 @@ class ff_monode: public ff_node { ff_node::skipfirstpop(sk); } +#ifdef DFF_ENABLED + inline void skipallpop(bool sk) { + lb->skipallpop = sk; + ff_node::skipallpop(sk); + } +#endif + /** * \brief Provides the next channel id that will be selected for sending out the task * @@ -1064,4 +1071,4 @@ struct internal_mi_transformer: ff_minode { } // namespace -#endif /* FF_MULTINODE_HPP */ +#endif /* FF_MULTINODE_HPP */ \ No newline at end of file diff --git a/ff/node.hpp b/ff/node.hpp index 1bebfd2d..4458e5cc 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -508,6 +508,10 @@ class ff_node { bool myoutbuffer; bool myinbuffer; bool skip1pop; +#ifdef DFF_ENABLED + bool _skipallpop; +#endif + bool in_active; // allows to disable/enable input tasks receiving bool my_own_thread; @@ -637,6 +641,10 @@ class ff_node { */ virtual inline void skipfirstpop(bool sk) { skip1pop=sk;} +#ifdef DFF_ENABLED + virtual inline void skipallpop(bool sk) {_skipallpop = sk;} +#endif + /** * \brief Gets the status of spontaneous start * @@ -1649,4 +1657,4 @@ struct ff_buffernode: ff_node { } // namespace ff -#endif /* FF_NODE_HPP */ +#endif /* FF_NODE_HPP */ \ No newline at end of file diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp new file mode 100644 index 00000000..50ec9e64 --- /dev/null +++ b/tests/distributed/test_a2a_h1.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct Source : ff_monode_t{ + int numWorker, generatorID; + Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + std::string* svc(std::string* in){ + std::cout << "Source starting generating tasks!" << std::endl; + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + + + if (generatorID != 0) + sleep(10); + + std::cout << "Source " << generatorID << " generated all task sending now EOS!" << std::endl; + return EOS; + } + void svc_end() { + std::cout << "Source Exited!\n"; + } +}; + + +struct Sink : ff_minode_t{ + int sinkID; + Sink(int id): sinkID(id) {} + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << std::endl; + delete in; + return this->GO_ON; + } + + void eosnotify(ssize_t){ + std::cout << "Receiver " << sinkID << " - Received EOS!\n"; + } + + void svc_end() { + std::cout << "Sink Exited!\n"; + } +}; + +int main(int argc, char*argv[]){ + + if (argc != 2){ + std::cerr << "Execute with the index of process!" << std::endl; + return 1; + } + + ff_farm gFarm; + ff_a2a a2a; + if (atoi(argv[1]) == 0){ + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 0, 1, {{-100, 1}})); + gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8002, ConnectionType::INTERNAL), 1)); + + auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); ea->skipallpop(true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); + a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); + + } else { + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 0, 1, {{-101, 1}})); + gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), 1)); + + auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); ea->skipallpop(true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); + a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); + + } + gFarm.add_workers({&a2a}); + gFarm.run_and_wait_end(); +} \ No newline at end of file diff --git a/tests/distributed/test_a2a_h2.cpp b/tests/distributed/test_a2a_h2.cpp new file mode 100644 index 00000000..b42bcb87 --- /dev/null +++ b/tests/distributed/test_a2a_h2.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct RealSource : ff_monode_t{ + std::string* svc(std::string*){ + for(int i = 0; i < 2; i++) ff_send_out_to(new std::string("Trigger string!"), i); + return EOS; + } +}; + +struct Source : ff_monode_t{ + int numWorker, generatorID; + Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + std::string* svc(std::string* in){ + delete in; + std::cout << "Source starting generating tasks!" << std::endl; + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + + std::cout << "Source generated all task sending now EOS!" << std::endl; + return EOS; + } +}; + + +struct Sink : ff_minode_t{ + int sinkID; + Sink(int id): sinkID(id) {} + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + + if (argc != 2){ + std::cerr << "Execute with the index of process!" << std::endl; + return 1; + } + + ff_farm gFarm; + ff_a2a a2a; + if (atoi(argv[1]) == 0){ + gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001, ConnectionType::EXTERNAL), ff_endpoint("127.0.0.1", 8002, ConnectionType::EXTERNAL)})); + gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true, -1)}); + + gFarm.run_and_wait_end(); + return 0; + } else if (atoi(argv[1]) == 1){ + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, 1, {{0, 0}, {-100, 1}})); + gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8002, ConnectionType::INTERNAL), 1)); + + auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); //ea->skipallpop(true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); + a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); + + } else { + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, 1, {{1, 0}, {-101, 1}})); + gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), 1)); + + auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); //ea->skipallpop(true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); + a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); + + } + gFarm.add_workers({&a2a}); + gFarm.run_and_wait_end(); +} \ No newline at end of file From b772dd3211f7824145633ba70efde5cd1aeef3b5 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 5 Nov 2021 09:45:54 +0100 Subject: [PATCH 080/202] fixed problem with eosnotify --- ff/all2all.hpp | 7 +++++-- ff/distributed/ff_dadapters.hpp | 16 +++------------- ff/distributed/ff_dreceiver.hpp | 3 --- ff/distributed/ff_dsender.hpp | 7 ++----- tests/distributed/test_a2a_h1.cpp | 24 ++++-------------------- tests/distributed/test_a2a_h2.cpp | 19 ++++++++++++++----- 6 files changed, 28 insertions(+), 48 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 85c7c1ed..c2f8d6ec 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -713,6 +713,7 @@ class ff_a2a: public ff_node { } int create_output_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { + int id=0; size_t nworkers2 = workers2.size(); for(size_t i=0;iisMultiOutput()) { @@ -721,12 +722,14 @@ class ff_a2a: public ff_node { assert(w.size()); for(size_t j=0;jset_id(j); + t->set_id(id++); internalSupportNodes.push_back(t); if (workers2[i]->set_output(t)<0) return -1; } - } else + } else{ if (workers2[i]->create_output_buffer(nentries,fixedsize)==-1) return -1; + id++; + } } return 0; } diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index f65cff4b..a5b8d84c 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -41,18 +41,12 @@ class SquareBoxEmitter : public ff_minode_t, SMmessage_t> { return o; } - void eosnotify(ssize_t i) { - std::cout << "SquareBox - Received EOS! " << i <<"\n"; - if (i == sources.size()) return; + void eosnotify(ssize_t id) { + if (id == sources.size()) return; // EOS coming from the SquareBoxCollector, we must ignore it if (++neos == sources.size()){ this->ff_send_out(this->EOS); - std::cout << "SquareBox - Sending out EOS!\n"; } } - - void svc_end() { - std::cout << "SQEmitter Exited!\n"; - } }; template @@ -65,10 +59,6 @@ class SquareBoxCollector : public ff_monode_t> { this->ff_send_out_to(new ResultWrapper(reinterpret_cast(in->task), in->sender), destinations[in->dst]); delete in; return this->GO_ON; } - - void svc_end() { - std::cout << "SQCollector Exited!\n"; - } }; template @@ -236,4 +226,4 @@ class CollectorAdapter: public internal_mi_transformer { ff_node* getOriginal(){ return this->n; } }; -#endif \ No newline at end of file +#endif diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 2e038357..d4d2d940 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -186,9 +186,6 @@ class ff_dreceiver: public ff_monode_t { #ifdef LOCAL unlink(this->acceptAddr.address.c_str()); #endif - - std::cout << "Receiver Exited!\n"; - } /* Here i should not care of input type nor input data since they come from a socket listener. diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index c209cd7f..1017ca2b 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -198,8 +198,6 @@ class ff_dsender: public ff_minode_t { // close the socket not matter if local or remote for(size_t i=0; i < this->sockets.size(); i++) close(sockets[i]); - - std::cout << "Sender Exited!\n"; } message_t *svc(message_t* task) { @@ -215,10 +213,9 @@ class ff_dsender: public ff_minode_t { return this->GO_ON; } - void eosnotify(ssize_t i) { + void eosnotify(ssize_t id) { // receive it from an internal gateway - std::cout << "Sender: getchannelid: " << this->get_channel_id() << " - i: " << i << std::endl; - if (internalGateways > 0 && this->get_channel_id() >= (this->get_num_inchannels() - internalGateways) && ++Ineos == internalGateways){ + if (internalGateways > 0 && id >= (this->get_num_inchannels() - internalGateways) && ++Ineos == internalGateways){ message_t E_O_S(0,0); for (const auto & sck : type2sck[ConnectionType::INTERNAL]) sendToSck(sck, &E_O_S); } else diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp index 50ec9e64..c7261284 100644 --- a/tests/distributed/test_a2a_h1.cpp +++ b/tests/distributed/test_a2a_h1.cpp @@ -12,20 +12,12 @@ struct Source : ff_monode_t{ Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} std::string* svc(std::string* in){ - std::cout << "Source starting generating tasks!" << std::endl; - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); - - - if (generatorID != 0) - sleep(10); - std::cout << "Source " << generatorID << " generated all task sending now EOS!" << std::endl; + for(int i = 0; i < 10; i++) + ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i%numWorker)), i%numWorker); + return EOS; } - void svc_end() { - std::cout << "Source Exited!\n"; - } }; @@ -38,14 +30,6 @@ struct Sink : ff_minode_t{ delete in; return this->GO_ON; } - - void eosnotify(ssize_t){ - std::cout << "Receiver " << sinkID << " - Received EOS!\n"; - } - - void svc_end() { - std::cout << "Sink Exited!\n"; - } }; int main(int argc, char*argv[]){ @@ -78,4 +62,4 @@ int main(int argc, char*argv[]){ } gFarm.add_workers({&a2a}); gFarm.run_and_wait_end(); -} \ No newline at end of file +} diff --git a/tests/distributed/test_a2a_h2.cpp b/tests/distributed/test_a2a_h2.cpp index b42bcb87..d0f27960 100644 --- a/tests/distributed/test_a2a_h2.cpp +++ b/tests/distributed/test_a2a_h2.cpp @@ -21,8 +21,8 @@ struct Source : ff_monode_t{ std::string* svc(std::string* in){ delete in; std::cout << "Source starting generating tasks!" << std::endl; - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + for(int i = 0; i < 10; i++) + ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i%numWorker)), i%numWorker); std::cout << "Source generated all task sending now EOS!" << std::endl; return EOS; @@ -41,6 +41,12 @@ struct Sink : ff_minode_t{ } }; + +struct ForwarderNode : ff_node{ + void* svc(void* input){return input;} +}; + + int main(int argc, char*argv[]){ if (argc != 2){ @@ -60,8 +66,10 @@ int main(int argc, char*argv[]){ gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, 1, {{0, 0}, {-100, 1}})); gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8002, ConnectionType::INTERNAL), 1)); - auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); //ea->skipallpop(true); + //auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); //ea->skipallpop(true); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true), true, true); + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); @@ -69,7 +77,8 @@ int main(int argc, char*argv[]){ gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, 1, {{1, 0}, {-101, 1}})); gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), 1)); - auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); //ea->skipallpop(true); + //auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); //ea->skipallpop(true); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true), true, true); a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); @@ -77,4 +86,4 @@ int main(int argc, char*argv[]){ } gFarm.add_workers({&a2a}); gFarm.run_and_wait_end(); -} \ No newline at end of file +} From cde95f22b909905523b5a51f66636a15b7977b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 15 Nov 2021 14:55:53 +0100 Subject: [PATCH 081/202] Added test_a2a_h3.cpp --- tests/distributed/test_a2a_h3.cpp | 105 ++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tests/distributed/test_a2a_h3.cpp diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp new file mode 100644 index 00000000..c4a38409 --- /dev/null +++ b/tests/distributed/test_a2a_h3.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct RealSource : ff_monode_t{ + std::string* svc(std::string*){ + for(int i = 0; i < 2; i++) ff_send_out_to(new std::string("Trigger string!"), i); + return EOS; + } +}; + +struct Source : ff_monode_t{ + int numWorker, generatorID; + Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + std::string* svc(std::string* in){ + delete in; + std::cout << "Source starting generating tasks!" << std::endl; + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + + std::cout << "Source generated all task sending now EOS!" << std::endl; + return EOS; + } +}; + + +struct Sink : ff_minode_t{ + int sinkID; + Sink(int id): sinkID(id) {} + std::string* svc(std::string* in){ + std::string* output = new std::string(*in + " received by Sink " + std::to_string(sinkID) + " from " + std::to_string(get_channel_id())); + delete in; + return output; + } +}; + +struct StringPrinter : ff_node_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "Received something! Addr:" << in << "\n"; + try { + std::cout << *in << std::endl; + //delete in; + } catch (const std::exception& ex){ + std::cerr << ex.what(); + } + return this->GO_ON; + } +}; + + +struct ForwarderNode : ff_node{ + void* svc(void* input){return input;} +}; + + +int main(int argc, char*argv[]){ + + if (argc != 2){ + std::cerr << "Execute with the index of process!" << std::endl; + return 1; + } + + ff_farm gFarm; + ff_a2a a2a; + if (atoi(argv[1]) == 0){ + gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001, ConnectionType::EXTERNAL), ff_endpoint("127.0.0.1", 8002, ConnectionType::EXTERNAL)})); + gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true, -1)}); + + gFarm.run_and_wait_end(); + return 0; + } else if (atoi(argv[1]) == 1){ + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, 1, {{0, 0}, {-100, 1}})); + gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8002, ConnectionType::INTERNAL), ff_endpoint("127.0.0.1", 8003, ConnectionType::EXTERNAL)}, 1)); + + auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true), true, true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); + a2a.add_secondset({new ff_comb(new CollectorAdapter(new Sink(0), {0}, true), new WrapperOUT(new ForwarderNode, 1, true, 0)), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); + + } else if (atoi(argv[1]) == 2) { + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, 1, {{1, 0}, {-101, 1}})); + gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), ff_endpoint("127.0.0.1", 8003, ConnectionType::EXTERNAL)}, 1)); + + auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true), true, true); + + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); + a2a.add_secondset({new ff_comb(new CollectorAdapter(new Sink(1), {1}, true), new WrapperOUT(new ForwarderNode, 1, true, 0)), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); + + } else { + gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8003), 2)); + gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); + + gFarm.run_and_wait_end(); + return 0; + } + gFarm.add_workers({&a2a}); + gFarm.run_and_wait_end(); +} From 90acebe77a0651cbd49877f07e08e1b25d05874f Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 15 Nov 2021 16:51:39 +0100 Subject: [PATCH 082/202] fixed related to wrappers and some memory leaks --- ff/distributed/ff_dadapters.hpp | 1 + ff/distributed/ff_dsender.hpp | 3 ++- ff/distributed/ff_wrappers.hpp | 39 ++++++++++++++++++++++--------- ff/distributed/loader/dff_run.cpp | 1 - ff/lb.hpp | 4 ++-- tests/distributed/test_a2a_h3.cpp | 20 ++++++++++++---- 6 files changed, 49 insertions(+), 19 deletions(-) diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index a5b8d84c..b505e12d 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -209,6 +209,7 @@ class CollectorAdapter: public internal_mi_transformer { ResultWrapper * tw = reinterpret_cast*>(in); task = tw->result; channel = tw->source; + delete tw; } else { // the result come from a local worker, just pass it to collector and compute the right worker id task = reinterpret_cast(in); channel = localWorkers.at(get_channel_id()); diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 1017ca2b..b408895d 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -118,7 +118,8 @@ class ff_dsender: public ff_minode_t { close(socketFD); } - + free(result); + if (rp == NULL) /* No address succeeded */ return -1; #endif diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 354959ce..5d0789a0 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -37,16 +37,16 @@ class WrapperIN: public internal_mi_transformer { public: using ff_deserialize_F_t = std::function(char*,size_t)>; - WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup= cleanup; } - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } - WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } - WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } template - WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(n, cleanup), inchannels(inchannels), finalizer_(finalizer) {} + WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); @@ -107,7 +107,9 @@ struct WrapperINCustom : public WrapperIN { void * svc(void* in) { message_t* msg = (message_t*)in; - return new SMmessage_t(this->deserialize(msg->data), msg->sender, msg->chid); + auto* out = new SMmessage_t(this->deserialize(msg->data), msg->sender, msg->chid); + delete msg; + return out; } }; @@ -125,25 +127,39 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { typedef Tout T_out; using ff_serialize_F_t = std::function(std::add_pointer_t)>; - WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); } - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); //maybe useless?? + } - WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup= cleanup; + registerCallback(ff_send_out_to_cbk, this); // maybe useless?? } - WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(n, cleanup), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); + } template WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + this->n = n; + this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); + } bool serialize(Tout* in, int id) { @@ -215,6 +231,7 @@ struct WrapperOUTCustom : public WrapperOUT { SMmessage_t * input = reinterpret_cast(in); Wrapper::myID = input->sender; this->serialize(reinterpret_cast(input->task), input->dst); + delete in; return this->GO_ON; } }; @@ -365,4 +382,4 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { } }; -#endif \ No newline at end of file +#endif diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 8489b897..338a11c9 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include diff --git a/ff/lb.hpp b/ff/lb.hpp index ed2045e9..984e17c1 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -1476,7 +1476,7 @@ class ff_loadbalancer: public ff_thread { bool blocking_out; #ifdef DFF_ENABLED - bool skipallpop; + bool skipallpop = false; #endif #if defined(TRACE_FASTFLOW) @@ -1494,4 +1494,4 @@ class ff_loadbalancer: public ff_thread { } // namespace ff -#endif /* FF_LB_HPP */ \ No newline at end of file +#endif /* FF_LB_HPP */ diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp index c4a38409..1a8ab341 100644 --- a/tests/distributed/test_a2a_h3.cpp +++ b/tests/distributed/test_a2a_h3.cpp @@ -44,12 +44,14 @@ struct StringPrinter : ff_node_t{ std::string* svc(std::string* in){ const std::lock_guard lock(mtx); std::cout << "Received something! Addr:" << in << "\n"; +#if 1 try { std::cout << *in << std::endl; - //delete in; + delete in; } catch (const std::exception& ex){ std::cerr << ex.what(); } +#endif return this->GO_ON; } }; @@ -87,11 +89,21 @@ int main(int argc, char*argv[]){ } else if (atoi(argv[1]) == 2) { gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, 1, {{1, 0}, {-101, 1}})); gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), ff_endpoint("127.0.0.1", 8003, ConnectionType::EXTERNAL)}, 1)); + gFarm.cleanup_emitter(); + gFarm.cleanup_collector(); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true), true, true); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode, 1, true), new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true), true, true); - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); - a2a.add_secondset({new ff_comb(new CollectorAdapter(new Sink(1), {1}, true), new WrapperOUT(new ForwarderNode, 1, true, 0)), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); + a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}, 0, true); + + a2a.add_secondset({ + new ff_comb(new CollectorAdapter(new Sink(1), {1}, true), + new WrapperOUT(new ForwarderNode, 1, true, 0), true, true), + new ff_comb(new SquareBoxEmitter({1}), + new WrapperOUTCustom(),true, true) + }, true); + + } else { gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8003), 2)); From 4ad40365394bd6ddf86c7edfb05e7b345a2daa44 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 16 Nov 2021 16:00:06 +0100 Subject: [PATCH 083/202] fixed problem with BLOCKING_MODE in the farm containing an a2a, Makefile improved --- ff/distributed/loader/dff_run.cpp | 4 ++-- ff/farm.hpp | 12 +++++++----- tests/distributed/Makefile | 10 +++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 338a11c9..fe276c12 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -109,7 +109,8 @@ static inline void usage(char* progname) { std::cout << "\nUSAGE: " << progname << " [Options] -f \n" << "Options: \n" << "\t -v ,..., \t Prints the output of the specified groups\n" - << "\t -V \t Print the output of all groups\n"; + << "\t -V \t Print the output of all groups\n" + << "\t -p \"TCP|MPI\" \t Force communication protocol\n"; } @@ -272,7 +273,6 @@ int main(int argc, char** argv) { // invoke mpirun using the just created hostfile char command[350]; - sprintf(command, "mpirun --hostfile %s %s --DFF_Config=%s", hostFile.c_str(), executable.c_str(), configFile.c_str()); diff --git a/ff/farm.hpp b/ff/farm.hpp index 9dcdf4bc..540c4df9 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -369,12 +369,14 @@ class ff_farm: public ff_node { if (W2[i]->set_output(outputNodes[i])<0) return -1; } } else { - // NOTE: the following call might fail because the buffers were already created for example by - // the pipeline that contains this stage - if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) { - if (lb->masterworker()) return -1; // something went wrong - } + // TODO: we support only feedback channels and not both feedback and forward channels + // for the last stages of the last all-to-all if (lb->masterworker()) { + if (a2a_last->create_output_buffer(out_buffer_entries,(lb->masterworker()?false:fixedsizeOUT))<0) { + error("FARM failed to create feedback channels\n"); + return -1; + } + for(size_t i=0;i w(1); W2[i]->get_out_nodes(w); diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index 9f8e0fc0..a3f19866 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -35,8 +35,12 @@ endif ifdef EXCLUDE_MPI CXXFLAGS += -DDFF_EXCLUDE_MPI else - INCS += -I/usr/local/Cellar/open-mpi/4.1.1_2/include - CXXFLAGS += -L/usr/local/opt/libevent/lib -L/usr/local/Cellar/open-mpi/4.1.1_2/lib -lmpi +ifdef MPI_HOME + INCS += `pkg-config --cflags-only-I $(MPI_HOME)/lib/pkgconfig/ompi-cxx.pc` + LIBS += `pkg-config --libs $(MPI_HOME)/lib/pkgconfig/ompi-cxx.pc` +else + CXXFLAGS += -DDFF_EXCLUDE_MPI +endif endif ifdef FF_HOME INCS += -I$(FF_HOME) @@ -51,7 +55,7 @@ endif CXXFLAGS += -Wall -LIBS = -pthread +LIBS += -pthread INCLUDES = $(INCS) SOURCES = $(wildcard *.cpp) From 762535a1b4baadab45b3e2438ca4d5054b531b5e Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 17 Nov 2021 10:15:39 +0100 Subject: [PATCH 084/202] removed some warnings and minor fixes --- ff/distributed/ff_dadapters.hpp | 8 ++-- ff/distributed/ff_dprinter.hpp | 4 +- ff/distributed/ff_dreceiverMPI.hpp | 12 ++--- ff/distributed/ff_dsender.hpp | 8 ++-- ff/distributed/ff_dsenderMPI.hpp | 12 ++--- ff/distributed/ff_wrappers.hpp | 4 +- tests/distributed/dwordcount/dwordcount.cpp | 45 +++++++++---------- tests/distributed/dwordcount/dwordcountb.cpp | 47 ++++++++++---------- tests/distributed/runParametricPerf.sh | 9 ++-- tests/distributed/test_parametricPerf.json | 30 +++++++++---- 10 files changed, 96 insertions(+), 83 deletions(-) diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index b505e12d..650e54d2 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -35,15 +35,15 @@ class SquareBoxEmitter : public ff_minode_t, SMmessage_t> { SquareBoxEmitter(const std::vector localSources) : sources(localSources) {} SMmessage_t* svc(TaskWrapper* in) { - int chId = ff_minode::get_channel_id(); + size_t chId = ff_minode::get_channel_id(); SMmessage_t* o = new SMmessage_t(in->task, chId < sources.size() ? sources[chId] : -1 , -100 - in->destination); delete in; return o; } void eosnotify(ssize_t id) { - if (id == sources.size()) return; // EOS coming from the SquareBoxCollector, we must ignore it - if (++neos == sources.size()){ + if (id == (ssize_t)sources.size()) return; // EOS coming from the SquareBoxCollector, we must ignore it + if (++neos == (int)sources.size()){ this->ff_send_out(this->EOS); } } @@ -205,7 +205,7 @@ class CollectorAdapter: public internal_mi_transformer { ssize_t channel; // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from - if (get_channel_id() == localWorkers.size()){ + if ((size_t)get_channel_id() == localWorkers.size()){ ResultWrapper * tw = reinterpret_cast*>(in); task = tw->result; channel = tw->source; diff --git a/ff/distributed/ff_dprinter.hpp b/ff/distributed/ff_dprinter.hpp index f9db8d02..54df727a 100644 --- a/ff/distributed/ff_dprinter.hpp +++ b/ff/distributed/ff_dprinter.hpp @@ -16,7 +16,7 @@ namespace ff { if (c != std::char_traits::eof()) { if (this->need_prefix && !this->prefix.empty() - && this->prefix.size() != this->sbuf->sputn(&this->prefix[0], this->prefix.size())) { + && (int)(this->prefix.size()) != this->sbuf->sputn(&this->prefix[0], this->prefix.size())) { return std::char_traits::eof(); } this->need_prefix = c == '\n'; @@ -41,4 +41,4 @@ namespace ff { Printer cout("undefined", std::cout); } -#endif \ No newline at end of file +#endif diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 4fef8eee..fe190f36 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -58,14 +58,14 @@ class ff_dreceiverMPI: public ff_monode_t { */ message_t *svc(message_t* task) { MPI_Status status; - int header[3]; + long header[3]; while(neos < input_channels){ - if (MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) + if (MPI_Recv(header, 3, MPI_LONG, MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) error("Error on Recv Receiver primo in alto\n"); - int sz = header[0]; + size_t sz = header[0]; if (sz == 0){ neos++; @@ -83,6 +83,8 @@ class ff_dreceiverMPI: public ff_monode_t { out->sender = header[1]; out->chid = header[2]; + assert(out->chid>=0); + //std::cout << "received something from " << sender << " directed to " << chid << std::endl; ff_send_out_to(out, this->routingTable[out->chid]); // assume the routing table is consistent WARNING!!! @@ -113,9 +115,9 @@ class ff_dreceiverMPIOD: public ff_dreceiverMPI { message_t *svc(message_t* task) { MPI_Request tmpAckReq; MPI_Status status; - int header[3]; + long header[3]; while(neos < input_channels){ - MPI_Recv(header, 3, MPI_INT, MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status); + MPI_Recv(header, 3, MPI_LONG, MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status); size_t sz = header[0]; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index b408895d..0fb8b63c 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -24,8 +24,8 @@ using namespace ff; class ff_dsender: public ff_minode_t { protected: - size_t neos=0; - size_t Ineos=0; + int neos=0; + int Ineos=0; int next_rr_destination = 0; //next destiation to send for round robin policy std::vector dest_endpoints; std::map dest2Socket; @@ -216,13 +216,13 @@ class ff_dsender: public ff_minode_t { void eosnotify(ssize_t id) { // receive it from an internal gateway - if (internalGateways > 0 && id >= (this->get_num_inchannels() - internalGateways) && ++Ineos == internalGateways){ + if (internalGateways > 0 && id >= (ssize_t)(this->get_num_inchannels() - internalGateways) && ++Ineos == internalGateways){ message_t E_O_S(0,0); for (const auto & sck : type2sck[ConnectionType::INTERNAL]) sendToSck(sck, &E_O_S); } else ++neos; - if (neos > 0 && neos + Ineos >= this->get_num_inchannels()){ + if (neos > 0 && (neos + Ineos) >= (int)this->get_num_inchannels()){ // send to all the external message_t E_O_S(0,0); for(const auto& sck : type2sck[ConnectionType::EXTERNAL]) sendToSck(sck, &E_O_S); diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 3cf44230..7df12335 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -83,9 +83,9 @@ class ff_dsenderMPI: public ff_minode_t { size_t sz = task->data.getLen(); int rank =dest2Rank[task->chid]; - int header[3] = {sz, task->sender, task->chid}; + long header[3] = {(long)sz, task->sender, task->chid}; - MPI_Send(header, 3, MPI_INT, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); @@ -96,10 +96,10 @@ class ff_dsenderMPI: public ff_minode_t { void eosnotify(ssize_t) { if (++neos >= this->get_num_inchannels()){ - int header[3] = {0,0,0}; + long header[3] = {0,0,0}; for(auto& ep : destRanks) - MPI_Send(header, 3, MPI_INT, ep.getRank(), DFF_HEADER_TAG, MPI_COMM_WORLD); + MPI_Send(header, 3, MPI_LONG, ep.getRank(), DFF_HEADER_TAG, MPI_COMM_WORLD); } } @@ -194,9 +194,9 @@ class ff_dsenderMPIOD: public ff_dsenderMPI { size_t sz = task->data.getLen(); - int header[3] = {sz, task->sender, task->chid}; + long header[3] = {(long)sz, task->sender, task->chid}; - MPI_Send(header, 3, MPI_INT, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 5d0789a0..56d4591d 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -14,7 +14,7 @@ using namespace ff; template struct DummyNode : public ff_node_t { - Tout* svc(Tin* i){} + Tout* svc(Tin* in){ return nullptr;} }; class Wrapper { @@ -231,7 +231,7 @@ struct WrapperOUTCustom : public WrapperOUT { SMmessage_t * input = reinterpret_cast(in); Wrapper::myID = input->sender; this->serialize(reinterpret_cast(input->task), input->dst); - delete in; + delete input; return this->GO_ON; } }; diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index 73ed115e..738035fb 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -63,7 +63,6 @@ #include using namespace ff; -using namespace std; const size_t qlen = DEFAULT_BUFFER_CAPACITY; const int MAXLINE=128; @@ -82,9 +81,9 @@ struct result_t { uint64_t ts; // timestamp }; -vector dataset; // contains all the input tuples in memory -atomic total_lines=0; // total number of lines processed by the system -atomic total_bytes=0; // total number of bytes processed by the system +std::vector dataset; // contains all the input tuples in memory +std::atomic total_lines=0; // total number of lines processed by the system +std::atomic total_bytes=0; // total number of bytes processed by the system /// application run time (source generates the stream for app_run_time seconds, then sends out EOS) unsigned long app_run_time = 15; // time in seconds @@ -206,11 +205,11 @@ struct Sink: ff_node_t { * * @param file_path the path of the input dataset file */ -int parse_dataset_and_create_tuples(const string& file_path) { - ifstream file(file_path); +int parse_dataset_and_create_tuples(const std::string& file_path) { + std::ifstream file(file_path); if (file.is_open()) { size_t all_records = 0; // counter of all records (dataset line) read - string line; + std::string line; while (getline(file, line)) { // process file line if (!line.empty()) { @@ -253,11 +252,11 @@ int main(int argc, char* argv[]) { int option = 0; while ((option = getopt(argc, argv, "f:p:t:")) != -1) { switch (option) { - case 'f': file_path=string(optarg); break; + case 'f': file_path=std::string(optarg); break; case 'p': { - vector par_degs; - string pars(optarg); - stringstream ss(pars); + std::vector par_degs; + std::string pars(optarg); + std::stringstream ss(pars); for (size_t i; ss >> i;) { par_degs.push_back(i); if (ss.peek() == ',') @@ -272,7 +271,7 @@ int main(int argc, char* argv[]) { } } break; case 't': { - long t = stol(optarg); + long t = std::stol(optarg); if (t<=0 || t > 100) { std::cerr << "Wrong value for the '-t' option, it should be in the range [1,100]\n"; return -1; @@ -303,10 +302,10 @@ int main(int argc, char* argv[]) { if (parse_dataset_and_create_tuples(file_path)< 0) return -1; - cout << "Executing WordCount with parameters:" << endl; - cout << " * source/splitter : " << source_par_deg << endl; - cout << " * counter/sink : " << sink_par_deg << endl; - cout << " * running time : " << app_run_time << " (s)\n"; + std::cout << "Executing WordCount with parameters:" << endl; + std::cout << " * source/splitter : " << source_par_deg << endl; + std::cout << " * counter/sink : " << sink_par_deg << endl; + std::cout << " * running time : " << app_run_time << " (s)\n"; } /// application starting time @@ -365,15 +364,15 @@ int main(int argc, char* argv[]) { return -1; } volatile unsigned long end_time_main_usecs = current_time_usecs(); - cout << "Exiting" << endl; + std::cout << "Exiting" << endl; double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); - cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; + std::cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; if (DFF_getMyGroup() == "G1") { - cout << "total_lines sent : " << total_lines << "\n"; - cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; + std::cout << "total_lines sent : " << total_lines << "\n"; + std::cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; //double throughput = total_lines / elapsed_time_seconds; //double mbs = (double)((total_bytes / 1048576) / elapsed_time_seconds); - //cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; + //std::cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; } else { size_t words=0; size_t unique=0; @@ -383,8 +382,8 @@ int main(int argc, char* argv[]) { delete S[i]; delete C[i]; } - cout << "words : " << words << "\n"; - cout << "unique : " << unique<< "\n"; + std::cout << "words : " << words << "\n"; + std::cout << "unique : " << unique<< "\n"; } return 0; diff --git a/tests/distributed/dwordcount/dwordcountb.cpp b/tests/distributed/dwordcount/dwordcountb.cpp index 7d3e55b6..938fc219 100644 --- a/tests/distributed/dwordcount/dwordcountb.cpp +++ b/tests/distributed/dwordcount/dwordcountb.cpp @@ -64,7 +64,6 @@ using namespace ff; -using namespace std; const size_t qlen = DEFAULT_BUFFER_CAPACITY; const int MAXLINE=128; @@ -97,9 +96,9 @@ struct Result_t { }; -vector dataset; // contains all the input tuples in memory -atomic total_lines=0; // total number of lines processed by the system -atomic total_bytes=0; // total number of bytes processed by the system +std::vector dataset; // contains all the input tuples in memory +std::atomic total_lines=0; // total number of lines processed by the system +std::atomic total_bytes=0; // total number of bytes processed by the system /// application run time (source generates the stream for app_run_time seconds, then sends out EOS) unsigned long app_run_time = 15; // time in seconds @@ -245,11 +244,11 @@ struct Sink: ff_node_t { * * @param file_path the path of the input dataset file */ -int parse_dataset_and_create_tuples(const string& file_path) { - ifstream file(file_path); +int parse_dataset_and_create_tuples(const std::string& file_path) { + std::ifstream file(file_path); if (file.is_open()) { size_t all_records = 0; // counter of all records (dataset line) read - string line; + std::string line; while (getline(file, line)) { // process file line if (!line.empty()) { @@ -293,11 +292,11 @@ int main(int argc, char* argv[]) { int option = 0; while ((option = getopt(argc, argv, "f:p:t:b:")) != -1) { switch (option) { - case 'f': file_path=string(optarg); break; + case 'f': file_path=std::string(optarg); break; case 'p': { - vector par_degs; - string pars(optarg); - stringstream ss(pars); + std::vector par_degs; + std::string pars(optarg); + std::stringstream ss(pars); for (size_t i; ss >> i;) { par_degs.push_back(i); if (ss.peek() == ',') @@ -312,7 +311,7 @@ int main(int argc, char* argv[]) { } } break; case 't': { - long t = stol(optarg); + long t = std::stol(optarg); if (t<=0 || t > 100) { std::cerr << "Wrong value for the '-t' option, it should be in the range [1,100]\n"; return -1; @@ -320,7 +319,7 @@ int main(int argc, char* argv[]) { app_run_time = t; } break; case 'b': { - buffered_lines = stol(optarg); + buffered_lines = std::stol(optarg); if (buffered_lines<=0 || buffered_lines>10000000) { std::cerr << "Wrong value fro the '-b' option\n"; return -1; @@ -350,10 +349,10 @@ int main(int argc, char* argv[]) { if (parse_dataset_and_create_tuples(file_path)< 0) return -1; - cout << "Executing WordCount with parameters:" << endl; - cout << " * source/splitter : " << source_par_deg << endl; - cout << " * counter/sink : " << sink_par_deg << endl; - cout << " * running time : " << app_run_time << " (s)\n"; + std::cout << "Executing WordCount with parameters:" << endl; + std::cout << " * source/splitter : " << source_par_deg << endl; + std::cout << " * counter/sink : " << sink_par_deg << endl; + std::cout << " * running time : " << app_run_time << " (s)\n"; } /// application starting time @@ -410,15 +409,15 @@ int main(int argc, char* argv[]) { return -1; } volatile unsigned long end_time_main_usecs = current_time_usecs(); - cout << "Exiting" << endl; + std::cout << "Exiting" << endl; double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); - cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; + std::cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; if (DFF_getMyGroup() == "G1") { - cout << "total_lines sent : " << total_lines << "\n"; - cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; + std::cout << "total_lines sent : " << total_lines << "\n"; + std::cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; //double throughput = total_lines / elapsed_time_seconds; //double mbs = (double)((total_bytes / 1048576) / elapsed_time_seconds); - //cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; + //std::cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; } else { size_t words=0; size_t unique=0; @@ -428,8 +427,8 @@ int main(int argc, char* argv[]) { delete S[i]; delete C[i]; } - cout << "words : " << words << "\n"; - cout << "unique : " << unique<< "\n"; + std::cout << "words : " << words << "\n"; + std::cout << "unique : " << unique<< "\n"; } return 0; diff --git a/tests/distributed/runParametricPerf.sh b/tests/distributed/runParametricPerf.sh index 1d67e989..402da7da 100755 --- a/tests/distributed/runParametricPerf.sh +++ b/tests/distributed/runParametricPerf.sh @@ -67,8 +67,9 @@ echo " ] ## #items #byteXitem #execTimeSource #execTimeSink #nw_sx #nw_dx ### -dff_run -V -f tmpFilePP.json $1 $ITEMS $BYTExITEM $EXECTIMESOURCE $EXECTIMESINK $2 $3 $WORKERSXPROCESS +DFF_RUN_HOME=../../ff/distributed/loader +$DFF_RUN_HOME/dff_run -V -f tmpFilePP.json $1 $ITEMS $BYTExITEM $EXECTIMESOURCE $EXECTIMESINK $2 $3 $WORKERSXPROCESS -rm tmpFilePP.json -#exiting justo for testin purpose -exit 0 \ No newline at end of file +#rm tmpFilePP.json +#exiting just for testin purpose +exit 0 diff --git a/tests/distributed/test_parametricPerf.json b/tests/distributed/test_parametricPerf.json index fab97f4a..49a75d52 100644 --- a/tests/distributed/test_parametricPerf.json +++ b/tests/distributed/test_parametricPerf.json @@ -1,13 +1,25 @@ { "groups" : [ - { - "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] - }, - { - "name" : "G2", - "endpoint": "localhost:8005" - } + { + "endpoint" : "localhost:8000", + "name" : "S0", + "OConn" : ["D0", "D1"] + } + , + { + "endpoint" : "localhost:8001", + "name" : "S1", + "OConn" : ["D0", "D1"] + } + , + { + "endpoint" : "localhost:8002", + "name" : "D0" + } + , + { + "endpoint" : "localhost:8003", + "name" : "D1" + } ] } From 593661b99bdb0686bc64d0380adfa0fd535bc121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 22 Nov 2021 12:05:04 +0100 Subject: [PATCH 085/202] Commit before major changes on wrappers. Added new way to handle serialization functions --- ff/distributed/ff_dgroups.hpp | 8 +++- ff/distributed/ff_network.hpp | 11 +++++ ff/distributed/ff_typetraits.hpp | 74 ++++++++++++++++++++++++++++++++ ff/node.hpp | 41 ++++++++++++++++++ tests/distributed/Makefile | 6 +-- 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 ff/distributed/ff_typetraits.hpp diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 6677f5d5..72428471 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -179,9 +179,13 @@ static inline int DFF_Init(int& argc, char**& argv){ dGroups::Instance()->parseConfig(configFile); - if (!groupName.empty()){ + if (!groupName.empty()) dGroups::Instance()->forceProtocol(Proto::TCP); - } + #ifdef DFF_MPI + else + dGroups::Instance()->forceProtocol(Proto::MPI); + #endif + if (dGroups::Instance()->usedProtocol == Proto::TCP){ if (groupName.empty()){ diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index f581b514..c547e133 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -37,14 +37,23 @@ class dataBuffer: public std::stringbuf { dataBuffer() : std::stringbuf(std::ios::in | std::ios::out | std::ios::binary) { } + dataBuffer(char p[], size_t len, bool cleanup=false) : std::stringbuf(std::ios::in | std::ios::out | std::ios::binary), len(len),cleanup(cleanup) { setg(p, p, p + len); } + ~dataBuffer() { if (cleanup) delete [] getPtr(); } + + void setBuffer(char p[], size_t len, bool cleanup=true){ + setg(p, p, p+len); + this->len = len; + this->cleanup = cleanup; + } + size_t getLen() const { if (len>=0) return len; return str().length(); @@ -62,6 +71,8 @@ class dataBuffer: public std::stringbuf { bool cleanup = false; }; +using ffDbuffer = std::pair; + struct SMmessage_t { SMmessage_t(){} SMmessage_t(void* t, int s, int d) : task(t), sender(s), dst(d) {} diff --git a/ff/distributed/ff_typetraits.hpp b/ff/distributed/ff_typetraits.hpp new file mode 100644 index 00000000..255a4453 --- /dev/null +++ b/ff/distributed/ff_typetraits.hpp @@ -0,0 +1,74 @@ +#ifndef FF_TYPETRAITS_H +#define FF_TYPETRAITS_H + +#include + +namespace ff{ + +namespace traits { + +template class Test> +struct exists{ + template + static std::true_type check(Test*); + + template + static std::false_type check(...); + + static constexpr bool value = decltype(check(0))::value; +}; + +template>(std::declval&>(), std::declval()))>>> +struct user_serialize_test{}; + +template>(std::declval&>(), std::declval()))>>> +struct user_deserialize_test{}; + +template, decltype(serializeWrapper(std::declval()))>>> +struct serialize_test{}; + +template(std::declval(), std::declval(), std::declval()))>>> +struct deserialize_test{}; + + + +/* + High level traits to use +*/ + +template +using is_serializable = exists; + +//helper +template +inline constexpr bool is_serializable_v = is_serializable::value; + + +template +using is_deserializable = exists; + +// helper +template +inline constexpr bool is_deserializable_v = is_deserializable::value; + +} + + +/* + Wrapper to user defined serialize and de-serialize functions, in order to exploits user defined functions in other translation units. +*/ +template::value>> +std::pair serializeWrapper(T*in){ + std::pair p; + mYserialize>(p, in); + return p; +} + +template::value>> +void deserializeWrapper(char* c, size_t s, T*& obj){ + mYdeserialize>(std::make_pair(c, s),obj); +} + + +} +#endif diff --git a/ff/node.hpp b/ff/node.hpp index 4458e5cc..fac6786e 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -46,6 +46,17 @@ #include #include +#ifdef DFF_ENABLED + +#include +#include +#include +#include +#include +#include + +#endif + namespace ff { @@ -1233,6 +1244,13 @@ class ff_node { virtual void propagateEOS(void* task=FF_EOS) { (void)task; } +#ifdef DFF_ENABLED + std::function serializeF; + std::function deserializeF; + + bool isSerializable(){ return (bool)serializeF; } + bool isDeserializable(){ return (bool)deserializeF; } +#endif protected: @@ -1523,6 +1541,29 @@ struct ff_node_t: ff_node { EOSW((OUT_t*)FF_EOSW), GO_OUT((OUT_t*)FF_GO_OUT), EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { +#ifdef DFF_ENABLED + + // check on Serialization capabilities on the OUTPUT type! + if constexpr (traits::is_serializable_v){ + this->serializeF = [](void* o, dataBuffer& b) {std::pair p = serializeWrapper(reinterpret_cast(o)); b.setBuffer(p.first, p.second);}; + } else if constexpr (cereal::traits::is_output_serializable::value){ + this->serializeF = [](void* o, dataBuffer& b) -> void { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); delete reinterpret_cast(o);}; + } + + // check on Serialization capabilities on the INPUT type! + if constexpr (traits::is_deserializable_v){ + this->deserializeF = [](dataBuffer& b) -> void* { + IN_t* ptr = nullptr; + b.doNotCleanup(); + deserializeWrapper(b.getPtr(), b.getLen(), ptr); + return ptr; + }; + } else if constexpr(cereal::traits::is_input_serializable::value){ + this->deserializeF = [](dataBuffer& b) -> void* {std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); IN_t* o = new IN_t; ar >> *o; return o;}; + } + +#endif + } OUT_t * const GO_ON, *const EOS, *const EOSW, *const GO_OUT, *const EOS_NOFREEZE; virtual ~ff_node_t() {} diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index a3f19866..b443ecc3 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -23,7 +23,7 @@ # # --------------------------------------------------------------------------- -CXXFLAGS += -std=c++17 +CXXFLAGS += -std=c++20 ifdef DEBUG OPTIMIZE_FLAGS += -g -fno-inline-functions else @@ -45,12 +45,12 @@ endif ifdef FF_HOME INCS += -I$(FF_HOME) else - INCS += -I ~/SPM/fastflow + INCS += -I ~/fastflow endif ifdef CEREAL_HOME INCS += -I$(CEREAL_HOME) else - INCS += -I ~/SPM/cereal + INCS += -I ~/cereal endif From 64fdf26121fcb230801c3732ee82ec2b137a8418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 22 Nov 2021 16:53:54 +0100 Subject: [PATCH 086/202] Changed wrappers with the new way to serialize types. Tested untill test_complete4 included --- ff/distributed/ff_dgroup.hpp | 421 +++----------------------- ff/distributed/ff_dgroups.hpp | 6 +- ff/distributed/ff_dprinter.hpp | 2 +- ff/distributed/ff_wrappers.hpp | 229 +++----------- ff/multinode.hpp | 44 +++ tests/distributed/test_complete1.cpp | 6 +- tests/distributed/test_complete1.json | 1 - tests/distributed/test_complete2.cpp | 2 +- tests/distributed/test_complete3.cpp | 9 +- tests/distributed/test_complete4.cpp | 2 +- 10 files changed, 135 insertions(+), 587 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index e63fe940..ce75f305 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -35,38 +35,18 @@ class MySet { dGroup* group; struct ForwarderNode : ff_node{ + ForwarderNode(std::function f){ + this->serializeF = f; + } + ForwarderNode(std::function f){ + this->deserializeF = f; + } void* svc(void* input){return input;} }; public: MySet(dGroup* group): group(group){ } - template - MySet& operator<<(ff_node_t*) {} - - template - MySet& operator<<(ff_minode_t*) {} - - template - MySet& operator<<(ff_monode_t*) {} - - template - MySet& operator<<=(ff_node_t*) {} - - template - MySet& operator<<=(std::pair*, Function>); - - template - MySet& operator<<=(ff_minode_t*); - - template - MySet& operator<<=(std::pair*, Function>); - - template - MySet& operator<<=(ff_monode_t*); - - template - MySet& operator<<=(std::pair*, Function>); - + MySet& operator<<(ff_node*); bool check_inout(ff_node* node); }; @@ -88,7 +68,7 @@ class dGroup : public ff_farm { * Key: reference to original node * Value: pair of [reference to wrapper, serialization_required] **/ - std::map> in_, out_; + std::map in_, out_; std::map inout_; bool isSource(){return in_.empty() && inout_.empty();} @@ -101,12 +81,12 @@ class dGroup : public ff_farm { return true; } - bool replaceWrapper(const ff::svector& list, std::map>& wrappers_){ + bool replaceWrapper(const ff::svector& list, std::map& wrappers_){ for (ff_node* node : list){ ff_node* parentBB = getBB(this->parentStructure, node); if (parentBB != nullptr){ - ff_node* wrapped = wrappers_[node].first; + ff_node* wrapped = wrappers_[node]; if (parentBB->isPipe()){ reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); @@ -131,7 +111,7 @@ class dGroup : public ff_farm { } ff_node* getOriginal(ff_node* wrapper){ - auto resultI = std::find_if(this->in_.begin(), this->in_.end(), [&](const std::pair> &pair){return pair.second.first == wrapper;}); + auto resultI = std::find_if(this->in_.begin(), this->in_.end(), [&](const std::pair &pair){return pair.second == wrapper;}); if (resultI != this->in_.end()) return resultI->first; auto resultII = std::find_if(this->inout_.begin(), this->inout_.end(), [&](const std::pair &pair){return pair.second == wrapper;}); if (resultII != this->inout_.end()) return resultII->first; @@ -145,12 +125,12 @@ class dGroup : public ff_farm { if (isSeq(bb)){ ff::svector svectBB(1); svectBB.push_back(bb); if (isSource() && this->out_.find(bb) != this->out_.end() && replaceWrapper(svectBB, this->out_)){ - this->add_workers({this->out_[bb].first}); + this->add_workers({this->out_[bb]}); return true; } if (isSink() && this->in_.find(bb) != this->in_.end() && replaceWrapper(svectBB, this->in_)){ - this->add_workers({this->in_[bb].first}); + this->add_workers({this->in_[bb]}); return true; } @@ -379,378 +359,47 @@ class dGroup : public ff_farm { **/ template<> -template -MySet& MySet::operator<<(ff_node_t* node){ - /*if (condizione){ - error("Errore!"); - throw - }*/ - - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(node); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - } else - this->group->in_.insert({node, {new WrapperIN(node, false), true}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<(ff_minode_t* node){ - /*if (condizione){ - error("Errore!"); - throw - }*/ - - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(node); - if (!handle.empty()){// the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); - } else - this->group->in_.insert({node, {new WrapperIN(node, false), true}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<(ff_monode_t* node){ - - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(node); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - } else { - ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true, false); - this->group->in_.insert({node, {combine, true}}); +MySet& MySet::operator<<(ff_node* node){ + if (!node->isDeserializable()){ + error("The annotated node is not able to deserialize the type!\n"); + abort(); } - return *this; -} - -template<> -template -MySet& MySet::operator<<(ff_node_t* node){ - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(node); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); - } else - this->group->out_.insert({node, {new WrapperOUT(node, 1, false, -1), true}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<(ff_minode_t* node){ - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(node); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); - } else { - ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, 1, true, -1), false, true); - this->group->out_.insert({node, {combine, true}}); - } - - return *this; -} - -template<> -template -MySet& MySet::operator<<(ff_monode_t* node){ - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(node); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, false, -1, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); - } else - this->group->out_.insert({node, {new WrapperOUT(node, 1, false, -1), true}}); - - return *this; -} - - -template<> -template -MySet& MySet::operator<<=(ff_node_t* node){ - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(node); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - } else - this->group->in_.insert({node, {new WrapperIN(node, true), false}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ - if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(nodeFun.first); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second)}); - } - } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - } else - this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, true, nodeFun.second), false}}); - - return *this; -} - - -template<> -template -MySet& MySet::operator<<=(ff_minode_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - auto handle = this->group->out_.extract(node); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, nullptr, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); - - } else - this->group->in_.insert({node, {new WrapperIN(node, true), false}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ - if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(nodeFun.first); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second)}); - } - } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getFirst())->getTransform())}); - - } else - this->group->in_.insert({nodeFun.first, {new WrapperIN(nodeFun.first, 1, true, nodeFun.second), false}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<=(ff_monode_t* node){ - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(node); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, nullptr, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - - } else { - ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true), node, true); - this->group->in_.insert({node, {combine, false}}); + if (!this->group->out_.extract(node).empty()) // if the node was also annotedted in output just create the WrapperINOUT + this->group->inout_.emplace(node, new WrapperINOUT(node, 1, false)); + else { + if (node->isMultiOutput()) + this->group->in_.emplace(node, new ff_comb(new WrapperIN(new ForwarderNode(node->deserializeF), true), node, true, false)); + else + this->group->in_.emplace(node, new WrapperIN(node, 1, false)); } return *this; } template<> -template -MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ - if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->out_.extract(nodeFun.first); - if (!handle.empty()){ // the node is edge also in its output - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second)}); - } - } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, nodeFun.second, ((WrapperOUT*)handle.mapped().first)->getTransform())}); - - } else { - ff_comb* combine = new ff_comb(new WrapperIN(new ForwarderNode, true, nodeFun.second), nodeFun.first, true); - this->group->in_.insert({nodeFun.first, {combine, false}}); +MySet& MySet::operator<<(ff_node* node){ + if (!node->isSerializable()){ + error("The annotated node is not able to serialize the type!\n"); + abort(); } - return *this; -} - -template<> -template -MySet& MySet::operator<<=(ff_node_t* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - auto handle = this->group->in_.extract(node); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); - } else - this->group->out_.insert({node, {new WrapperOUT(node, 1, true, -1), false}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ - if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(nodeFun.first); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, -1, nullptr, nodeFun.second)}); - } - } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); - } else - this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, -1, nodeFun.second), false}}); - - return *this; -} - - - -template<> -template -MySet& MySet::operator<<=(ff_minode_t* node){ - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(node); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer())}); - } else { - ff_comb* combine = new ff_comb(node, new WrapperOUT(new ForwarderNode, 1, true, -1), false, true); - this->group->out_.insert({node, {combine, false}}); - } - - return *this; -} - -template<> -template -MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ - if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(nodeFun.first); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, -1, nullptr, nodeFun.second)}); - } - } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, ((WrapperIN*)handle.mapped().first)->getFinalizer(), nodeFun.second)}); - } else { - ff_comb* combine = new ff_comb(nodeFun.first, new WrapperOUT(new ForwarderNode, 1, true, -1, nodeFun.second), false, true); - this->group->out_.insert({nodeFun.first, {combine, false}}); + if (!this->group->in_.extract(node).empty()) // if the node was also annotedted in output just create the WrapperINOUT + this->group->inout_.emplace(node, new WrapperINOUT(node, 1, false)); + else { + if (node->isMultiInput()) + this->group->out_.emplace(node, new ff_comb(node, new WrapperOUT(new ForwarderNode(node->serializeF), 1, true), false, true)); + else + this->group->out_.emplace(node, new WrapperOUT(node, 1, false)); } return *this; } -template<> -template -MySet& MySet::operator<<=(ff_monode_t* node){ - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(node); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({node, (ff_node*) new WrapperINOUT(node, 1, true)}); - } - } else - this->group->inout_.insert({node, (ff_node*)new WrapperINOUT(node, 1, true, -1, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer())}); - } else - this->group->out_.insert({node, {new WrapperOUT(node, 1, true, -1), false}}); - - return *this; -} - -template<> -template -MySet& MySet::operator<<=(std::pair*, Function> nodeFun){ - if (check_inout(nodeFun.first)) return *this; // the node is already processed in input and output, just skip it! - - auto handle = this->group->in_.extract(nodeFun.first); - if (!handle.empty()){ // the node is edge also in its input - if (handle.mapped().second){ - if constexpr (cereal::traits::is_output_serializable::value){ - this->group->inout_.insert({nodeFun.first, (ff_node*) new WrapperINOUT(nodeFun.first, 1, true, -1, nullptr, nodeFun.second)}); - } - } else - this->group->inout_.insert({nodeFun.first, (ff_node*)new WrapperINOUT(nodeFun.first, 1, true, -1, reinterpret_cast*>(reinterpret_cast(handle.mapped().first)->getLast())->getFinalizer(), nodeFun.second)}); - } else - this->group->out_.insert({nodeFun.first, {new WrapperOUT(nodeFun.first, 1, true, -1, nodeFun.second), false}}); - - return *this; -} - template bool MySet::check_inout(ff_node* node){ return this->group->inout_.find(node) != this->group->inout_.end(); diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 72428471..277aab19 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -217,12 +217,10 @@ static inline int DFF_Init(int& argc, char**& argv){ std::cout << "Running group: " << dGroups::Instance()->getRunningGroup() << " on rank: " << myrank << "\n"; } - // set the name for the printer - ff::cout.setPrefix(dGroups::Instance()->getRunningGroup()); - #endif - + // set the name for the printer + ff::cout.setPrefix(dGroups::Instance()->getRunningGroup()); // recompact the argv array int j = 0; diff --git a/ff/distributed/ff_dprinter.hpp b/ff/distributed/ff_dprinter.hpp index 54df727a..0114ab10 100644 --- a/ff/distributed/ff_dprinter.hpp +++ b/ff/distributed/ff_dprinter.hpp @@ -26,7 +26,7 @@ namespace ff { public: prefixbuf(std::string const& p, std::streambuf* sbuf) : prefix(std::string("[" + p + "] ")), sbuf(sbuf) {} - void setPrefix(std::string const& p){prefix = std::string("[" + p + "] ");} + void setPrefix(std::string const& p){this->prefix = std::string("[" + p + "] ");} }; class Printer : private virtual prefixbuf, public std::ostream { diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 56d4591d..35cb051f 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -29,50 +29,19 @@ class Wrapper { /* Wrapper IN class */ -template class WrapperIN: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have public: - using ff_deserialize_F_t = std::function(char*,size_t)>; - - WrapperIN(ff_node_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) { this->n = n; this->cleanup= cleanup; } - - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } - WrapperIN(ff_minode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } - WrapperIN(ff_monode_t* n, int inchannels, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } - - template - WrapperIN(ff_comb_t* n, int inchannels=1, bool cleanup=false, const ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; })): internal_mi_transformer(this, false), inchannels(inchannels), finalizer_(finalizer) {this->n = n; this->cleanup= cleanup; } + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false): internal_mi_transformer(this, false), inchannels(inchannels) {this->n = n; this->cleanup= cleanup;} void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } - Tin* deserialize(dataBuffer& dbuffer) { - Tin* data = nullptr; - if constexpr (Serialization) { - // deserialize the buffer into a heap allocated buffer - - std::istream iss(&dbuffer); - cereal::PortableBinaryInputArchive iarchive(iss); - - data = new Tin; - iarchive >> *data; - - } else { - // deserialize the buffer into a heap allocated buffer - - dbuffer.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - data = finalizer_(dbuffer.getPtr(), dbuffer.getLen()); - } - - return data; - } - int svc_init() { if (this->n->isMultiOutput()) { ff_minode* mi = reinterpret_cast(this->n); @@ -83,31 +52,30 @@ class WrapperIN: public internal_mi_transformer { void svc_end(){this->n->svc_end();} - void * svc(void* in) { + void * svc(void* in) { message_t* msg = (message_t*)in; - int channelid = msg->sender; + if (this->n->isMultiInput()) { + int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); } - Tin* inputData = deserialize(msg->data); + void* inputData = this->n->deserializeF(msg->data); + delete msg; return n->svc(inputData); } ff_node* getOriginal(){ return this->n; } - - ff_deserialize_F_t finalizer_; - ff_deserialize_F_t getFinalizer() {return this->finalizer_;} }; template -struct WrapperINCustom : public WrapperIN { - WrapperINCustom() : WrapperIN(new DummyNode(), 1, true){} +struct WrapperINCustom : public WrapperIN { + WrapperINCustom() : WrapperIN(new DummyNode(), 1, true){} void * svc(void* in) { message_t* msg = (message_t*)in; - auto* out = new SMmessage_t(this->deserialize(msg->data), msg->sender, msg->chid); + auto* out = new SMmessage_t(this->n->deserializeF(msg->data), msg->sender, msg->chid); delete msg; return out; } @@ -117,82 +85,37 @@ struct WrapperINCustom : public WrapperIN { /* Wrapper OUT class */ -template + class WrapperOUT: public Wrapper, public internal_mo_transformer { private: int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; public: - - typedef Tout T_out; - using ff_serialize_F_t = std::function(std::add_pointer_t)>; - - WrapperOUT(ff_node_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup= cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup= cleanup; - registerCallback(ff_send_out_to_cbk, this); //maybe useless?? - - } - WrapperOUT(ff_monode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup= cleanup; - - registerCallback(ff_send_out_to_cbk, this); // maybe useless?? - } - - WrapperOUT(ff_minode_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { - this->n = n; - this->cleanup= cleanup; - registerCallback(ff_send_out_to_cbk, this); - - } - - template - WrapperOUT(ff_comb_t* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1, ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), transform_(transform) { + WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination) { this->n = n; this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); } - bool serialize(Tout* in, int id) { + bool serialize(void* in, int id) { if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); - message_t* msg = nullptr; - - if constexpr (Serialization){ - msg = new message_t; - assert(msg); - msg->sender = this->myID; - msg->chid = id; - std::ostream oss(&msg->data); - cereal::PortableBinaryOutputArchive oarchive(oss); - oarchive << *in; - delete in; - } else { - std::pair raw_data = transform_(in); - - msg = new message_t(raw_data.first, raw_data.second, true); - assert(msg); - msg->sender = this->myID; - msg->chid = id; - } + message_t* msg = new message_t; - return this->ff_send_out(msg); + + this->n->serializeF(in, msg->data); + msg->sender = get_my_id(); // da cambiare con qualcosa di reale! + msg->chid = id; + return this->ff_send_out(msg); } void * svc(void* in) { void* out = n->svc(in); if (out > FF_TAG_MIN) return out; - serialize(reinterpret_cast(out), defaultDestination); + serialize(out, defaultDestination); return GO_ON; } @@ -211,26 +134,23 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { return internal_mo_transformer::run(skip_init); } - ff_serialize_F_t transform_; - ff_serialize_F_t getTransform() { return this->transform_;} - ff::ff_node* getOriginal(){return this->n;} static inline bool ff_send_out_to_cbk(void* task, int id, unsigned long retry, unsigned long ticks, void* obj) { - return ((WrapperOUT*)obj)->serialize(reinterpret_cast(task), id); + return ((WrapperOUT*)obj)->serialize(task, id); } }; -template -struct WrapperOUTCustom : public WrapperOUT { - WrapperOUTCustom() : WrapperOUT(new DummyNode(), 1, true){} +template +struct WrapperOUTCustom : public WrapperOUT { + WrapperOUTCustom() : WrapperOUT(new DummyNode(), 1, true){} void* svc(void* in){ SMmessage_t * input = reinterpret_cast(in); Wrapper::myID = input->sender; - this->serialize(reinterpret_cast(input->task), input->dst); + this->serialize(input->task, input->dst); delete input; return this->GO_ON; } @@ -240,7 +160,6 @@ struct WrapperOUTCustom : public WrapperOUT { /* Wrapper INOUT class */ -template class WrapperINOUT: public Wrapper, public internal_mi_transformer { private: @@ -250,30 +169,7 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { public: - typedef Tout T_out; - using ff_serialize_F_t = std::function(std::add_pointer_t)>; - using ff_deserialize_F_t = std::function(char*,size_t)>; - - WrapperINOUT(ff_node_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - - WrapperINOUT(ff_minode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - - WrapperINOUT(ff_monode_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - - template - WrapperINOUT(ff_comb_t* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1, ff_deserialize_F_t finalizer=([](char* p, size_t){ return new (p) Tin; }), ff_serialize_F_t transform=([](Tout* in){return std::make_pair((char*)in, sizeof(Tout));})): Wrapper(), internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), finalizer_(finalizer), transform_(transform){ + WrapperINOUT(ff_node* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -283,54 +179,17 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { internal_mi_transformer::registerCallback(cb,arg); } - - Tin* deserialize(void* buffer) { - message_t* msg = (message_t*)buffer; - Tin* data = nullptr; - if constexpr (SerializationIN) { - // deserialize the buffer into a heap allocated buffer - - std::istream iss(&msg->data); - cereal::PortableBinaryInputArchive iarchive(iss); - - data = new Tin; - iarchive >> *data; - - } else { - // deserialize the buffer into a heap allocated buffer - - msg->data.doNotCleanup(); // to not delete the area of memory during the delete of the message_t wrapper - data = finalizer_(msg->data.getPtr(), msg->data.getLen()); - } - - delete msg; - return data; - } - - bool serialize(Tout* in, int id) { - if ((void*)in > FF_TAG_MIN) return ff_node::ff_send_out(in); + bool serialize(void* in, int id) { + if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); - message_t* msg = nullptr; - - if constexpr (SerializationOUT){ - msg = new message_t; - assert(msg); - msg->sender = this->myID; - msg->chid = id; - std::ostream oss(&msg->data); - cereal::PortableBinaryOutputArchive oarchive(oss); - oarchive << *in; - delete in; - } else { - std::pair raw_data = transform_(in); - - msg = new message_t(raw_data.first, raw_data.second, true); - assert(msg); - msg->sender = this->myID; - msg->chid = id; - } + message_t* msg = new message_t; + + + this->n->serializeF(in, msg->data); + msg->sender = get_my_id(); // da cambiare con qualcosa di reale! + msg->chid = id; - return ff_node::ff_send_out(msg); + return this->ff_send_out(msg); } int svc_init() { @@ -344,16 +203,20 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { void svc_end(){this->n->svc_end();} void * svc(void* in) { - message_t* msg = (message_t*)in; - int channelid = msg->sender; + message_t* msg = (message_t*)in; + if (this->n->isMultiInput()) { + int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); - } + } + + void* out = n->svc(this->n->deserializeF(msg->data)); + delete msg; - void* out = n->svc(deserialize(in)); if (out > FF_TAG_MIN) return out; - serialize(reinterpret_cast(out), defaultDestination); + + serialize(out, defaultDestination); return GO_ON; } @@ -369,16 +232,12 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { ff_node::set_output_blocking(m,c,canoverwrite); } - - ff_deserialize_F_t finalizer_; - std::function(Tout*)> transform_; - ff_node* getOriginal(){ return this->n; } static inline bool ff_send_out_to_cbk(void* task, int id, unsigned long retry, unsigned long ticks, void* obj) { - return ((WrapperINOUT*)obj)->serialize(reinterpret_cast(task), id); + return ((WrapperINOUT*)obj)->serialize(task, id); } }; diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 23b7e4bb..0fd873f7 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -823,6 +823,28 @@ struct ff_minode_t: ff_minode { EOS((OUT_t*)FF_EOS),EOSW((OUT_t*)FF_EOSW), GO_OUT((OUT_t*)FF_GO_OUT), EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { +#ifdef DFF_ENABLED + + // check on Serialization capabilities on the OUTPUT type! + if constexpr (traits::is_serializable_v){ + this->serializeF = [](void* o, dataBuffer& b) {std::pair p = serializeWrapper(reinterpret_cast(o)); b.setBuffer(p.first, p.second);}; + } else if constexpr (cereal::traits::is_output_serializable::value){ + this->serializeF = [](void* o, dataBuffer& b) -> void { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); delete reinterpret_cast(o);}; + } + + // check on Serialization capabilities on the INPUT type! + if constexpr (traits::is_deserializable_v){ + this->deserializeF = [](dataBuffer& b) -> void* { + IN_t* ptr = nullptr; + b.doNotCleanup(); + deserializeWrapper(b.getPtr(), b.getLen(), ptr); + return ptr; + }; + } else if constexpr(cereal::traits::is_input_serializable::value){ + this->deserializeF = [](dataBuffer& b) -> void* {std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); IN_t* o = new IN_t; ar >> *o; return o;}; + } + +#endif } OUT_t * const GO_ON, *const EOS, *const EOSW, *const GO_OUT, *const EOS_NOFREEZE; virtual ~ff_minode_t() {} @@ -861,6 +883,28 @@ struct ff_monode_t: ff_monode { EOS((OUT_t*)FF_EOS),EOSW((OUT_t*)FF_EOSW), GO_OUT((OUT_t*)FF_GO_OUT), EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { +#ifdef DFF_ENABLED + + // check on Serialization capabilities on the OUTPUT type! + if constexpr (traits::is_serializable_v){ + this->serializeF = [](void* o, dataBuffer& b) {std::pair p = serializeWrapper(reinterpret_cast(o)); b.setBuffer(p.first, p.second);}; + } else if constexpr (cereal::traits::is_output_serializable::value){ + this->serializeF = [](void* o, dataBuffer& b) -> void { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); delete reinterpret_cast(o);}; + } + + // check on Serialization capabilities on the INPUT type! + if constexpr (traits::is_deserializable_v){ + this->deserializeF = [](dataBuffer& b) -> void* { + IN_t* ptr = nullptr; + b.doNotCleanup(); + deserializeWrapper(b.getPtr(), b.getLen(), ptr); + return ptr; + }; + } else if constexpr(cereal::traits::is_input_serializable::value){ + this->deserializeF = [](dataBuffer& b) -> void* {std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); IN_t* o = new IN_t; ar >> *o; return o;}; + } + +#endif } OUT_t * const GO_ON, *const EOS, *const EOSW, *const GO_OUT, *const EOS_NOFREEZE; virtual ~ff_monode_t() {} diff --git a/tests/distributed/test_complete1.cpp b/tests/distributed/test_complete1.cpp index 5e49da1c..f219c833 100644 --- a/tests/distributed/test_complete1.cpp +++ b/tests/distributed/test_complete1.cpp @@ -37,7 +37,7 @@ struct MoNode : ff::ff_monode_t{ void svc_end(){ const std::lock_guard lock(mtx); - std::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + ff::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; } }; @@ -50,7 +50,7 @@ struct MiNode : ff::ff_minode_t{ void svc_end(){ const std::lock_guard lock(mtx); - std::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + ff::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; } }; @@ -66,7 +66,7 @@ struct Sink : ff::ff_minode_t{ int local_sum = 0; for(int i = 0; i < ITEMS; i++) local_sum += i; const std::lock_guard lock(mtx); - std::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; + ff::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; } }; diff --git a/tests/distributed/test_complete1.json b/tests/distributed/test_complete1.json index 63d2e24d..beae1ddf 100644 --- a/tests/distributed/test_complete1.json +++ b/tests/distributed/test_complete1.json @@ -1,5 +1,4 @@ { - "protocol" : "MPI", "groups" : [ { "endpoint" : "localhost:8004", diff --git a/tests/distributed/test_complete2.cpp b/tests/distributed/test_complete2.cpp index e2db74d6..1924b797 100644 --- a/tests/distributed/test_complete2.cpp +++ b/tests/distributed/test_complete2.cpp @@ -74,7 +74,7 @@ struct Node4: ff_minode_t{ if (processed != ntasks) { abort(); } - std::cout << "RESULT OK\n"; + ff::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_complete3.cpp b/tests/distributed/test_complete3.cpp index 75214ea7..ed52a3e8 100644 --- a/tests/distributed/test_complete3.cpp +++ b/tests/distributed/test_complete3.cpp @@ -60,8 +60,8 @@ struct Node3: ff_monode_t{ return t; } void eosnotify(ssize_t) { - printf("Node3 %ld EOS RECEIVED\n", get_my_id()); - fflush(NULL); + + ff::cout << "Node3 " << get_my_id() << " EOS RECEIVED\n"; } }; @@ -75,15 +75,14 @@ struct Node4: ff_minode_t{ return GO_ON; } void eosnotify(ssize_t id) { - printf("Node4 EOS RECEIVED from %ld\n", id); - fflush(NULL); + ff::cout << "Node4 EOS RECEIVED from " << id << ff::endl; } void svc_end() { if (processed != ntasks) { abort(); } - std::cout << "RESULT OK\n"; + ff::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_complete4.cpp b/tests/distributed/test_complete4.cpp index 7feb2766..24c2c4ec 100644 --- a/tests/distributed/test_complete4.cpp +++ b/tests/distributed/test_complete4.cpp @@ -76,7 +76,7 @@ struct Node4: ff_minode_t{ if (processed != ntasks) { abort(); } - std::cout << "RESULT OK\n"; + ff::cout << "RESULT OK\n"; } long ntasks; long processed=0; From 4f66cf3c8b073f300242337a282cc51aca5bd79c Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 22 Nov 2021 18:56:14 +0100 Subject: [PATCH 087/202] removed volatile warnings when compiling with std=c++20 --- ff/buffer.hpp | 12 +++++++----- ff/dynqueue.hpp | 6 ++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ff/buffer.hpp b/ff/buffer.hpp index f2642080..9750fb20 100644 --- a/ff/buffer.hpp +++ b/ff/buffer.hpp @@ -233,7 +233,7 @@ class SWSR_Ptr_Buffer { WMB(); //std::atomic_thread_fence(std::memory_order_release); buf[pwrite] = data; - pwrite += (pwrite+1 >= size) ? (1-size): 1; // circular buffer + pwrite = pwrite + (pwrite+1 >= size) ? (1-size): 1; // circular buffer return true; } return false; @@ -312,7 +312,7 @@ class SWSR_Ptr_Buffer { */ inline bool inc() { buf[pread]=NULL; - pread += (pread+1 >= size) ? (1-size): 1; // circular buffer + pread = pread + (pread+1 >= size) ? (1-size): 1; // circular buffer return true; } @@ -352,7 +352,8 @@ class SWSR_Ptr_Buffer { pwrite = longxCacheLine-1; pread = longxCacheLine-1; } else { - pread=pwrite=0; + pread=0; + pwrite=0; } #if defined(SWSR_MULTIPUSH) mcnt = 0; @@ -487,7 +488,7 @@ class Lamport_Buffer { if (empty()) return false; *data = buf[pread]; - pread += (pread+1 >= size) ? (1-size): 1; + pread = pread + (pread+1 >= size) ? (1-size): 1; return true; } @@ -495,7 +496,8 @@ class Lamport_Buffer { * TODO */ inline void reset() { - pread=pwrite=0; + pread=0; + pwrite=0; if (size<=512) for(unsigned long i=0;idata = NULL; n->next = NULL; - head=tail=n; + head=n; + tail=n; cache.init(); if (fillcache) { for(int i=0;idata = NULL; n->next = NULL; - head=tail=n; + head=n; + tail=n; cache=(void**)getAlignedMemory(longxCacheLine*sizeof(long),cachesize*sizeof(void*)); if (!cache) { From 4f3190a6d5e12a50f1ed2668fa8863cfde164f7b Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 22 Nov 2021 19:23:37 +0100 Subject: [PATCH 088/202] missing code parts of previous fix related to volatile warnings for std=c++20 --- ff/buffer.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ff/buffer.hpp b/ff/buffer.hpp index 9750fb20..fa12e989 100644 --- a/ff/buffer.hpp +++ b/ff/buffer.hpp @@ -233,7 +233,7 @@ class SWSR_Ptr_Buffer { WMB(); //std::atomic_thread_fence(std::memory_order_release); buf[pwrite] = data; - pwrite = pwrite + (pwrite+1 >= size) ? (1-size): 1; // circular buffer + pwrite = pwrite + ((pwrite+1 >= size) ? (1-size): 1); // circular buffer return true; } return false; @@ -263,7 +263,7 @@ class SWSR_Ptr_Buffer { buf[pwrite+i] = data[i]; WMB(); - pwrite = (last+1 >= size) ? 0 : (last+1); + pwrite = pwrite + ((last+1 >= size) ? 0 : (last+1)); #if defined(SWSR_MULTIPUSH) mcnt = 0; // reset mpush counter #endif @@ -312,7 +312,7 @@ class SWSR_Ptr_Buffer { */ inline bool inc() { buf[pread]=NULL; - pread = pread + (pread+1 >= size) ? (1-size): 1; // circular buffer + pread = pread + ((pread+1 >= size) ? (1-size): 1); // circular buffer return true; } @@ -488,7 +488,7 @@ class Lamport_Buffer { if (empty()) return false; *data = buf[pread]; - pread = pread + (pread+1 >= size) ? (1-size): 1; + pread = pread + ((pread+1 >= size) ? (1-size): 1); return true; } From 51bf3074276e999037c830a74701ecea8f47583f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 22 Nov 2021 22:32:49 +0100 Subject: [PATCH 089/202] Fixed bug on the new version of WrapperINOUT. Some minor changes on test_complete7-8 --- ff/distributed/ff_wrappers.hpp | 14 +-- tests/distributed/test_complete5.json | 2 +- tests/distributed/test_complete7.cpp | 4 +- tests/distributed/test_complete8.cpp | 2 +- tests/distributed/test_wrapper2.cpp | 144 -------------------------- 5 files changed, 9 insertions(+), 157 deletions(-) delete mode 100644 tests/distributed/test_wrapper2.cpp diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 35cb051f..1fc0d2bb 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -113,8 +113,7 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { } void * svc(void* in) { - void* out = n->svc(in); - if (out > FF_TAG_MIN) return out; + void* out = n->svc(in); serialize(out, defaultDestination); return GO_ON; } @@ -180,7 +179,7 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { } bool serialize(void* in, int id) { - if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); + if ((void*)in > FF_TAG_MIN) return ff_node::ff_send_out(in); message_t* msg = new message_t; @@ -189,12 +188,12 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { msg->sender = get_my_id(); // da cambiare con qualcosa di reale! msg->chid = id; - return this->ff_send_out(msg); + return ff_node::ff_send_out(msg); } int svc_init() { - if (this->n->isMultiOutput()) { - ff_minode* mi = reinterpret_cast(this->n); + if (this->n->isMultiOutput()) { // ??? what?? + ff_minode* mi = reinterpret_cast(this->n); // what????? mi->set_running(inchannels); } return n->svc_init(); @@ -210,12 +209,9 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); } - void* out = n->svc(this->n->deserializeF(msg->data)); delete msg; - if (out > FF_TAG_MIN) return out; - serialize(out, defaultDestination); return GO_ON; } diff --git a/tests/distributed/test_complete5.json b/tests/distributed/test_complete5.json index 52be0e32..30557b8d 100644 --- a/tests/distributed/test_complete5.json +++ b/tests/distributed/test_complete5.json @@ -1,5 +1,5 @@ { - "protocol" : "MPI", + "protocol" : "TCP", "groups" : [ { "endpoint" : "localhost:8004", diff --git a/tests/distributed/test_complete7.cpp b/tests/distributed/test_complete7.cpp index 0df142b4..93eb7c53 100644 --- a/tests/distributed/test_complete7.cpp +++ b/tests/distributed/test_complete7.cpp @@ -41,7 +41,7 @@ struct Node1: ff_monode_t{ task->S.f = i*1.0; ff_send_out(task); } - std::cout << "Source exiting!" << std::endl; + ff::cout << "Source exiting!" << std::endl; return EOS; } const long ntasks; @@ -77,7 +77,7 @@ struct Node4: ff_minode_t{ if (processed != ntasks) { abort(); } - std::cout << "RESULT OK\n"; + ff::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_complete8.cpp b/tests/distributed/test_complete8.cpp index 9c13fd81..a92d904e 100644 --- a/tests/distributed/test_complete8.cpp +++ b/tests/distributed/test_complete8.cpp @@ -75,7 +75,7 @@ struct Node4: ff_node_t{ if (processed != ntasks) { abort(); } - std::cout << "RESULT OK\n"; + ff::cout << "RESULT OK\n"; } long ntasks; long processed=0; diff --git a/tests/distributed/test_wrapper2.cpp b/tests/distributed/test_wrapper2.cpp deleted file mode 100644 index 7a879269..00000000 --- a/tests/distributed/test_wrapper2.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * | -> Node2 ->| | -> Node3->Helper ->| - * Node1-->| | -->| | --> Node4 - * | -> Node2 ->| | -> Node3->Helper ->| - * - * /<-- combine -->/ - * /<-------------- a2a ---------------->/ - * /<------------------------ pipe ------------------------>/ - * - * - * Node2 uses the WrapperOUT - * Node3 uses the WrapperIN - * Helper uses the WrapperOUT - * Node4 uses the WrapperIN - */ - -#include -#include -#include - -using namespace ff; - -struct myTask_t { - std::string str; - struct S_t { - long t; - float f; - } S; - - template - void serialize(Archive & archive) { - archive(str, S.t, S.f); - } - -}; - - -struct Node1: ff_monode_t{ - Node1(long ntasks):ntasks(ntasks) {} - myTask_t* svc(myTask_t*){ - for(long i=0; i< ntasks; i++) { - myTask_t* task = new myTask_t; - task->str="Hello"; - task->S.t = i; - task->S.f = i*1.0; - ff_send_out(task); - } - return EOS; - } - const long ntasks; -}; - -struct Node2: ff_monode_t{ - myTask_t* svc(myTask_t* t){ - t->str += std::string(" World"); - //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; - - int dest = ++cnt % get_num_outchannels(); - ff_send_out_to(t, dest); - - return GO_ON; - } - size_t cnt=0; -}; - -struct Node3: ff_minode_t{ - myTask_t* svc(myTask_t* t){ - static bool even=true; - std::cout << "Node3" << get_my_id()+1 << ": from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; - - t->S.t += 1; - t->S.f += 1.0; - if (even) { - ff_send_out(t); - even=false; - return GO_ON; - } - even=true; - return t; - } -}; - -struct Helper: ff_node_t { - myTask_t* svc(myTask_t* t) { return t;} -}; - -struct Node4: ff_minode_t{ - Node4(long ntasks):ntasks(ntasks) {} - myTask_t* svc(myTask_t* t){ - std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; - ++processed; - delete t; - return GO_ON; - } - void svc_end() { - if (processed != ntasks) { - abort(); - } - std::cout << "RESULT OK\n"; - } - long ntasks; - long processed=0; -}; - - -int main(int argc, char*argv[]){ - long ntasks = 1000; - if (argc>1) { - ntasks = std::stol(argv[1]); - } - - ff_pipeline pipe; - Node1 n1(ntasks); - ff_a2a a2a; - Node2 n21, n22; - Node3 n31, n32; - Node4 n4(ntasks); - - pipe.add_stage(&n1); - std::vector L; - std::vector R; - L.push_back(new WrapperOUT(&n21, 2)); - L.push_back(new WrapperOUT(&n22, 2)); - a2a.add_firstset(L, 0 ,true); - - ff_comb comb1(new WrapperIN(&n31), - new WrapperOUT(new Helper,1,true), - true,true); - ff_comb comb2(new WrapperIN(&n32), - new WrapperOUT(new Helper,1,true), - true,true); - - R.push_back(&comb1); - R.push_back(&comb2); - a2a.add_secondset(R); - pipe.add_stage(&a2a); - pipe.add_stage(new WrapperIN(&n4), true); - - if (pipe.run_and_wait_end()<0) { - error("running the main pipe\n"); - return -1; - } - return 0; -} From e5c8f582c8a7884768b5d675d22baebf14c907a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Tue, 23 Nov 2021 11:35:57 +0100 Subject: [PATCH 090/202] Changed custom serialization function name. parametricPerf and Wordcount tests complaint with the new serialization mechanisms --- ff/distributed/ff_dgroup.hpp | 22 -------------- ff/distributed/ff_typetraits.hpp | 8 +++--- tests/distributed/dwordcount/dwordcount.cpp | 16 ++++++++--- tests/distributed/test_parametricPerf.cpp | 32 +++++++++++---------- 4 files changed, 33 insertions(+), 45 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index ce75f305..f2de07f2 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -352,12 +352,6 @@ class dGroup : public ff_farm { MySet out; }; - -/** - * If the user uses << operator -> serialization is used - * If the user uses <<= operator -> NO serialization is used - **/ - template<> MySet& MySet::operator<<(ff_node* node){ if (!node->isDeserializable()){ @@ -480,20 +474,4 @@ ff::dGroup& ff_pipeline::createGroup(std::string name){ dGroup * g = new dGroup(this, std::move(name)); return *g; } - -// utility functions useful for creating suitable pairs to be used -// when defining custom serialization/deserialization functions -template -static inline std::pair*, Function> packup(ff_node_t* node, Function f){ - return std::make_pair(node, f); -} -template -static inline std::pair*, Function> packup(ff_minode_t* node, Function f){ - return std::make_pair(node, f); -} -template -static inline std::pair*, Function> packup(ff_monode_t* node, Function f){ - return std::make_pair(node, f); -} - #endif diff --git a/ff/distributed/ff_typetraits.hpp b/ff/distributed/ff_typetraits.hpp index 255a4453..eb0ab73e 100644 --- a/ff/distributed/ff_typetraits.hpp +++ b/ff/distributed/ff_typetraits.hpp @@ -18,10 +18,10 @@ struct exists{ static constexpr bool value = decltype(check(0))::value; }; -template>(std::declval&>(), std::declval()))>>> +template>(std::declval&>(), std::declval()))>>> struct user_serialize_test{}; -template>(std::declval&>(), std::declval()))>>> +template>(std::declval&>(), std::declval()))>>> struct user_deserialize_test{}; template, decltype(serializeWrapper(std::declval()))>>> @@ -60,13 +60,13 @@ inline constexpr bool is_deserializable_v = is_deserializable::value; template::value>> std::pair serializeWrapper(T*in){ std::pair p; - mYserialize>(p, in); + serialize>(p, in); return p; } template::value>> void deserializeWrapper(char* c, size_t s, T*& obj){ - mYdeserialize>(std::make_pair(c, s),obj); + deserialize>(std::make_pair(c, s),obj); } diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index 738035fb..f07c8f5e 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -81,6 +81,16 @@ struct result_t { uint64_t ts; // timestamp }; +template +void serialize(Buffer&b, result_t* input){ + b = {reinterpret_cast(input), sizeof(result_t)}; +} + +template +void deserialize(const Buffer&b, result_t*& strPtr){ + strPtr = reinterpret_cast(b.first); +} + std::vector dataset; // contains all the input tuples in memory std::atomic total_lines=0; // total number of lines processed by the system std::atomic total_bytes=0; // total number of bytes processed by the system @@ -329,8 +339,7 @@ int main(int argc, char* argv[]) { pipe0->add_stage(sp); L.push_back(pipe0); - // using the default serialization since the result_t is contiguous in memory - G1.out <<= sp; + G1.out << sp; } for (size_t i=0;iadd_stage(S[i]); R.push_back(pipe1); - // using the default deserialization - G2.in <<= C[i]; + G2.in << C[i]; } a2a.add_firstset(L, 0, true); diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 6a3289cb..89b3530f 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -68,6 +68,21 @@ struct ExcType { }; + +#ifdef MANUAL_SERIALIZATION +template +void serialize(Buffer&b, ExcType* input){ + b = {(char*)input, input->clen+sizeof(ExcType)}; +} + +template +void deserialize(const Buffer&b, ExcType*& Ptr){ + ExcType* p = new (b.first) ExcType(true); + p->clen = b.second - sizeof(ExcType); + Ptr = p; +} +#endif + static ExcType* allocateExcType(size_t size, bool setdata=false) { char* _p = (char*)malloc(size+sizeof(ExcType)); ExcType* p = new (_p) ExcType(true); // contiguous allocation @@ -180,27 +195,14 @@ int main(int argc, char*argv[]){ for(int i = 0; i < numProcSx; i++){ auto& g = a2a.createGroup(std::string("S")+std::to_string(i)); for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ - #if defined(MANUAL_SERIALIZATION) - g.out <<= packup(sxWorkers[j], [](ExcType* in) -> std::pair {return std::make_pair((char*)in, in->clen+sizeof(ExcType)); }); - #else - g.out << sxWorkers[j]; - #endif + g.out << sxWorkers[j]; } } for(int i = 0; i < numProcDx; i++){ auto& g = a2a.createGroup(std::string("D")+std::to_string(i)); for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ - #if defined(MANUAL_SERIALIZATION) - g.in <<= packup(dxWorkers[j], [](char* in, size_t len) -> ExcType* { - ExcType* p = new (in) ExcType(true); - p->C = in + sizeof(ExcType); - p->clen = len - sizeof(ExcType); - return p; - }); - #else - g.in << dxWorkers[j]; - #endif + g.in << dxWorkers[j]; } } From 18b23f59ca7279c08e7c4cd441a81d6ec631a372 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 24 Nov 2021 19:25:12 +0100 Subject: [PATCH 091/202] minor modification to remove some memory leaks --- ff/distributed/ff_dgroup.hpp | 20 ++++++++++-- ff/distributed/ff_dgroups.hpp | 13 +++++--- tests/distributed/dwordcount/dwordcount.cpp | 32 +++++++++++++++----- tests/distributed/dwordcount/dwordcountb.cpp | 3 ++ 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f2de07f2..bc04539c 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -63,7 +63,7 @@ class dGroup : public ff_farm { ff_endpoint endpoint; std::vector destinations; int expectedInputConnections; - + bool executed = false; /** * Key: reference to original node * Value: pair of [reference to wrapper, serialization_required] @@ -296,6 +296,7 @@ class dGroup : public ff_farm { } #endif + this->cleanup_emitter(); } // create sender if (!isSink()){ @@ -315,7 +316,7 @@ class dGroup : public ff_farm { this->add_collector(new ff_dsenderMPI(this->destinations), true); } #endif - + } return 0; @@ -330,11 +331,24 @@ class dGroup : public ff_farm { dGroups::Instance()->addGroup(label, this); } + ~dGroup() { + if (!executed) { + for(auto s: in_) + delete s.second; + for(auto s: out_) + delete s.second; + for(auto s: inout_) + delete s.second; + } + } + int run(bool skip_init=false) override {return 0;} int run(ff_node* baseBB, bool skip_init=false) override { buildFarm(reinterpret_cast(baseBB)); - return ff_farm::run(skip_init); + auto r= ff_farm::run(skip_init); + executed = true; + return r; } diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 277aab19..7cc0a75e 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -32,11 +32,16 @@ class dGroups { public: static dGroups* Instance(){ - if (i == nullptr) - i = new dGroups(); - return i; + static dGroups dg; + return &dg; } - + ~dGroups() { + for (auto g : groups) + if (g.second) + delete g.second; + groups.clear(); + } + Proto usedProtocol; void parseConfig(std::string); diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index f07c8f5e..14fdf6bc 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -50,6 +50,8 @@ */ #define FF_BOUNDED_BUFFER +//#define MAKE_VALGRIND_HAPPY +//#define MANUAL_SERIALIZATION #define DEFAULT_BUFFER_CAPACITY 2048 #define BYKEY true @@ -79,8 +81,15 @@ struct result_t { char key[MAXWORD]; // key word uint64_t id; // id that indicates the current number of occurrences of the key word uint64_t ts; // timestamp + + template + void serialize(Archive & archive) { + archive(key,id,ts); + } + }; +#if defined(MANUAL_SERIALIZATION) template void serialize(Buffer&b, result_t* input){ b = {reinterpret_cast(input), sizeof(result_t)}; @@ -90,6 +99,7 @@ template void deserialize(const Buffer&b, result_t*& strPtr){ strPtr = reinterpret_cast(b.first); } +#endif std::vector dataset; // contains all the input tuples in memory std::atomic total_lines=0; // total number of lines processed by the system @@ -166,10 +176,14 @@ struct Splitter: ff_monode_t { #endif result_t* r = new result_t; assert(r); +#if defined(MAKE_VALGRIND_HAPPY) + bzero(r->key, MAXWORD); +#endif strncpy(r->key, token, MAXWORD-1); r->key[MAXWORD-1]='\0'; r->ts = in->ts; - + r->id = in->id; + ff_send_out_to(r, ch); token = strtok_r(NULL, " ", &tmpstr); } @@ -326,20 +340,21 @@ int main(int argc, char* argv[]) { std::vector L; // left and right workers of the A2A std::vector R; + ff_a2a a2a(false, qlen, qlen, true); auto G1 = a2a.createGroup("G1"); auto G2 = a2a.createGroup("G2"); - + for (size_t i=0;iadd_stage(new Source(app_start_time)); + pipe0->add_stage(new Source(app_start_time), true); Splitter* sp = new Splitter(sink_par_deg); - pipe0->add_stage(sp); + pipe0->add_stage(sp, true); L.push_back(pipe0); - G1.out << sp; + G1.out << sp; } for (size_t i=0;iwords; unique+= C[i]->unique(); - delete S[i]; - delete C[i]; } std::cout << "words : " << words << "\n"; std::cout << "unique : " << unique<< "\n"; } + for(size_t i=0;i { outV[ch] = r; } result_t r; +#if defined(MAKE_VALGRIND_HAPPY) + bzero(r.key, MAXWORD); +#endif strncpy(r.key, token, MAXWORD-1); r.key[MAXWORD-1]='\0'; r.ts = in->ts; From 09c5bfaa8243b9582318f5874acbe01907129dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Fri, 10 Dec 2021 11:23:30 +0100 Subject: [PATCH 092/202] First bugged version of the new horizontal a2a split --- ff/distributed/ff_dadapters.hpp | 130 +++++++---------------------- ff/distributed/ff_dgroup.hpp | 4 +- ff/distributed/ff_dgroups.hpp | 4 +- ff/distributed/ff_dreceiver.hpp | 131 ++++++++++++++++++++---------- ff/distributed/ff_dsender.hpp | 131 ++++++++++++++++++++++++++---- ff/distributed/ff_network.hpp | 9 +- tests/distributed/test_a2a_h1.cpp | 39 ++++++--- 7 files changed, 266 insertions(+), 182 deletions(-) diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index 650e54d2..e0db7b9c 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -13,95 +13,49 @@ #define FARM_GATEWAY -10 using namespace ff; -template -struct TaskWrapper { - T* task; - int destination; - TaskWrapper(T* task, int d) : task(task), destination(d) {} -}; - -template -struct ResultWrapper { - T* result; - int source; - ResultWrapper(T* result, int s): result(result), source(s) {} -}; -template -class SquareBoxEmitter : public ff_minode_t, SMmessage_t> { - std::vector sources; - int neos = 0; -public: - SquareBoxEmitter(const std::vector localSources) : sources(localSources) {} - - SMmessage_t* svc(TaskWrapper* in) { - size_t chId = ff_minode::get_channel_id(); - SMmessage_t* o = new SMmessage_t(in->task, chId < sources.size() ? sources[chId] : -1 , -100 - in->destination); - delete in; - return o; - } +class SquareBoxRight : public ff_minode { + ssize_t neos = 0; +public: + void* svc(void* in) {return in;} void eosnotify(ssize_t id) { - if (id == (ssize_t)sources.size()) return; // EOS coming from the SquareBoxCollector, we must ignore it - if (++neos == (int)sources.size()){ + if (id == this->get_num_inchannels() - 1) return; // EOS coming from the SquareLeft, we must ignore it + if (++neos == (this->get_num_inchannels() - 1)) this->ff_send_out(this->EOS); - } } }; -template -class SquareBoxCollector : public ff_monode_t> { +class SquareBoxLeft : public ff_monode { std::unordered_map destinations; public: - SquareBoxCollector(const std::unordered_map localDestinations) : destinations(localDestinations) {} + SquareBoxLeft(const std::unordered_map localDestinations) : destinations(localDestinations) {} - ResultWrapper* svc(SMmessage_t* in){ - this->ff_send_out_to(new ResultWrapper(reinterpret_cast(in->task), in->sender), destinations[in->dst]); - delete in; return this->GO_ON; + void* svc(void* in){ + this->ff_send_out_to(in, destinations[reinterpret_cast(in)->chid]); + return this->GO_ON; } }; -template class EmitterAdapter: public internal_mo_transformer { private: - int totalWorkers; + int totalWorkers, index; std::unordered_map localWorkersMap; int nextDestination; public: - - typedef Tout T_out; - - EmitterAdapter(ff_node_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - - EmitterAdapter(ff_node* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { + /** Parameters: + * - n: rightmost sequential node of the builiding block representing the left-set worker + * - totalWorkers: number of the workers on the right set (i.e., all the possible destinations) of the original entire a2a + * - index: index of nodde n in the output list of the left set of the orgiginal entire a2a + * - localWorkers: list of pairs where logical_destination + * - cleanup + **/ + EmitterAdapter(ff_node* n, int totalWorkers, int index, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), index(index), localWorkersMap(localWorkers) { this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - EmitterAdapter(ff_monode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - - EmitterAdapter(ff_minode_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - - template - EmitterAdapter(ff_comb_t* n, int totalWorkers, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), localWorkersMap(localWorkers) { - this->n = n; - this->cleanup = cleanup; - registerCallback(ff_send_out_to_cbk, this); - } - void * svc(void* in) { void* out = n->svc(in); if (out > FF_TAG_MIN) return out; @@ -117,16 +71,14 @@ class EmitterAdapter: public internal_mo_transformer { nextDestination = (nextDestination + 1) % totalWorkers; } - - auto pyshicalDestination = localWorkersMap.find(destination); if (pyshicalDestination != localWorkersMap.end()) { - ff_send_out_to(task, pyshicalDestination->second); + return ff_send_out_to(task, pyshicalDestination->second); } else { - ff_send_out_to(new TaskWrapper(reinterpret_cast(task), destination), localWorkersMap.size()); + message_t* msg = new message_t(index, destination); + this->n->serializeF(task, msg->data); + return ff_send_out_to(msg, localWorkersMap.size()); } - - return true; } int svc_init() { @@ -153,39 +105,17 @@ class EmitterAdapter: public internal_mo_transformer { }; -template class CollectorAdapter: public internal_mi_transformer { private: std::vector localWorkers; public: - CollectorAdapter(ff_node_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { - this->n = n; - this->cleanup = cleanup; - } - CollectorAdapter(ff_node* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; } - CollectorAdapter(ff_minode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { - this->n = n; - this->cleanup = cleanup; - } - - CollectorAdapter(ff_monode_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { - this->n = n; - this->cleanup = cleanup; - } - - template - CollectorAdapter(ff_comb_t* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { - this->n = n; - this->cleanup = cleanup; - } - void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } @@ -201,17 +131,15 @@ class CollectorAdapter: public internal_mi_transformer { void svc_end(){this->n->svc_end();} void * svc(void* in) { - Tin * task; ssize_t channel; // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from if ((size_t)get_channel_id() == localWorkers.size()){ - ResultWrapper * tw = reinterpret_cast*>(in); - task = tw->result; - channel = tw->source; - delete tw; + message_t * m = reinterpret_cast(in); + channel = m->sender; + in = this->n->deserializeF(m->data); + delete m; } else { // the result come from a local worker, just pass it to collector and compute the right worker id - task = reinterpret_cast(in); channel = localWorkers.at(get_channel_id()); } @@ -221,7 +149,7 @@ class CollectorAdapter: public internal_mi_transformer { mi->set_input_channelid(channel, true); } - return n->svc(task); + return n->svc(in); } ff_node* getOriginal(){ return this->n; } diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f2de07f2..f70b9828 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -282,9 +282,9 @@ class dGroup : public ff_farm { if (!isSource()){ if (currentProto == Proto::TCP){ if (onDemandReceiver) - this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, 0, buildRoutingTable(level1BB))); // set right parameters HERE!! + this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! else - this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, 0, buildRoutingTable(level1BB))); + this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); } #ifdef DFF_MPI diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 277aab19..109085ba 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -13,8 +13,6 @@ #include - - #include #include #include @@ -56,6 +54,8 @@ class dGroups { const std::string& getRunningGroup() const { return runningGroup; } void forceProtocol(Proto p){this->usedProtocol = p;} + + bool isBuildByMyBuildingBlock(const std::string gName){ return groups[gName] == groups[runningGroup];} int run_and_wait_end(ff_node* parent){ diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index d4d2d940..27a4d9ea 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -21,26 +22,19 @@ using namespace ff; class ff_dreceiver: public ff_monode_t { protected: - int sendRoutingTable(int sck, ConnectionType type){ + static int sendRoutingTable(const int sck, const std::vector& dest){ dataBuffer buff; std::ostream oss(&buff); cereal::PortableBinaryOutputArchive oarchive(oss); - std::vector reachableDestinations; - - for(auto const& p : this->routingTable){ - if (type == ConnectionType::EXTERNAL && p.first >= 0) - reachableDestinations.push_back(p.first); - else if (type == ConnectionType::INTERNAL && p.first <= -100) - reachableDestinations.push_back(p.first); - } - - oarchive << reachableDestinations; + oarchive << dest; size_t sz = htobe64(buff.getLen()); - struct iovec iov[1]; + struct iovec iov[2]; iov[0].iov_base = &sz; iov[0].iov_len = sizeof(sz); + iov[1].iov_base = buff.getPtr(); + iov[1].iov_len = buff.getLen(); - if (writevn(sck, iov, 1) < 0 || writen(sck, buff.getPtr(), buff.getLen()) < 0){ + if (writevn(sck, iov, 2) < 0){ error("Error writing on socket the routing Table\n"); return -1; } @@ -48,28 +42,29 @@ class ff_dreceiver: public ff_monode_t { return 0; } - int handshakeHandler(int sck){ + virtual int handshakeHandler(const int sck){ // ricevo l'handshake e mi salvo che tipo di connessione è - ConnectionType type; - - struct iovec iov; iov.iov_base = &type; iov.iov_len = sizeof(type); + size_t size; + struct iovec iov; iov.iov_base = &size; iov.iov_len = sizeof(size); switch (readvn(sck, &iov, 1)) { case -1: error("Error reading from socket\n"); // fatal error case 0: return -1; // connection close } - // TODO da sistemare network byte order del connectiontype!!!!!!!!! - //type = (ConnectionType) type); + size = be64toh(size); - // save the connection type locally - connectionsTypes[sck] = type; - //std::cout << "i got a " << type << " connection" << std::endl; + char groupName[size]; + if (readn(sck, groupName, size) < 0){ + error("Error reading from socket groupName\n"); return -1; + } + std::vector reachableDestinations; + for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); - return this->sendRoutingTable(sck, type); + return this->sendRoutingTable(sck, reachableDestinations); } void registerEOS(int sck){ - switch(connectionsTypes[sck]){ + /*switch(connectionsTypes[sck]){ case ConnectionType::EXTERNAL: if (++Eneos == Einput_channels) for(auto& c : routingTable) if (c.first >= 0) ff_send_out_to(this->EOS, c.second); @@ -78,7 +73,12 @@ class ff_dreceiver: public ff_monode_t { if (++Ineos == Iinput_channels) for(auto & c : routingTable) if (c.first <= -100) ff_send_out(this->EOS, c.second); break; - } + }*/ + neos++; + } + + virtual void forward(message_t* task, int){ + ff_send_out_to(task, this->routingTable[task->chid]); // assume the routing table is consistent WARNING!!! } virtual int handleRequest(int sck){ @@ -116,7 +116,7 @@ class ff_dreceiver: public ff_monode_t { out->sender = sender; out->chid = chid; - ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! + this->forward(out, sck); return 0; } @@ -127,8 +127,8 @@ class ff_dreceiver: public ff_monode_t { } public: - ff_dreceiver(ff_endpoint acceptAddr, size_t Einput_channels, size_t Iinput_channels = 0, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) - : Einput_channels(Einput_channels), Iinput_channels(Iinput_channels), acceptAddr(acceptAddr), routingTable(routingTable), coreid(coreid) {} + ff_dreceiver(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : input_channels(input_channels), acceptAddr(acceptAddr), routingTable(routingTable), coreid(coreid) {} int svc_init() { if (coreid!=-1) @@ -174,12 +174,9 @@ class ff_dreceiver: public ff_monode_t { return -1; } - /*for (const auto& e : routingTable) - std::cout << "Entry: " << e.first << " -> " << e.second << std::endl; - */ - return 0; } + void svc_end() { close(this->listen_sck); @@ -191,9 +188,7 @@ class ff_dreceiver: public ff_monode_t { Here i should not care of input type nor input data since they come from a socket listener. Everything will be handled inside a while true in the body of this node where data is pulled from network */ - message_t *svc(message_t* task) { - /* here i should receive the task via socket */ - + message_t *svc(message_t* task) { fd_set set, tmpset; // intialize both sets (master, temp) FD_ZERO(&set); @@ -205,7 +200,7 @@ class ff_dreceiver: public ff_monode_t { // hold the greater descriptor int fdmax = this->listen_sck; - while((Ineos + Eneos) < (Einput_channels + Iinput_channels)){ + while(neos < input_channels){ // copy the master set to the temporary tmpset = set; @@ -260,16 +255,70 @@ class ff_dreceiver: public ff_monode_t { } protected: - size_t Eneos = 0, Ineos = 0; - size_t Einput_channels, Iinput_channels; + size_t neos = 0; + size_t input_channels; int listen_sck; ff_endpoint acceptAddr; std::map routingTable; - std::map connectionsTypes; int last_receive_fd = -1; int coreid; }; + +class ff_dreceiverH : public ff_dreceiver { + + std::vector internalDestinations; + std::map isInternalConnection; + int internalNEos = 0; + + void registerEOS(int sck){ + if (isInternalConnection[sck] && ++internalNEos == std::count_if(std::begin(isInternalConnection), + std::end (isInternalConnection), + [](std::pair const &p) {return p.second;})) + ff_send_out_to(this->EOS, this->get_num_outchannels()-1); + + neos++; + } + + virtual int handshakeHandler(const int sck){ + size_t size; + struct iovec iov; iov.iov_base = &size; iov.iov_len = sizeof(size); + switch (readvn(sck, &iov, 1)) { + case -1: error("Error reading from socket\n"); // fatal error + case 0: return -1; // connection close + } + + size = be64toh(size); + + char groupName[size]; + if (readn(sck, groupName, size) < 0){ + error("Error reading from socket groupName\n"); return -1; + } + + bool internalGroup = dGroups::Instance()->isBuildByMyBuildingBlock(std::string(groupName)); + + isInternalConnection[sck] = internalGroup; // save somewhere the fact that this sck represent an internal connection + + if (internalGroup) return this->sendRoutingTable(sck, internalDestinations); + + std::vector reachableDestinations; + for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); + return this->sendRoutingTable(sck, reachableDestinations); + } + + void forward(message_t* task, int sck){ + if (isInternalConnection[sck]) ff_send_out_to(task, this->get_num_outchannels()-1); + else ff_dreceiver::forward(task, sck); + } + +public: + ff_dreceiverH(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {{0,0}}, std::vector internalRoutingTable = {0}, int coreid=-1) + : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid), internalDestinations(internalRoutingTable) { + + } + +}; + /* ONDEMAND specification */ @@ -335,8 +384,8 @@ class ff_dreceiverOD: public ff_dreceiver { } public: - ff_dreceiverOD(ff_endpoint acceptAddr, size_t Einput_channels, size_t Iinput_channels = 0, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) - : ff_dreceiver(acceptAddr, Einput_channels, Iinput_channels, routingTable, coreid) {} + ff_dreceiverOD(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid) {} private: ack_t ACK; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 0fb8b63c..a9a6d2b5 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -25,16 +25,15 @@ using namespace ff; class ff_dsender: public ff_minode_t { protected: int neos=0; - int Ineos=0; int next_rr_destination = 0; //next destiation to send for round robin policy std::vector dest_endpoints; std::map dest2Socket; - std::unordered_map> type2sck; + //std::unordered_map> type2sck; std::vector sockets; - int internalGateways; + //int internalGateways; int coreid; - int receiveReachableDestinations(int sck){ + int receiveReachableDestinations(int sck, std::map& m){ size_t sz; ssize_t r; @@ -66,10 +65,36 @@ class ff_dsender: public ff_minode_t { iarchive >> destinationsList; - for (int d : destinationsList) dest2Socket[d] = sck; + for (const int& d : destinationsList) m[d] = sck; + + for (const auto& p : m) + std::cout << p.first << " - " << p.second << std::endl; return 0; } + + int sendGroupName(const int sck){ + std::string gName = dGroups::Instance()->getRunningGroup(); + size_t sz = htobe64(gName.size()); + struct iovec iov[2]; + iov[0].iov_base = &sz; + iov[0].iov_len = sizeof(sz); + iov[1].iov_base = (char*)(gName.c_str()); + iov[1].iov_len = gName.size(); + + if (writevn(sck, iov, 2) < 0){ + error("Error writing on socket\n"); + return -1; + } + + return 0; + } + + virtual int handshakeHandler(const int sck, bool){ + if (sendGroupName(sck) < 0) return -1; + + return receiveReachableDestinations(sck, dest2Socket); + } int create_connect(const ff_endpoint& destination){ int socketFD; @@ -124,18 +149,8 @@ class ff_dsender: public ff_minode_t { return -1; #endif - // store the connection type - type2sck[destination.typ].push_back(socketFD); - - if (writen(socketFD, (char*) &destination.typ, sizeof(destination.typ)) < 0){ - error("Error sending the connection type during handshaking!"); - return -1; - } // receive the reachable destination from this sockets - if (receiveReachableDestinations(socketFD) < 0) - return -1; - return socketFD; } @@ -178,19 +193,23 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(ff_endpoint dest_endpoint, int internalGateways = 0, int coreid=-1): internalGateways(internalGateways), coreid(coreid) { + ff_dsender(ff_endpoint dest_endpoint, int coreid=-1): coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector dest_endpoints_, int internalGateways = 0, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), internalGateways(internalGateways), coreid(coreid) {} + ff_dsender( std::vector dest_endpoints_, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), coreid(coreid) {} + + int svc_init() { if (coreid!=-1) ff_mapThreadToCpu(coreid); sockets.resize(this->dest_endpoints.size()); - for(size_t i=0; i < this->dest_endpoints.size(); i++) + for(size_t i=0; i < this->dest_endpoints.size(); i++){ if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; + if (handshakeHandler(sockets[i], false) < 0) return -1; + } return 0; } @@ -215,6 +234,12 @@ class ff_dsender: public ff_minode_t { } void eosnotify(ssize_t id) { + if (++neos >= this->get_num_inchannels()){ + message_t E_O_S(0,0); + for(const auto& sck : sockets) sendToSck(sck, &E_O_S); + } + + /* // receive it from an internal gateway if (internalGateways > 0 && id >= (ssize_t)(this->get_num_inchannels() - internalGateways) && ++Ineos == internalGateways){ message_t E_O_S(0,0); @@ -227,11 +252,81 @@ class ff_dsender: public ff_minode_t { message_t E_O_S(0,0); for(const auto& sck : type2sck[ConnectionType::EXTERNAL]) sendToSck(sck, &E_O_S); } + */ + + + } }; +class ff_dsenderH : public ff_dsender { + + std::map internalDest2Socket; + std::map::const_iterator rr_iterator; + std::vector internalSockets; + +public: + + ff_dsenderH(ff_endpoint e, int coreid=-1) : ff_dsender(e, coreid) {} + ff_dsenderH(std::vector dest_endpoints_, int coreid=-1) : ff_dsender(dest_endpoints_, coreid) {} + + int handshakeHandler(const int sck, bool isInternal){ + if (sendGroupName(sck) < 0) return -1; + + return receiveReachableDestinations(sck, isInternal ? internalDest2Socket : dest2Socket); + } + + int svc_init() { + + sockets.resize(this->dest_endpoints.size()); + for(const auto& endpoint : this->dest_endpoints){ + int sck = tryConnect(endpoint); + if (sck <= 0) return -1; + + bool isInternal = dGroups::Instance()->isBuildByMyBuildingBlock(endpoint.groupName); + if (isInternal) internalSockets.push_back(sck); + else sockets.push_back(sck); + handshakeHandler(sck, isInternal); + } + + rr_iterator = internalDest2Socket.cbegin(); + return 0; + } + + message_t *svc(message_t* task) { + if (this->get_channel_id() == (this->get_num_inchannels() - 1)){ + // pick destination from the list of internal connections! + if (task->chid == -1){ // roundrobin over the destinations + task->chid = rr_iterator->first; + if (++rr_iterator == internalDest2Socket.cend()) rr_iterator = internalDest2Socket.cbegin(); + } + std::cout << "[Sender] Internal message to be sent to " << task->chid <<" through socket " << internalDest2Socket[task->chid] << " of size " << internalDest2Socket.size() << "!\n"; + sendToSck(internalDest2Socket[task->chid], task); + delete task; + return this->GO_ON; + } + + return ff_dsender::svc(task); + } + + void eosnotify(ssize_t id) { + if (id == (this->get_num_inchannels() - 1)){ + // send the EOS to all the internal connections + message_t E_O_S(0,0); + for(const auto& sck : internalSockets) sendToSck(sck, &E_O_S); + ++neos; // count anyway a new EOS received! + } else if (++neos >= this->get_num_inchannels()){ + message_t E_O_S(0,0); + for(const auto& sck : sockets) sendToSck(sck, &E_O_S); + } + } +}; + + + + /* ONDEMAND specification diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index c547e133..7655e3aa 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -95,16 +95,13 @@ struct ack_t { char ack = 'A'; }; -enum ConnectionType : int {EXTERNAL = 0, INTERNAL = 1}; - struct ff_endpoint { ff_endpoint(){} - ff_endpoint(std::string addr, int port, ConnectionType t = EXTERNAL) : address(addr), port(port), typ(t) {} - ff_endpoint(int rank, ConnectionType t = EXTERNAL) : port(rank), typ(t) {} + ff_endpoint(std::string addr, int port) : address(addr), port(port) {} + ff_endpoint(int rank) : port(rank) {} const int getRank() {return port;} - std::string address; + std::string address, groupName; int port; - ConnectionType typ; }; diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp index c7261284..c8ccafac 100644 --- a/tests/distributed/test_a2a_h1.cpp +++ b/tests/distributed/test_a2a_h1.cpp @@ -13,8 +13,8 @@ struct Source : ff_monode_t{ std::string* svc(std::string* in){ - for(int i = 0; i < 10; i++) - ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i%numWorker)), i%numWorker); + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); return EOS; } @@ -39,25 +39,40 @@ int main(int argc, char*argv[]){ return 1; } + ff_endpoint g1("127.0.0.1", 8001); + g1.groupName = "G1"; + + ff_endpoint g2("127.0.0.1", 8002); + g2.groupName = "G2"; + + ff_farm gFarm; ff_a2a a2a; + + // the following are just for building this example! + dGroups::Instance()->addGroup("G1", &a2a); + dGroups::Instance()->addGroup("G2", &a2a); + if (atoi(argv[1]) == 0){ - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 0, 1, {{-100, 1}})); - gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8002, ConnectionType::INTERNAL), 1)); + dGroups::Instance()->setRunningGroup("G1"); + gFarm.add_emitter(new ff_dreceiverH(g1, 1, {{0, 0}}, {0})); + gFarm.add_collector(new ff_dsenderH(g2)); - auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); ea->skipallpop(true); + auto ea1 = new EmitterAdapter(new Source(2,0), 2, 0, {{0,0}}, true); ea1->skipallpop(true); + auto ea2 = new EmitterAdapter(new Source(2,1), 2, 1, {{0,0}}, true); ea2->skipallpop(true); - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); - a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); + a2a.add_firstset({ea1, ea2, new SquareBoxLeft({std::make_pair(0,0)})}); + a2a.add_secondset({new CollectorAdapter(new Sink(0), {0, 1}, true), new SquareBoxRight()}); } else { - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 0, 1, {{-101, 1}})); - gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), 1)); + dGroups::Instance()->setRunningGroup("G2"); + gFarm.add_emitter(new ff_dreceiverH(g2, 1, {{0, 0}}, {1})); + gFarm.add_collector(new ff_dsenderH(g1)); - auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); ea->skipallpop(true); + auto ea = new EmitterAdapter(new Source(2,2), 2, 2, {{1,0}}, true); ea->skipallpop(true); - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); - a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); + a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}); + a2a.add_secondset({new CollectorAdapter(new Sink(1), {2}, true), new SquareBoxRight()}); } gFarm.add_workers({&a2a}); From 9d7ff7661b371bad2de4742ab5da9da439710f67 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 10 Dec 2021 12:34:44 +0100 Subject: [PATCH 093/202] fixed problem in test_a2a_h1 --- ff/distributed/ff_dgroup.hpp | 12 ++++++++++++ ff/distributed/ff_dgroups.hpp | 2 +- ff/distributed/ff_dreceiver.hpp | 2 +- tests/distributed/test_a2a_h1.cpp | 7 ++++--- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f1aaaf2f..a6a3149e 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -358,6 +358,8 @@ class dGroup : public ff_farm { ff_endpoint getEndpoint(){return this->endpoint;} + ff_node* getParent() const { return parentStructure; } + void setDestination(ff_endpoint e){ this->destinations.push_back(std::move(e));} void setExpectedInputConnections(int connections){this->expectedInputConnections = connections;} @@ -440,6 +442,14 @@ void dGroups::consolidateGroups(){ } +bool dGroups::isBuildByMyBuildingBlock(const std::string gName) { + auto g1 = reinterpret_cast(groups[gName]); + auto g2 = reinterpret_cast(groups[runningGroup]); + + return g1->getParent() == g2->getParent(); +} + + void dGroups::parseConfig(std::string configFile){ std::ifstream is(configFile); @@ -488,4 +498,6 @@ ff::dGroup& ff_pipeline::createGroup(std::string name){ dGroup * g = new dGroup(this, std::move(name)); return *g; } + + #endif diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 1e988956..49910313 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -60,7 +60,7 @@ class dGroups { void forceProtocol(Proto p){this->usedProtocol = p;} - bool isBuildByMyBuildingBlock(const std::string gName){ return groups[gName] == groups[runningGroup];} + bool isBuildByMyBuildingBlock(const std::string gName); int run_and_wait_end(ff_node* parent){ diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 27a4d9ea..e9d7d243 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -295,7 +295,7 @@ class ff_dreceiverH : public ff_dreceiver { error("Error reading from socket groupName\n"); return -1; } - bool internalGroup = dGroups::Instance()->isBuildByMyBuildingBlock(std::string(groupName)); + bool internalGroup = dGroups::Instance()->isBuildByMyBuildingBlock(std::string(groupName,size)); isInternalConnection[sck] = internalGroup; // save somewhere the fact that this sck represent an internal connection diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp index c8ccafac..5798d355 100644 --- a/tests/distributed/test_a2a_h1.cpp +++ b/tests/distributed/test_a2a_h1.cpp @@ -47,11 +47,12 @@ int main(int argc, char*argv[]){ ff_farm gFarm; - ff_a2a a2a; + ff_a2a a2a; + // the following are just for building this example! - dGroups::Instance()->addGroup("G1", &a2a); - dGroups::Instance()->addGroup("G2", &a2a); + a2a.createGroup("G1"); + a2a.createGroup("G2"); if (atoi(argv[1]) == 0){ dGroups::Instance()->setRunningGroup("G1"); From df714710e9842eeb9e9279617b9b376baf136e24 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 10 Dec 2021 14:54:12 +0100 Subject: [PATCH 094/202] cosmetic changes to test_a2a_h*.cpp --- tests/distributed/test_a2a_h1.cpp | 14 ++++++++++++++ tests/distributed/test_a2a_h2.cpp | 14 ++++++++++++++ tests/distributed/test_a2a_h3.cpp | 15 +++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp index 5798d355..2aa6b171 100644 --- a/tests/distributed/test_a2a_h1.cpp +++ b/tests/distributed/test_a2a_h1.cpp @@ -1,3 +1,17 @@ +/* + * Source1 ->| + * | | ->Sink1 + * Source2 ->| -> | + * | | ->Sink2 + * Source3 ->| + * + * G1: Source1, Source2, Sink1 + * G2: Source3, Sink2 + * + */ + + + #include #include #include diff --git a/tests/distributed/test_a2a_h2.cpp b/tests/distributed/test_a2a_h2.cpp index d0f27960..8935cdbf 100644 --- a/tests/distributed/test_a2a_h2.cpp +++ b/tests/distributed/test_a2a_h2.cpp @@ -1,3 +1,17 @@ +/* + * |-> Forwarder1 ->| + * | | -> |-> Sink1 + * Source ->|-> Forwarder2 ->| | + * | | -> |-> Sink2 + * |-> Forwarder3 ->| + * + * + * G0: Source + * G1: Forwarer1, Forwarder2, Sink1 + * G2: Forwarder3, Sink2 + * + */ + #include #include #include diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp index 1a8ab341..4b9d316b 100644 --- a/tests/distributed/test_a2a_h3.cpp +++ b/tests/distributed/test_a2a_h3.cpp @@ -1,3 +1,18 @@ +/* + * |-> Forwarder1 ->| + * | | -> |-> Sink1 ->| + * Source ->|-> Forwarder2 ->| | | -> StringPrinter + * | | -> |-> Sink2 ->| + * |-> Forwarder3 ->| + * + * + * G0: Source + * G1: Forwarer1, Forwarder2, Sink1 + * G2: Forwarder3, Sink2 + * G3: StringPrinter + * + */ + #include #include #include From 2ab878e0c280ea55da4662f73a8b0dfcdcedf4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Fri, 10 Dec 2021 19:28:03 +0100 Subject: [PATCH 095/202] Refactored test_a2a_h2 and test_a2a_h3. Now they are working again with the new implemented stuff --- tests/distributed/test_a2a_h2.cpp | 53 ++++++++++++++++------- tests/distributed/test_a2a_h3.cpp | 70 +++++++++++++++++++++---------- 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/tests/distributed/test_a2a_h2.cpp b/tests/distributed/test_a2a_h2.cpp index 8935cdbf..7d4f4e7a 100644 --- a/tests/distributed/test_a2a_h2.cpp +++ b/tests/distributed/test_a2a_h2.cpp @@ -57,8 +57,14 @@ struct Sink : ff_minode_t{ struct ForwarderNode : ff_node{ - void* svc(void* input){return input;} -}; + ForwarderNode(std::function f){ + this->serializeF = f; + } + ForwarderNode(std::function f){ + this->deserializeF = f; + } + void* svc(void* input){return input;} + }; int main(int argc, char*argv[]){ @@ -70,32 +76,47 @@ int main(int argc, char*argv[]){ ff_farm gFarm; ff_a2a a2a; + + ff_endpoint g1("127.0.0.1", 8001); + g1.groupName = "G1"; + + ff_endpoint g2("127.0.0.1", 8002); + g2.groupName = "G2"; + + + a2a.createGroup("G1"); + a2a.createGroup("G2"); + ff_pipeline dummypipe; dummypipe.createGroup("G0"); + if (atoi(argv[1]) == 0){ - gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001, ConnectionType::EXTERNAL), ff_endpoint("127.0.0.1", 8002, ConnectionType::EXTERNAL)})); - gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true, -1)}); + dGroups::Instance()->setRunningGroup("G0"); + gFarm.add_collector(new ff_dsender({g1, g2})); + gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); gFarm.run_and_wait_end(); return 0; } else if (atoi(argv[1]) == 1){ - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, 1, {{0, 0}, {-100, 1}})); - gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8002, ConnectionType::INTERNAL), 1)); + dGroups::Instance()->setRunningGroup("G1"); + gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0})); + gFarm.add_collector(new ff_dsenderH(g2)); - //auto ea = new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true); //ea->skipallpop(true); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true), true, true); + auto s = new Source(2,0); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); - a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); + a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(0,0)})}); + a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new SquareBoxRight}); } else { - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, 1, {{1, 0}, {-101, 1}})); - gFarm.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), 1)); + dGroups::Instance()->setRunningGroup("G2"); + gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1})); + gFarm.add_collector(new ff_dsenderH(g1)); - //auto ea = new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true); //ea->skipallpop(true); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true), true, true); + auto s = new Source(2,1); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}); - a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new ff_comb(new SquareBoxEmitter({1}), new WrapperOUTCustom(),true, true)}); + a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}); + a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new SquareBoxRight}); } gFarm.add_workers({&a2a}); diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp index 4b9d316b..7611eec0 100644 --- a/tests/distributed/test_a2a_h3.cpp +++ b/tests/distributed/test_a2a_h3.cpp @@ -73,7 +73,13 @@ struct StringPrinter : ff_node_t{ struct ForwarderNode : ff_node{ - void* svc(void* input){return input;} + ForwarderNode(std::function f){ + this->serializeF = f; + } + ForwarderNode(std::function f){ + this->deserializeF = f; + } + void* svc(void* input){return input;} }; @@ -84,45 +90,67 @@ int main(int argc, char*argv[]){ return 1; } + ff_endpoint g1("127.0.0.1", 8001); + g1.groupName = "G1"; + + ff_endpoint g2("127.0.0.1", 8002); + g2.groupName = "G2"; + + ff_endpoint g3("127.0.0.1", 8003); + g3.groupName = "G3"; + ff_farm gFarm; ff_a2a a2a; + + a2a.createGroup("G1"); + a2a.createGroup("G2"); + ff_pipeline dummypipe; dummypipe.createGroup("G3"); dummypipe.createGroup("G0"); + + if (atoi(argv[1]) == 0){ - gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001, ConnectionType::EXTERNAL), ff_endpoint("127.0.0.1", 8002, ConnectionType::EXTERNAL)})); - gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true, -1)}); + dGroups::Instance()->setRunningGroup("G0"); + gFarm.add_collector(new ff_dsender({g1, g2})); + gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); - gFarm.run_and_wait_end(); - return 0; + gFarm.run_and_wait_end(); + return 0; } else if (atoi(argv[1]) == 1){ - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, 1, {{0, 0}, {-100, 1}})); - gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8002, ConnectionType::INTERNAL), ff_endpoint("127.0.0.1", 8003, ConnectionType::EXTERNAL)}, 1)); + dGroups::Instance()->setRunningGroup("G1"); + gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0})); + gFarm.add_collector(new ff_dsenderH({g2,g3})); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode), new EmitterAdapter(new Source(2,0), 2, {{0,0}}, true), true, true); - - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(0,0)}), true, true)}); - a2a.add_secondset({new ff_comb(new CollectorAdapter(new Sink(0), {0}, true), new WrapperOUT(new ForwarderNode, 1, true, 0)), new ff_comb(new SquareBoxEmitter({0}), new WrapperOUTCustom(), true, true)}); + auto s = new Source(2,0); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); + + a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(0,0)})}); + auto sink = new Sink(0); + a2a.add_secondset({new ff_comb(new CollectorAdapter(sink, {0}, true), new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true)), new SquareBoxRight}); } else if (atoi(argv[1]) == 2) { - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, 1, {{1, 0}, {-101, 1}})); - gFarm.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001, ConnectionType::INTERNAL), ff_endpoint("127.0.0.1", 8003, ConnectionType::EXTERNAL)}, 1)); + dGroups::Instance()->setRunningGroup("G2"); + gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1})); + gFarm.add_collector(new ff_dsenderH({g1, g3})); gFarm.cleanup_emitter(); gFarm.cleanup_collector(); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode, 1, true), new EmitterAdapter(new Source(2,1), 2, {{1,0}}, true), true, true); + auto s = new Source(2,1); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); - a2a.add_firstset({ea, new ff_comb(new WrapperINCustom(), new SquareBoxCollector({std::make_pair(1,0)}), true, true)}, 0, true); + a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}, 0, true); + auto sink = new Sink(1); a2a.add_secondset({ - new ff_comb(new CollectorAdapter(new Sink(1), {1}, true), - new WrapperOUT(new ForwarderNode, 1, true, 0), true, true), - new ff_comb(new SquareBoxEmitter({1}), - new WrapperOUTCustom(),true, true) + new ff_comb(new CollectorAdapter(sink, {1}, true), + new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true), true, true), + new SquareBoxRight }, true); } else { - gFarm.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8003), 2)); - gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); + dGroups::Instance()->setRunningGroup("G3"); + gFarm.add_emitter(new ff_dreceiver(g3, 2)); + gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); gFarm.run_and_wait_end(); return 0; From 0d37d5f8883fdeb60ed1d00682fe67c9516b5282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 13 Dec 2021 15:51:59 +0100 Subject: [PATCH 096/202] First working example of an a2a splitted obliquely --- tests/distributed/test_a2a_h4.cpp | 172 ++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 tests/distributed/test_a2a_h4.cpp diff --git a/tests/distributed/test_a2a_h4.cpp b/tests/distributed/test_a2a_h4.cpp new file mode 100644 index 00000000..68960e95 --- /dev/null +++ b/tests/distributed/test_a2a_h4.cpp @@ -0,0 +1,172 @@ +/* + * |-> Forwarder1 ->| + * | | -> |-> Sink1 ->| + * Source ->|-> Forwarder2 ->| | | -> StringPrinter + * | | -> |-> Sink2 ->| + * |-> Forwarder3 ->| + * + * + * G0: Source + * G1: Forwarer1, Forwarder2, Sink1 + * G2: Forwarder3, Sink2 + * G3: StringPrinter + * + */ + +#include +#include +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct RealSource : ff_monode_t{ + std::string* svc(std::string*){ + for(int i = 0; i < 2; i++) ff_send_out_to(new std::string("Trigger string!"), i); + return EOS; + } +}; + +struct Source : ff_monode_t{ + int numWorker, generatorID; + Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + std::string* svc(std::string* in){ + delete in; + std::cout << "Source starting generating tasks!" << std::endl; + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + + std::cout << "Source generated all task sending now EOS!" << std::endl; + return EOS; + } +}; + + +struct Sink : ff_minode_t{ + int sinkID; + Sink(int id): sinkID(id) {} + std::string* svc(std::string* in){ + std::string* output = new std::string(*in + " received by Sink " + std::to_string(sinkID) + " from " + std::to_string(get_channel_id())); + delete in; + return output; + } +}; + +struct StringPrinter : ff_node_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "Received something! Addr:" << in << "\n"; +#if 1 + try { + std::cout << *in << std::endl; + delete in; + } catch (const std::exception& ex){ + std::cerr << ex.what(); + } +#endif + return this->GO_ON; + } +}; + + +struct ForwarderNode : ff_node{ + ForwarderNode(std::function f){ + this->serializeF = f; + } + ForwarderNode(std::function f){ + this->deserializeF = f; + } + void* svc(void* input){return input;} +}; + + +int main(int argc, char*argv[]){ + + if (argc != 2){ + std::cerr << "Execute with the index of process!" << std::endl; + return 1; + } + + ff_endpoint g1("127.0.0.1", 8001); + g1.groupName = "G1"; + + ff_endpoint g2("127.0.0.1", 8002); + g2.groupName = "G2"; + + ff_endpoint g3("127.0.0.1", 8003); + g3.groupName = "G3"; + + ff_endpoint g4("127.0.0.1", 8004); + g4.groupName = "G4"; + + ff_farm gFarm; + ff_a2a a2a; + + a2a.createGroup("G1"); + a2a.createGroup("G2"); + a2a.createGroup("G3"); + ff_pipeline dummypipe; dummypipe.createGroup("G4"); dummypipe.createGroup("G0"); + + + if (atoi(argv[1]) == 0){ + dGroups::Instance()->setRunningGroup("G0"); + gFarm.add_collector(new ff_dsender({g1, g3})); + gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); + + gFarm.run_and_wait_end(); + return 0; + } else if (atoi(argv[1]) == 1){ + dGroups::Instance()->setRunningGroup("G1"); + gFarm.add_emitter(new ff_dreceiver(g1, 1, {{0, 0}})); + gFarm.add_collector(new ff_dsender({g2,g3})); + + gFarm.add_workers({new WrapperINOUT(new Source(2,0), 1, true)}); + + gFarm.run_and_wait_end(); + return 0; + + } else if (atoi(argv[1]) == 2){ + dGroups::Instance()->setRunningGroup("G2"); + gFarm.add_emitter(new ff_dreceiver(g2, 2, {{0, 0}})); + gFarm.add_collector(new ff_dsender(g4)); + + gFarm.add_workers({new WrapperINOUT(new Sink(0), 1, true)}); + + gFarm.run_and_wait_end(); + return 0; + + } else if (atoi(argv[1]) == 3) { + dGroups::Instance()->setRunningGroup("G3"); + gFarm.add_emitter(new ff_dreceiverH(g3, 2, {{1, 0}}, {1})); + gFarm.add_collector(new ff_dsenderH({g2, g4})); + gFarm.cleanup_emitter(); + gFarm.cleanup_collector(); + + auto s = new Source(2,1); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); + + a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}, 0, true); + + auto sink = new Sink(1); + a2a.add_secondset({ + new ff_comb(new CollectorAdapter(sink, {1}, true), + new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true), true, true), + new SquareBoxRight + }, true); + + + + } else { + dGroups::Instance()->setRunningGroup("G4"); + gFarm.add_emitter(new ff_dreceiver(g4, 2)); + gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); + + gFarm.run_and_wait_end(); + return 0; + } + gFarm.add_workers({&a2a}); + gFarm.run_and_wait_end(); +} From 4571b76b0425b82c798d97f7787e2552c82ecfef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 22 Dec 2021 17:06:13 +0100 Subject: [PATCH 097/202] Posponed the generation of wrapper, if really needed. Still some minor things need to be checked properly --- ff/distributed/ff_dgroup.hpp | 198 +++++++++++++++-------------- ff/distributed/ff_dsender.hpp | 26 +--- ff/distributed/ff_wrappers.hpp | 51 ++------ tests/distributed/test_wrapper.cpp | 110 ---------------- 4 files changed, 117 insertions(+), 268 deletions(-) delete mode 100644 tests/distributed/test_wrapper.cpp diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index a6a3149e..18fc5f57 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -33,16 +34,6 @@ template class MySet { private: dGroup* group; - - struct ForwarderNode : ff_node{ - ForwarderNode(std::function f){ - this->serializeF = f; - } - ForwarderNode(std::function f){ - this->deserializeF = f; - } - void* svc(void* input){return input;} - }; public: MySet(dGroup* group): group(group){ } @@ -57,36 +48,59 @@ class dGroup : public ff_farm { friend class MySet; friend class MySet; + + struct ForwarderNode : ff_node { + ForwarderNode(std::function f){ + this->serializeF = f; + } + ForwarderNode(std::function f){ + this->deserializeF = f; + } + void* svc(void* input){return input;} + }; private: ff_node * parentStructure; + ff_node * level1BB; ff_endpoint endpoint; std::vector destinations; int expectedInputConnections; - bool executed = false; - /** - * Key: reference to original node - * Value: pair of [reference to wrapper, serialization_required] - **/ - std::map in_, out_; - std::map inout_; + + std::set in_, out_, inout_; bool isSource(){return in_.empty() && inout_.empty();} bool isSink(){return out_.empty() && inout_.empty();} - static bool isIncludedIn(const ff::svector& firstSet, std::vector& secondSet){ + static bool isIncludedIn(const ff::svector& firstSet, std::set& secondSet){ for (const ff_node* n : firstSet) if (std::find(secondSet.begin(), secondSet.end(), n) == secondSet.end()) return false; return true; } - bool replaceWrapper(const ff::svector& list, std::map& wrappers_){ + ff_node* buildWrapper(ff_node* n, IOTypes t){ + if (t == IOTypes::IN){ + if (n->isMultiOutput()) + return new ff_comb(new WrapperIN(new ForwarderNode(n->deserializeF), true), n, true, false); + return new WrapperIN(n, 1, false); + } + + if (t == IOTypes::OUT){ + int id = getIndexOfNode(level1BB, n, nullptr, IOTypes::OUT); + if (n->isMultiInput()) + return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF), id, 1, true), false, true); + return new WrapperOUT(n, id, 1, false); + } + + return nullptr; + } + + bool replaceWrapper(const ff::svector& list, IOTypes t){ for (ff_node* node : list){ ff_node* parentBB = getBB(this->parentStructure, node); if (parentBB != nullptr){ - ff_node* wrapped = wrappers_[node]; + ff_node* wrapped = buildWrapper(node, t); if (parentBB->isPipe()){ reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); @@ -110,27 +124,18 @@ class dGroup : public ff_farm { return true; } - ff_node* getOriginal(ff_node* wrapper){ - auto resultI = std::find_if(this->in_.begin(), this->in_.end(), [&](const std::pair &pair){return pair.second == wrapper;}); - if (resultI != this->in_.end()) return resultI->first; - auto resultII = std::find_if(this->inout_.begin(), this->inout_.end(), [&](const std::pair &pair){return pair.second == wrapper;}); - if (resultII != this->inout_.end()) return resultII->first; - - return nullptr; - } - static inline bool isSeq(ff_node* n){return (!n->isAll2All() && !n->isComp() && !n->isFarm() && !n->isOFarm() && !n->isPipe());} - bool processBB(ff_node* bb, std::vector in_C, std::vector out_C){ + bool processBB(ff_node* bb, std::set in_C, std::set out_C){ if (isSeq(bb)){ ff::svector svectBB(1); svectBB.push_back(bb); - if (isSource() && this->out_.find(bb) != this->out_.end() && replaceWrapper(svectBB, this->out_)){ - this->add_workers({this->out_[bb]}); + if (isSource() && this->out_.find(bb) != this->out_.end() /*&& replaceWrapper(svectBB, this->out_)*/){ + this->add_workers({buildWrapper(bb, IOTypes::OUT)}); return true; } - if (isSink() && this->in_.find(bb) != this->in_.end() && replaceWrapper(svectBB, this->in_)){ - this->add_workers({this->in_[bb]}); + if (isSink() && this->in_.find(bb) != this->in_.end() /*&& replaceWrapper(svectBB, this->in_)*/){ + this->add_workers({buildWrapper(bb, IOTypes::IN)}); return true; } @@ -148,7 +153,7 @@ class dGroup : public ff_farm { if (!isSink() && !isIncludedIn(out_nodes, out_C)) return false; - if ((isSource() || replaceWrapper(in_nodes, this->in_)) && (isSink() || replaceWrapper(out_nodes, this->out_))){ + if ((isSource() || replaceWrapper(in_nodes, IOTypes::IN)) && (isSink() || replaceWrapper(out_nodes, IOTypes::OUT))){ this->add_workers({bb}); // here the bb is already modified with the wrapper return true; } @@ -163,14 +168,26 @@ class dGroup : public ff_farm { return false; } - static int getInputIndexOfNode(ff_node* bb, ff_node* wrapper, ff_node* original){ + static ff::svector getIONodes(ff_node* n, IOTypes t){ + ff::svector sv; + switch (t) { + case IOTypes::IN: n->get_in_nodes(sv); break; + case IOTypes::OUT: n->get_out_nodes(sv); break; + } + return sv; + } + + /** + * Retrieve the index of the node wrapper (or the alternative) in the original shared memory graph. + * Depending on the type t (IN - OUT) we get the index either in input or in output. + */ + static int getIndexOfNode(ff_node* bb, ff_node* wrapper, ff_node* alternative, IOTypes t){ if (bb->isAll2All()){ ff_a2a* a2a = (ff_a2a*) bb; int index = 0; for (ff_node* n : a2a->getFirstSet()){ - ff::svector inputs; n->get_in_nodes(inputs); - for (const ff_node* input : inputs){ - if (input == wrapper || input == original) + for (const ff_node* io : getIONodes(n, t)){ + if (io == wrapper || io == alternative) return index; index++; } @@ -178,79 +195,86 @@ class dGroup : public ff_farm { index = 0; for (ff_node* n : a2a->getSecondSet()){ - ff::svector inputs; n->get_in_nodes(inputs); - for (ff_node* input : inputs) - if (input == wrapper || input == original) + for (ff_node* io : getIONodes(n, t)) + if (io == wrapper || io == alternative) return index; else index++; } } int index = 0; - ff::svector inputs; bb->get_in_nodes(inputs); - for (ff_node* input : inputs) - if (input == wrapper || input == original) + for (ff_node* io : getIONodes(bb, t)) + if (io == wrapper || io == alternative) return index; else index++; return 0; } + /** + * Given a node pointer w and an hint of the type of Wrapper t, get the poiinter of the original wrapped node. + */ + static ff_node* getOriginal(ff_node* w, IOTypes t){ + // if the wrapper is actually a composition, return the correct address of the orginal node! + if (w->isComp()){ + switch (t){ + case IOTypes::IN: return reinterpret_cast(w)->getRight(); + case IOTypes::OUT: return reinterpret_cast(w)->getLeft(); + default: return nullptr; + } + } + switch (t){ + case IOTypes::IN: return reinterpret_cast(w)->n; + case IOTypes::OUT: return reinterpret_cast(w)->n; + default: return nullptr; + } + } + std::map buildRoutingTable(ff_node* level1BB){ std::map routingTable; int localIndex = 0; for (ff_node* inputBB : this->getWorkers()){ ff::svector inputs; inputBB->get_in_nodes(inputs); - for (ff_node* input : inputs){ - routingTable[getInputIndexOfNode(level1BB, input, getOriginal(input))] = localIndex; - localIndex++; - } - //routingTable[getInputIndexOfNode(level1BB, reinterpret_cast(input)->getOriginal())] = localIndex++; + for (ff_node* input : inputs) + routingTable[getIndexOfNode(level1BB, input, getOriginal(input, IOTypes::IN), IOTypes::IN)] = localIndex++; } return routingTable; } + int buildFarm(ff_pipeline* basePipe = nullptr){ - - int buildFarm(ff_pipeline* basePipe = nullptr){ // chimato dalla run & wait della main pipe - - // find the 1 level builiding block which containes the group (level 1 BB means a BB whoch is a stage in the main piepline) - ff_node* level1BB = this->parentStructure; + // find the 1 level builiding block which containes the group (level 1 BB means a BB which is a stage in the main piepline) + //ff_node* level1BB = this->parentStructure; while(!isStageOf(level1BB, basePipe)){ level1BB = getBB(basePipe, level1BB); if (!level1BB || level1BB == basePipe) throw FF_Exception("A group were created from a builiding block not included in the Main Pipe! :("); } - - std::vector in_C, out_C; - for (const auto& pair : this->in_) in_C.push_back(pair.first); - for (const auto& pair : this->out_) out_C.push_back(pair.first); - int onDemandQueueLength = 0; bool onDemandReceiver = false; bool onDemandSender = false; - + if (this->parentStructure->isPipe()) - processBB(this->parentStructure, in_C, out_C); + processBB(this->parentStructure, in_, out_); if (this->parentStructure->isAll2All()){ ff_a2a * a2a = (ff_a2a*) this->parentStructure; - if (!processBB(a2a, in_C, out_C)){ // if the user has not wrapped the whole a2a, expand its sets + if (!processBB(a2a, in_, out_)){ // if the user has not wrapped the whole a2a, expand its sets bool first = false, second = false; for(ff_node* bb : a2a->getFirstSet()) - if (processBB(bb, in_C, out_C)) + if (processBB(bb, in_, out_)) first = true; for(ff_node* bb : a2a->getSecondSet()) - if (processBB(bb, in_C, out_C)) + if (processBB(bb, in_, out_)) second = true; // check on input/output nodes, used for ondemand stuff and for checking collision between nodes if (!first && !second){ - for (const auto& pair : this->inout_){ - if (std::find(a2a->getFirstSet().begin(), a2a->getFirstSet().end(), pair.first) != a2a->getFirstSet().end()) + for (const auto& n : this->inout_){ + if (std::find(a2a->getFirstSet().begin(), a2a->getFirstSet().end(), n) != a2a->getFirstSet().end()) first = true; - else if (std::find(a2a->getSecondSet().begin(), a2a->getSecondSet().end(), pair.first) != a2a->getSecondSet().end()) + else if (std::find(a2a->getSecondSet().begin(), a2a->getSecondSet().end(), n) != a2a->getSecondSet().end()) second = true; } } @@ -269,9 +293,9 @@ class dGroup : public ff_farm { } // in/out nodes left to be added to the farm. The next lines does it - for (const auto& pair : this->inout_){ + for (ff_node* n : this->inout_){ //std::cout << "Added INOUT node" << std::endl; - this->add_workers({pair.second}); + this->add_workers({new WrapperINOUT(n, getIndexOfNode(level1BB, n, nullptr, IOTypes::OUT))}); } if (this->getNWorkers() == 0) @@ -322,17 +346,14 @@ class dGroup : public ff_farm { return 0; } - ff_node* getWrapper(ff_node* n){ - return this->inout_[n]; - } - public: - dGroup(ff_node* parent, std::string label): parentStructure(parent), endpoint(), destinations(), expectedInputConnections(0), in(this), out(this){ + dGroup(ff_node* parent, std::string label): parentStructure(parent), level1BB(parent), endpoint(), destinations(), expectedInputConnections(0), in(this), out(this){ dGroups::Instance()->addGroup(label, this); } ~dGroup() { - if (!executed) { + // TO DO: check the cleanup with the new way of generate wrapping! + /*if (!prepared) { for(auto s: in_) delete s.second; for(auto s: out_) @@ -340,15 +361,14 @@ class dGroup : public ff_farm { for(auto s: inout_) delete s.second; } + */ } int run(bool skip_init=false) override {return 0;} int run(ff_node* baseBB, bool skip_init=false) override { buildFarm(reinterpret_cast(baseBB)); - auto r= ff_farm::run(skip_init); - executed = true; - return r; + return ff_farm::run(skip_init); } @@ -378,13 +398,9 @@ MySet& MySet::operator<<(ff_node* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! if (!this->group->out_.extract(node).empty()) // if the node was also annotedted in output just create the WrapperINOUT - this->group->inout_.emplace(node, new WrapperINOUT(node, 1, false)); - else { - if (node->isMultiOutput()) - this->group->in_.emplace(node, new ff_comb(new WrapperIN(new ForwarderNode(node->deserializeF), true), node, true, false)); - else - this->group->in_.emplace(node, new WrapperIN(node, 1, false)); - } + this->group->inout_.insert(node); + else + this->group->in_.insert(node); return *this; } @@ -399,13 +415,9 @@ MySet& MySet::operator<<(ff_node* node){ if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! if (!this->group->in_.extract(node).empty()) // if the node was also annotedted in output just create the WrapperINOUT - this->group->inout_.emplace(node, new WrapperINOUT(node, 1, false)); - else { - if (node->isMultiInput()) - this->group->out_.emplace(node, new ff_comb(node, new WrapperOUT(new ForwarderNode(node->serializeF), 1, true), false, true)); - else - this->group->out_.emplace(node, new WrapperOUT(node, 1, false)); - } + this->group->inout_.insert(node); + else + this->group->out_.insert(node); return *this; } diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index a9a6d2b5..901c5b89 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -24,7 +24,7 @@ using namespace ff; class ff_dsender: public ff_minode_t { protected: - int neos=0; + size_t neos=0; int next_rr_destination = 0; //next destiation to send for round robin policy std::vector dest_endpoints; std::map dest2Socket; @@ -67,9 +67,9 @@ class ff_dsender: public ff_minode_t { for (const int& d : destinationsList) m[d] = sck; - for (const auto& p : m) + /*for (const auto& p : m) std::cout << p.first << " - " << p.second << std::endl; - + */ return 0; } @@ -238,24 +238,6 @@ class ff_dsender: public ff_minode_t { message_t E_O_S(0,0); for(const auto& sck : sockets) sendToSck(sck, &E_O_S); } - - /* - // receive it from an internal gateway - if (internalGateways > 0 && id >= (ssize_t)(this->get_num_inchannels() - internalGateways) && ++Ineos == internalGateways){ - message_t E_O_S(0,0); - for (const auto & sck : type2sck[ConnectionType::INTERNAL]) sendToSck(sck, &E_O_S); - } else - ++neos; - - if (neos > 0 && (neos + Ineos) >= (int)this->get_num_inchannels()){ - // send to all the external - message_t E_O_S(0,0); - for(const auto& sck : type2sck[ConnectionType::EXTERNAL]) sendToSck(sck, &E_O_S); - } - */ - - - } }; @@ -302,7 +284,7 @@ class ff_dsenderH : public ff_dsender { task->chid = rr_iterator->first; if (++rr_iterator == internalDest2Socket.cend()) rr_iterator = internalDest2Socket.cbegin(); } - std::cout << "[Sender] Internal message to be sent to " << task->chid <<" through socket " << internalDest2Socket[task->chid] << " of size " << internalDest2Socket.size() << "!\n"; + sendToSck(internalDest2Socket[task->chid], task); delete task; return this->GO_ON; diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 1fc0d2bb..d1ac0711 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -17,15 +17,6 @@ struct DummyNode : public ff_node_t { Tout* svc(Tin* in){ return nullptr;} }; -class Wrapper { -protected: - int myID; // used to populate sender field of the header - -public: - Wrapper() : myID(-1){} - void setMyId(int id){myID = id;} -}; - /* Wrapper IN class */ @@ -69,34 +60,22 @@ class WrapperIN: public internal_mi_transformer { ff_node* getOriginal(){ return this->n; } }; -template -struct WrapperINCustom : public WrapperIN { - WrapperINCustom() : WrapperIN(new DummyNode(), 1, true){} - - void * svc(void* in) { - message_t* msg = (message_t*)in; - auto* out = new SMmessage_t(this->n->deserializeF(msg->data), msg->sender, msg->chid); - delete msg; - return out; - } -}; - /* Wrapper OUT class */ -class WrapperOUT: public Wrapper, public internal_mo_transformer { +class WrapperOUT: public internal_mo_transformer { private: int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; + int myID; public: - WrapperOUT(ff_node* n, int outchannels=1, bool cleanup=false, int defaultDestination = -1): Wrapper(), internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination) { + WrapperOUT(ff_node* n, int id, int outchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id) { this->n = n; this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); - } bool serialize(void* in, int id) { @@ -106,7 +85,7 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { this->n->serializeF(in, msg->data); - msg->sender = get_my_id(); // da cambiare con qualcosa di reale! + msg->sender = myID; // da cambiare con qualcosa di reale! msg->chid = id; return this->ff_send_out(msg); @@ -142,33 +121,19 @@ class WrapperOUT: public Wrapper, public internal_mo_transformer { } }; -template -struct WrapperOUTCustom : public WrapperOUT { - WrapperOUTCustom() : WrapperOUT(new DummyNode(), 1, true){} - - void* svc(void* in){ - SMmessage_t * input = reinterpret_cast(in); - Wrapper::myID = input->sender; - this->serialize(input->task, input->dst); - delete input; - return this->GO_ON; - } -}; - - /* Wrapper INOUT class */ -class WrapperINOUT: public Wrapper, public internal_mi_transformer { +class WrapperINOUT: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; - + int myID; public: - WrapperINOUT(ff_node* n, int inchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination){ + WrapperINOUT(ff_node* n, int id, int inchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), myID(id){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -185,7 +150,7 @@ class WrapperINOUT: public Wrapper, public internal_mi_transformer { this->n->serializeF(in, msg->data); - msg->sender = get_my_id(); // da cambiare con qualcosa di reale! + msg->sender = myID; // da cambiare con qualcosa di reale! msg->chid = id; return ff_node::ff_send_out(msg); diff --git a/tests/distributed/test_wrapper.cpp b/tests/distributed/test_wrapper.cpp deleted file mode 100644 index b197d41a..00000000 --- a/tests/distributed/test_wrapper.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Node1-->Node2 --> Node3 --> Node4 - * /<----------- pipe ------------>/ - * - */ - - -#include -#include -#include - -using namespace ff; - -struct myTask_t { - std::string str; - struct S_t { - long t; - float f; - } S; - - template - void serialize(Archive & archive) { - archive(str, S.t, S.f); - } - -}; - - -struct Node1: ff_node_t{ - Node1(long ntasks):ntasks(ntasks) {} - myTask_t* svc(myTask_t*){ - for(long i=0; i< ntasks; i++) { - myTask_t* task = new myTask_t; - task->str="Hello"; - task->S.t = i; - task->S.f = i*1.0; - ff_send_out(task); - } - return EOS; - } - const long ntasks; -}; - -struct Node2: ff_monode_t{ - myTask_t* svc(myTask_t* t){ - t->str += std::string(" World"); - //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; - - return t; - } -}; - -struct Node3: ff_node_t{ - myTask_t* svc(myTask_t* t){ - static bool even=true; - t->S.t += 1; - t->S.f += 1.0; - if (even) { - ff_send_out(t); - even=false; - return GO_ON; - } - even=true; - return t; - } -}; - -struct Node4: ff_node_t{ - Node4(long ntasks):ntasks(ntasks) {} - myTask_t* svc(myTask_t* t){ - //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; - ++processed; - delete t; - return GO_ON; - } - void svc_end() { - if (processed != ntasks) { - abort(); - } - std::cout << "RESULT OK\n"; - } - long ntasks; - long processed=0; -}; - - -int main(int argc, char*argv[]){ - long ntasks = 1000; - if (argc>1) { - ntasks = std::stol(argv[1]); - } - - ff_pipeline pipe; - Node1 n1(ntasks); - Node2 n2; - Node3 n3; - Node4 n4(ntasks); - - pipe.add_stage(&n1); - pipe.add_stage(new WrapperOUT(&n2), true); - pipe.add_stage(new WrapperINOUT(&n3), true); - pipe.add_stage(new WrapperIN(&n4), true); - - if (pipe.run_and_wait_end()<0) { - error("running the main pipe\n"); - return -1; - } - return 0; -} From 3b5007c658741fdb2c2f10fa92b35cd8066225c4 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 14 Jan 2022 12:49:58 +0100 Subject: [PATCH 098/202] added group tests, fix in the ff_dsender.hpp --- ff/distributed/ff_dsender.hpp | 2 +- tests/distributed/test_a2aOnDemand.cpp | 2 +- tests/distributed/test_a2aOnDemand2.cpp | 2 +- tests/distributed/test_group1.cpp | 136 +++++++++++++++++++++ tests/distributed/test_group2.cpp | 138 +++++++++++++++++++++ tests/distributed/test_group3.cpp | 89 ++++++++++++++ tests/distributed/test_group4.cpp | 87 +++++++++++++ tests/distributed/test_group5.cpp | 155 ++++++++++++++++++++++++ tests/distributed/test_group6.cpp | 141 +++++++++++++++++++++ 9 files changed, 749 insertions(+), 3 deletions(-) create mode 100644 tests/distributed/test_group1.cpp create mode 100644 tests/distributed/test_group2.cpp create mode 100644 tests/distributed/test_group3.cpp create mode 100644 tests/distributed/test_group4.cpp create mode 100644 tests/distributed/test_group5.cpp create mode 100644 tests/distributed/test_group6.cpp diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 901c5b89..217eb54b 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -340,7 +340,7 @@ class ff_dsenderOD: public ff_dsender { sockets.resize(this->dest_endpoints.size()); for(size_t i=0; i < this->dest_endpoints.size(); i++){ if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; - + if (handshakeHandler(sockets[i], false) < 0) return -1; // execute the following block only if the scheduling is onDemand sockCounters[sockets[i]] = queueDim; diff --git a/tests/distributed/test_a2aOnDemand.cpp b/tests/distributed/test_a2aOnDemand.cpp index b5998813..f5a8ef72 100644 --- a/tests/distributed/test_a2aOnDemand.cpp +++ b/tests/distributed/test_a2aOnDemand.cpp @@ -120,7 +120,7 @@ int main(int argc, char*argv[]){ for(int i = 0; i < numWorkerDx; i++) dxWorkers.push_back(new MiNode(i*100)); - a2a.add_firstset(sxWorkers, 1); + a2a.add_firstset(sxWorkers, 1); // enabling on-demand distribution policy a2a.add_secondset(dxWorkers); diff --git a/tests/distributed/test_a2aOnDemand2.cpp b/tests/distributed/test_a2aOnDemand2.cpp index 8b7ba0a3..3d7bbbd2 100644 --- a/tests/distributed/test_a2aOnDemand2.cpp +++ b/tests/distributed/test_a2aOnDemand2.cpp @@ -100,7 +100,7 @@ int main(int argc, char*argv[]){ sx.push_back(new Source(items)); g1.out << sx[i]; } - a2a.add_firstset(sx, asyncdegree); + a2a.add_firstset(sx, asyncdegree); // enabling on-demand distribution policy with #asyncdegree buffer slots for(int i = 0; i < numWorkerDx; i++){ dx.push_back(new Sink((long)100*(i+1))); diff --git a/tests/distributed/test_group1.cpp b/tests/distributed/test_group1.cpp new file mode 100644 index 00000000..81f44816 --- /dev/null +++ b/tests/distributed/test_group1.cpp @@ -0,0 +1,136 @@ +/* + * FastFlow concurrent network: + * + * + * ----------------------------- + * | |--> MiNode1 --> | + * | MoNode1-->| | + * Source -->| |--> MiNode2 --> | ----> Sink + * | MoNode2-->| | + * | |--> MiNode3 --> | + * ----------------------------- + * |<---------- A2A ------- ---->| + * |<------------------- pipe ----------------------->| + * + * + * distributed version: + * + * G1 G2 + * -------- ----------------------- + * | | | |-> MiNode1 | + * | Source | ----> | MoNode1 ->| | -->| ------ + * | | | | |-> MiNode2 | | | | + * -------- | ----------------------- |--> | Sink | + * | | | | + * | ----------------------- | ------ + * | --> | | -->| G4 + * | MoNode2 ->|-> MiNode3 | + * ----------------------- + * G3 + */ + +#include +#include +#include + +#define ITEMS 100 +std::mutex mtx; // used only for pretty printing + +using namespace ff; + +struct Source : ff_monode_t{ + int* svc(int* i){ + for(int i=0; i< ITEMS; i++) + ff_send_out(new int(i)); + + return EOS; + } +}; + +struct MoNode : ff_monode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct MiNode : ff_minode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct Sink : ff_minode_t{ + int sum = 0; + int* svc(int* i){ + sum += *i; + delete i; + return GO_ON; + } + + void svc_end() { + int local_sum = 0; + for(int i = 0; i < ITEMS; i++) local_sum += i; + const std::lock_guard lock(mtx); + std::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; + } +}; + + +int main(int argc, char*argv[]){ + + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + // defining the concurrent network + ff_pipeline mainPipe; + Source source; + ff_a2a a2a; + Sink sink; + mainPipe.add_stage(&source); + mainPipe.add_stage(&a2a); + mainPipe.add_stage(&sink); + + MoNode sx1, sx2; + MiNode dx1, dx2, dx3; + + a2a.add_firstset({&sx1, &sx2}); + a2a.add_secondset({&dx1, &dx2, &dx3}); + + //----- defining the distributed groups ------ + + dGroup g1 = source.createGroup("G1"); + dGroup g2 = a2a.createGroup("G2"); + dGroup g3 = a2a.createGroup("G3"); + dGroup g4 = sink.createGroup("G4"); + + g2.in << &sx1; + g2.out << &dx1 << &dx2; + + g3.in << &sx2; + g3.out << &dx3; + // ------------------------------------------- + + // running the distributed groups + if (mainPipe.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } + + return 0; +} diff --git a/tests/distributed/test_group2.cpp b/tests/distributed/test_group2.cpp new file mode 100644 index 00000000..557e3672 --- /dev/null +++ b/tests/distributed/test_group2.cpp @@ -0,0 +1,138 @@ +/* + * FastFlow concurrent network: + * + * + * ----------------------------- + * | |--> MiNode1 --> | + * | MoNode1-->| | + * Source -->| |--> MiNode2 --> | ----> Sink + * | MoNode2-->| | + * | |--> MiNode3 --> | + * ----------------------------- + * |<---------- A2A ------- ---->| + * |<------------------- pipe ----------------------->| + * + * + * distributed version: + * + * G1 G2 + * -------- ----------------------- + * | | | |-> MiNode1 | + * | Source | ----> | MoNode1 ->| | -->| + * | | | | |-> MiNode2 | | ------ + * -------- | | | | | | | + * | | |-> MinOde3 | |--> | Sink | + * | ----------------------- | | | + * | ---------- | ------ + * | --------> | | | G4 + * | MoNode2 |---------->| + * ---------- + * G3 + */ + +#include +#include +#include + +#define ITEMS 100 +std::mutex mtx; // used only for pretty printing + +using namespace ff; + +struct Source : ff_monode_t{ + int* svc(int* i){ + for(int i=0; i< ITEMS; i++) + ff_send_out(new int(i)); + + return EOS; + } +}; + +struct MoNode : ff_monode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct MiNode : ff_minode_t{ + int processedItems = 0; + int* svc(int* i){ + ++processedItems; + return i; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + std::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + } +}; + +struct Sink : ff_minode_t{ + int sum = 0; + int* svc(int* i){ + sum += *i; + delete i; + return GO_ON; + } + + void svc_end() { + int local_sum = 0; + for(int i = 0; i < ITEMS; i++) local_sum += i; + const std::lock_guard lock(mtx); + std::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; + } +}; + + +int main(int argc, char*argv[]){ + + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + // defining the concurrent network + ff_pipeline mainPipe; + Source source; + ff_a2a a2a; + Sink sink; + mainPipe.add_stage(&source); + mainPipe.add_stage(&a2a); + mainPipe.add_stage(&sink); + + MoNode sx1, sx2; + MiNode dx1, dx2, dx3; + + a2a.add_firstset({&sx1, &sx2}); + a2a.add_secondset({&dx1, &dx2, &dx3}); + + //----- defining the distributed groups ------ + + dGroup g1 = source.createGroup("G1"); + dGroup g2 = a2a.createGroup("G2"); + dGroup g3 = a2a.createGroup("G3"); + dGroup g4 = sink.createGroup("G4"); + + g2.in << &sx1; + g2.out << &dx1 << &dx2 << &dx3; + + g3.in << &sx2; + g3.out << &dx2; + + // ------------------------------------------- + + // running the distributed groups + if (mainPipe.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } + + return 0; +} diff --git a/tests/distributed/test_group3.cpp b/tests/distributed/test_group3.cpp new file mode 100644 index 00000000..4b1dc83b --- /dev/null +++ b/tests/distributed/test_group3.cpp @@ -0,0 +1,89 @@ +/* + * FastFlow concurrent network: + * + * ------------------------ + * | Source1-->| | + * | | --> Sink1 | + * | Source2-->| | + * | | --> Sink2 | + * | Source3-->| | + * ------------------------ + * + * distributed version: + * + * ------------------------ + * | | + * | Source1 -->| | + * | | --> Sink1 | ------- + * | Source2 -->| | | | + * | | --------->| --> | Sink2 | + * | Source3 -->| | | | + * | | ------- + * ------------------------ + * G1 G2 + * + */ + +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct Source : ff_monode_t{ + + std::string* svc(std::string* in){ + long outchannels = get_num_outchannels(); + + for(long i = 0; i < outchannels; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(get_my_id()) + " for " + std::to_string(i)), i); + + return EOS; + } +}; + + +struct Sink : ff_minode_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << *in << " received by Sink " << get_my_id() << " from " << get_channel_id() << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + ff_a2a a2a; + Source source1; + Source source2; + Source source3; + Sink sink1; + Sink sink2; + a2a.add_firstset({&source1, &source2, &source3}); + a2a.add_secondset({&sink1, &sink2}); + + //----- defining the distributed groups ------ + + dGroup g1 = a2a.createGroup("G1"); + dGroup g2 = a2a.createGroup("G2"); + + g1.out << &source1 << &source2 << &source3; // &sink1 automatically added to G1 + //g1.in << &sink1; // redundant!!! + g2.in << &sink2; + + // ------------------------------------------- + + // running the distributed groups + if (a2a.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group4.cpp b/tests/distributed/test_group4.cpp new file mode 100644 index 00000000..3f583983 --- /dev/null +++ b/tests/distributed/test_group4.cpp @@ -0,0 +1,87 @@ +/* + * FastFlow concurrent network: + * + * ------------------------ + * | Source1-->| | + * | | --> Sink1 | + * | Source2-->| | + * | | --> Sink2 | + * | Source3-->| | + * ------------------------ + * + * distributed version: + * + * ------------------- -------------------- + * | | | | + * | Source1 -->| | | |-> Sink1 | + * | | --> | --> | | | + * | Source2 -->| | | Source3 ->|-> Sink2 | + * | | | | + * ------------------- --------------------- + * G1 G2 + * + */ + +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct Source : ff_monode_t{ + + std::string* svc(std::string* in){ + long outchannels = get_num_outchannels(); + + for(long i = 0; i < outchannels; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(get_my_id()) + " for " + std::to_string(i)), i); + + return EOS; + } +}; + + +struct Sink : ff_minode_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << *in << " received by Sink " << get_my_id() << " from " << get_channel_id() << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + + ff_a2a a2a; + Source source1; + Source source2; + Source source3; + Sink sink1; + Sink sink2; + a2a.add_firstset({&source1, &source2, &source3}); + a2a.add_secondset({&sink1, &sink2}); + + //----- defining the distributed groups ------ + + dGroup g1 = a2a.createGroup("G1"); + dGroup g2 = a2a.createGroup("G2"); + + g1.out << &source1 << &source2; + g2.in << &sink1 << &sink2; // &source3 automatically added to the group + + // ------------------------------------------- + + // running the distributed groups + if (a2a.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group5.cpp b/tests/distributed/test_group5.cpp new file mode 100644 index 00000000..5b64ac86 --- /dev/null +++ b/tests/distributed/test_group5.cpp @@ -0,0 +1,155 @@ +/* + * FastFlow concurrent network: + * + * ----------------- --------------------- + * | S1 --> | --> W1 | | --> T1 --> | | + * | | | | | | + * | S2 --> | --> W2 |--> | --> T2 --> | --> K1 | + * | | | | | | + * | S3 --> | --> W3 | | --> T3 --> | | + * ----------------- --------------------- + * |<----- A2A1 ---->| |<------ A2A2 ------->| + * |<----------------- pipe ------------------>| + * + * distributed version: + * + * G1 G3 + * ---------------- ----------------- + * | | | T1 -->| | + * | S1 -->| --> W1 | | | | + * | | | ---> | T2 -->| --> K1 | + * | S2 -->| --> W2 | ^ | | | + * | | | | T3 -->| | + * ---------------- | ----------------- + * | ^ | + * G2 v | | + * ---------------- | + * | | | + * | S3 -->| --> W3 |-- + * | | + * ---------------- + */ + +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct S : ff_monode_t{ + + std::string* svc(std::string* in){ + long outchannels = get_num_outchannels(); + + for(long i = 0; i < outchannels; i++) + ff_send_out_to(new std::string("[Task generated from S" + std::to_string(get_my_id()) + " for W" + std::to_string(i) + "]"), i); + + return EOS; + } +}; + +struct W_left: ff_minode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[W_left" << get_my_id() << " received " << *in << " from S" << get_channel_id() << "]" << std::endl; + + return in; + } +}; +struct W_right: ff_monode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + long outchannels = get_num_outchannels(); + + for(long i = 0; i < outchannels; i++) + ff_send_out_to(new std::string("[Task generated from W_right" + std::to_string(get_my_id()) + " to T" + std::to_string(i) + "]"), i); + + return GO_ON; + } +}; + +struct T_left: ff_minode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[T_left" << get_my_id() << " reiceived " << *in << " from W" << get_channel_id() << "]" << std::endl; + + return in; + } +}; +struct T_right: ff_monode_t { + + std::string* svc(std::string* in){ + return in; + } +}; + +struct K : ff_minode_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + + std::cout << "[K received " << *in << " from T" << get_channel_id() << "]" << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + + using W = ff_comb; + using T = ff_comb; + + ff_pipeline pipe; + ff_a2a a2a1; + ff_a2a a2a2; + S s1, s2, s3; + W w1(new W_left, new W_right, true, true); + W w2(new W_left, new W_right, true, true); + W w3(new W_left, new W_right, true, true); + T t1(new T_left, new T_right, true, true); + T t2(new T_left, new T_right, true, true); + T t3(new T_left, new T_right, true, true); + K k; + + pipe.add_stage(&a2a1); + pipe.add_stage(&a2a2); + + a2a1.add_firstset({&s1, &s2, &s3}); + a2a1.add_secondset({&w1, &w2, &w3}); + + a2a2.add_firstset({&t1, &t2, &t3}); + a2a2.add_secondset({&k}); + + + //----- defining the distributed groups ------ + + auto g1 = a2a1.createGroup("G1"); + auto g2 = a2a1.createGroup("G2"); + auto g3 = a2a2.createGroup("G3"); + + g1.out << &s1 << &s2 << &w1 << &w2; + g1.in << &w1 << &w2; + + g2.out << &s3 << &w3; + g2.in << &w3; + + //g3.in << &t1 << &t2 << &t3; + + // ------------------------------------------- + + // running the distributed groups + if (pipe.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group6.cpp b/tests/distributed/test_group6.cpp new file mode 100644 index 00000000..581a56e9 --- /dev/null +++ b/tests/distributed/test_group6.cpp @@ -0,0 +1,141 @@ +/* + * FastFlow concurrent network: + * + * ----------------- --------------------- + * | S1 --> | --> W1 | | --> T1 --> | | + * | | | | | | + * | S2 --> | --> W2 |--> | --> T2 --> | --> K1 | + * | | | | | | + * | S3 --> | --> W3 | | --> T3 --> | | + * ----------------- --------------------- + * |<----- A2A1 ---->| |<------ A2A2 ------->| + * |<----------------- pipe ------------------>| + * + * distributed version: + * + * G1 G2 + * ---------------- ----------------- + * | | | T1 -->| | + * | S1 -->| --> W1 | | | | + * | | | ---> | T2 -->| --> K1 | + * | S2 -->| --> W2 | | | | + * | | | | | | + * | S3 -->| --> W3 | | T3 -->| | + * ---------------- ----------------- + * + */ + +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct S : ff_monode_t{ + + std::string* svc(std::string* in){ + long outchannels = get_num_outchannels(); + + for(long i = 0; i < outchannels; i++) + ff_send_out_to(new std::string("[Task generated from S" + std::to_string(get_my_id()) + " for W" + std::to_string(i) + "]"), i); + + return EOS; + } +}; + +struct W_left: ff_minode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[W_left" << get_my_id() << " received " << *in << " from S" << get_channel_id() << "]" << std::endl; + + return in; + } +}; +struct W_right: ff_monode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + long outchannels = get_num_outchannels(); + + for(long i = 0; i < outchannels; i++) + ff_send_out_to(new std::string("[Task generated from W_right" + std::to_string(get_my_id()) + " to T" + std::to_string(i) + "]"), i); + + return GO_ON; + } +}; + +struct T_left: ff_minode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[T_left" << get_my_id() << " reiceived " << *in << " from W" << get_channel_id() << "]" << std::endl; + + return in; + } +}; +struct T_right: ff_monode_t { + + std::string* svc(std::string* in){ + return in; + } +}; + +struct K : ff_minode_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + + std::cout << "[K received " << *in << " from T" << get_channel_id() << "]" << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + + using W = ff_comb; + using T = ff_comb; + + ff_pipeline pipe; + ff_a2a a2a1; + ff_a2a a2a2; + S s1, s2, s3; + W w1(new W_left, new W_right, true, true); + W w2(new W_left, new W_right, true, true); + W w3(new W_left, new W_right, true, true); + T t1(new T_left, new T_right, true, true); + T t2(new T_left, new T_right, true, true); + T t3(new T_left, new T_right, true, true); + K k; + + pipe.add_stage(&a2a1); + pipe.add_stage(&a2a2); + + a2a1.add_firstset({&s1, &s2, &s3}); + a2a1.add_secondset({&w1, &w2, &w3}); + + a2a2.add_firstset({&t1, &t2, &t3}); + a2a2.add_secondset({&k}); + + + //----- defining the distributed groups ------ + + auto g1 = a2a1.createGroup("G1"); + auto g2 = a2a1.createGroup("G2"); + + // ------------------------------------------- + + // running the distributed groups + if (pipe.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + return 0; +} From 53d771cc5c673f4bfce0ed05a7a9093e94a184e1 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 28 Jan 2022 17:38:46 +0100 Subject: [PATCH 099/202] Working solution before mooving to new annotation mechanisms --- ff/all2all.hpp | 19 ++- ff/combine.hpp | 7 + ff/dff.hpp | 3 +- ff/distributed/ff_dgroup.hpp | 63 +------- ff/distributed/ff_dgroups.hpp | 212 ++++++++++++++++++++++---- ff/distributed/ff_network.hpp | 3 +- ff/node.hpp | 13 +- ff/optimize.hpp | 3 +- ff/pipeline.hpp | 28 ++-- tests/distributed/Makefile | 6 +- tests/distributed/test_a2a_h4.cpp | 7 +- tests/distributed/test_complete1.cpp | 5 +- tests/distributed/test_complete9.cpp | 62 ++++++++ tests/distributed/test_complete9.json | 13 ++ tests/distributed/test_group1.cpp | 17 +-- tests/distributed/test_group2.cpp | 8 +- 16 files changed, 328 insertions(+), 141 deletions(-) create mode 100644 tests/distributed/test_complete9.cpp create mode 100644 tests/distributed/test_complete9.json diff --git a/ff/all2all.hpp b/ff/all2all.hpp index c2f8d6ec..9997ee3f 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -39,9 +39,6 @@ namespace ff { // forward declarations static ff_node* ispipe_getlast(ff_node*); -#ifdef DFF_ENABLED -class dGroup; -#endif class ff_a2a: public ff_node { friend class ff_farm; @@ -456,6 +453,9 @@ class ff_a2a: public ff_node { return ret; } +#ifdef DFF_ENABLED + int run_and_wait_end(); +#else int run_and_wait_end() { if (isfrozen()) { // TODO error("A2A: Error: feature not yet supported\n"); @@ -465,6 +465,7 @@ class ff_a2a: public ff_node { if (wait()<0) return -1; return 0; } +#endif /** * \brief checks if the node is running @@ -695,7 +696,17 @@ class ff_a2a: public ff_node { #endif #ifdef DFF_ENABLED - ff::dGroup& createGroup(std::string); + virtual bool isSerializable(){ + svector outputs; this->get_out_nodes(outputs); + for(ff_node* output: outputs) if (!output->isSerializable()) return false; + return true; + } + + virtual bool isDeserializable(){ + svector inputs; this->get_in_nodes(inputs); + for(ff_node* input: inputs) if(!input->isDeserializable()) return false; + return true; + } #endif diff --git a/ff/combine.hpp b/ff/combine.hpp index 5dea0665..301f9794 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -310,6 +310,13 @@ class ff_comb: public ff_minode { out << "FastFlow trace not enabled\n"; } #endif + +#ifdef DFF_ENABLED + virtual bool isSerializable(){ return comp_nodes[1]->isSerializable(); } + virtual bool isDeserializable(){ return comp_nodes[0]->isDeserializable(); } + virtual decltype(serializeF) getSerializationFunction(){ return comp_nodes[1]->getSerializationFunction(); } + virtual decltype(deserializeF) getDeserializationFunction(){ return comp_nodes[0]->getDeserializationFunction(); } +#endif protected: ff_comb():ff_minode() {} diff --git a/ff/dff.hpp b/ff/dff.hpp index 6fc9ceb9..e50c01b5 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -37,6 +37,7 @@ #include #include #include -#include + +#include #endif /* FF_DFF_HPP */ diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 18fc5f57..f821b016 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -428,30 +428,7 @@ bool MySet::check_inout(ff_node* node){ } void dGroups::consolidateGroups(){ - for(size_t i = 0; i < parsedGroups.size(); i++){ - const G & g = parsedGroups[i]; - if (groups.find(g.name) != groups.end()) - switch (this->usedProtocol){ - case Proto::TCP : reinterpret_cast(groups[g.name])->setEndpoint(ff_endpoint(g.address, g.port)); break; - case Proto::MPI : reinterpret_cast(groups[g.name])->setEndpoint(ff_endpoint(i)); break; - } - else { - std::cout << "Cannot find group: " << g.name << std::endl; - throw FF_Exception("A specified group in the configuration file has not been implemented! :("); - } - } - - - for(G& g : parsedGroups){ - dGroup* groupRef = reinterpret_cast(groups[g.name]); - for(std::string& conn : g.Oconn) - if (groups.find(conn) != groups.end()) - groupRef->setDestination(reinterpret_cast(groups[conn])->getEndpoint()); - else throw FF_Exception("A specified destination has a wrong name! :("); - - groupRef->setExpectedInputConnections(this->expectedInputConnections(g.name)); - } - + } bool dGroups::isBuildByMyBuildingBlock(const std::string gName) { @@ -461,44 +438,6 @@ bool dGroups::isBuildByMyBuildingBlock(const std::string gName) { return g1->getParent() == g2->getParent(); } - -void dGroups::parseConfig(std::string configFile){ - - std::ifstream is(configFile); - - if (!is) throw FF_Exception("Unable to open configuration file for the program!"); - - try { - cereal::JSONInputArchive ari(is); - ari(cereal::make_nvp("groups", parsedGroups)); - - // get the protocol to be used from the configuration file - try { - std::string tmpProtocol; - ari(cereal::make_nvp("protocol", tmpProtocol)); - if (tmpProtocol == "MPI"){ - #ifdef DFF_MPI - this->usedProtocol = Proto::MPI; - #else - std::cout << "NO MPI support! Falling back to TCP\n"; - this->usedProtocol = Proto::TCP; - #endif - - } else this->usedProtocol = Proto::TCP; - } catch (cereal::Exception&) { - ari.setNextName(nullptr); - this->usedProtocol = Proto::TCP; - } - - } catch (const cereal::Exception& e){ - std::cerr << "Error parsing the JSON config file. Check syntax and structure of the file and retry!" << std::endl; - exit(EXIT_FAILURE); - } - } - -} - - // redefinition of createGroup methods for ff_a2a and ff_pipeline ff::dGroup& ff_a2a::createGroup(std::string name){ diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 49910313..3f244c21 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -3,15 +3,18 @@ #include #include +#include #include #include +#include #include -#include -#include +#include #include +#include +#include #include #include @@ -28,27 +31,62 @@ enum Proto {TCP , MPI}; class dGroups { public: - + template friend class SingleNodeAnnotator; static dGroups* Instance(){ - static dGroups dg; - return &dg; + static dGroups dg; + return &dg; } - ~dGroups() { - for (auto g : groups) + + /*~dGroups() { + for (auto g : this->groups) if (g.second) delete g.second; groups.clear(); - } + }*/ Proto usedProtocol; - void parseConfig(std::string); + void parseConfig(std::string configFile){ + std::ifstream is(configFile); - void consolidateGroups(); + if (!is) throw FF_Exception("Unable to open configuration file for the program!"); - void addGroup(std::string label, ff_node* g){ groups.insert(make_pair(label, g));} + try { + cereal::JSONInputArchive ari(is); + ari(cereal::make_nvp("groups", this->parsedGroups)); + + // get the protocol to be used from the configuration file + try { + std::string tmpProtocol; + ari(cereal::make_nvp("protocol", tmpProtocol)); + if (tmpProtocol == "MPI"){ + #ifdef DFF_MPI + this->usedProtocol = Proto::MPI; + #else + std::cout << "NO MPI support! Falling back to TCP\n"; + this->usedProtocol = Proto::TCP; + #endif + + } else this->usedProtocol = Proto::TCP; + } catch (cereal::Exception&) { + ari.setNextName(nullptr); + this->usedProtocol = Proto::TCP; + } + + } catch (const cereal::Exception& e){ + std::cerr << "Error parsing the JSON config file. Check syntax and structure of the file and retry!" << std::endl; + exit(EXIT_FAILURE); + } + } - int size(){ return groups.size();} + void annotateGroup(std::string name, ff_node* parentBB){ + if (annotatedGroups.contains(name)){ + std::cerr << "Group " << name << " created twice. Error!\n"; abort(); + } + annotatedGroups[name].parentBB = parentBB; + } + + int size(){ return annotatedGroups.size();} void setRunningGroup(std::string g){this->runningGroup = g;} @@ -62,20 +100,24 @@ class dGroups { bool isBuildByMyBuildingBlock(const std::string gName); - int run_and_wait_end(ff_node* parent){ - - // perfrom connection between groups - this->consolidateGroups(); - - if (groups.find(runningGroup) == groups.end()){ + int run_and_wait_end(ff_pipeline* parent){ + if (annotatedGroups.find(runningGroup) == annotatedGroups.end()){ ff::error("The group specified is not found nor implemented!\n"); return -1; } - ff_node* runningGroup = this->groups[this->runningGroup]; + + // qui dovrei creare la rappresentazione intermedia di tutti + this->prepareIR(parent); + + // buildare il farm dalla rappresentazione intermedia del gruppo che devo rannare + + // rannere il farm come sotto! + + //ff_node* runningGroup = this->groups[this->runningGroup]; - if (runningGroup->run(parent) < 0) return -1; - if (runningGroup->wait() < 0) return -1; + //if (runningGroup->run(parent) < 0) return -1; + //if (runningGroup->wait() < 0) return -1; #ifdef DFF_MPI if (usedProtocol == Proto::MPI) @@ -85,13 +127,14 @@ class dGroups { return 0; } protected: - dGroups() : groups(), runningGroup() { + dGroups() : runningGroup() { // costruttore } - + std::map> annotated; private: inline static dGroups* i = nullptr; - std::map groups; + std::map annotatedGroups; + std::string runningGroup; // helper class to parse config file Json @@ -131,13 +174,104 @@ class dGroups { } - int expectedInputConnections(std::string groupName){ - int result = 0; - for (const G& g : parsedGroups) - if (g.name != groupName) - for (const std::string& conn : g.Oconn) - if (conn == groupName) result++; - return result; + void prepareIR(ff_pipeline* parentPipe){ + ff::ff_IR& runningGroup_IR = annotatedGroups[this->runningGroup]; + + // TODO: check coverage all 1st level + + for(size_t i = 0; i < parsedGroups.size(); i++){ + const G & g = parsedGroups[i]; + + // throw an error if a group in the configuration has not been annotated in the current program + if (!annotatedGroups.contains(g.name)) throw FF_Exception("present in the configuration file has not been implemented! :("); + + // annotate the listen endpoint for the specified group + annotatedGroups[g.name].listenEndpoint = this->usedProtocol == Proto::TCP ? ff_endpoint(g.address, g.port) : ff_endpoint(i); + } + + auto g = std::find_if(parsedGroups.begin(), parsedGroups.end(), [this](const G& g){return g.name == getRunningGroup();}); + for(std::string& conn : g->Oconn) + // build the list of outgoing connections for each group + if (annotatedGroups.find(conn) != annotatedGroups.end()) + runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[conn].listenEndpoint); + else throw FF_Exception("A specified destination has a wrong name! :("); + + + // compute how many expected EOS the future running must wait before exiting + size_t expectedEOS = 0; + for (const G& g : parsedGroups) + if (g.name != this->runningGroup) + for (const std::string& conn : g.Oconn) + if (conn == this->runningGroup) expectedEOS++; + runningGroup_IR.expectedEOS = expectedEOS; + + + // build the map parentBB -> + std::map> parentBB2GroupsName; + for(auto& p: annotatedGroups) parentBB2GroupsName[p.second.parentBB].insert(p.first); + + // iterate over the 1st level building blocks and the list of create groups + for(const auto& pair : parentBB2GroupsName){ + + //if the current group is build from another building block we can just skip this! + if (!pair.second.contains(this->runningGroup)) + continue; + + bool isSrc = isSource(pair.first, parentPipe); + bool isSnk = isSink(pair.first, parentPipe); + // check if from the under analysis 1st level building block it has been created just one group + if (pair.second.size() == 1){ + // if the unique group is not the one i'm going to run just skip this 1st level building block + if ((isSrc || pair.first->isDeserializable()) && (isSnk || pair.first->isSerializable())) + runningGroup_IR.insertInList(std::make_pair(pair.first, SetEnum::L), true); + continue; // skip anyway + } + + // if i'm here it means that from this 1st level building block, multiple groups have been created! (An Error or an A2A or a Farm BB) + std::set> children = getChildBB(pair.first); + + std::erase_if(children, [&](auto& p){ + if (!annotated.contains(p.first)) return false; + auto& ann = annotated[p.first]; + if (((isSrc && p.second == SetEnum::L) || ann.first == NodeIOTypes::IN || ann.first == NodeIOTypes::INOUT) && (isSnk || ann.first == NodeIOTypes::OUT || ann.first == NodeIOTypes::INOUT)){ + annotatedGroups[ann.second].insertInList(std::make_pair(p.first, p.second)); return true; + } + return false; + }); + + if (!children.empty()){ + // seconda passata per verificare se ce da qualche parte c'è copertura completa altrimenti errore + for(const std::string& gName : pair.second){ + ff_IR& ir = annotatedGroups[gName]; + ir.computeCoverage(); + if (ir.coverageL) + std::erase_if(children, [&](auto& p){ + if (p.second == SetEnum::R && (isSnk || p.first->isSerializable()) && p.first->isDeserializable()){ + ir.insertInList(std::make_pair(p.first, SetEnum::R)); return true; + } + return false; + }); + + if (ir.coverageR) + std::erase_if(children, [&](auto& p){ + if (p.second == SetEnum::L && (isSrc || p.first->isDeserializable()) && p.first->isSerializable()){ + ir.insertInList(std::make_pair(p.first, SetEnum::L)); return true; + } + return false; + }); + } + + // ancora dei building block figli non aggiunti a nessun gruppo, lancio errore e abortisco l'esecuzione + if (!children.empty()){ + std::cerr << "Some building block has not been annotated and no coverage found! You missed something. Aborting now" << std::endl; + abort(); + } + } + } + + runningGroup_IR.buildIndexes(); + + runningGroup_IR.print(); } }; @@ -242,7 +376,21 @@ static inline int DFF_Init(int& argc, char**& argv){ static inline const std::string DFF_getMyGroup() { return dGroups::Instance()->getRunningGroup(); } - + +int ff_pipeline::run_and_wait_end() { + dGroups::Instance()->run_and_wait_end(this); + return 0; +} + +int ff_a2a::run_and_wait_end(){ + ff_pipeline p; + p.add_stage(this); + dGroups::Instance()->run_and_wait_end(&p); + return 0; } + + + +} #endif diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 7655e3aa..a3315e9b 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -97,7 +98,7 @@ struct ack_t { struct ff_endpoint { ff_endpoint(){} - ff_endpoint(std::string addr, int port) : address(addr), port(port) {} + ff_endpoint(std::string addr, int port) : address(std::move(addr)), port(port) {} ff_endpoint(int rank) : port(rank) {} const int getRank() {return port;} std::string address, groupName; diff --git a/ff/node.hpp b/ff/node.hpp index fac6786e..0df0570f 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -48,7 +48,6 @@ #ifdef DFF_ENABLED -#include #include #include #include @@ -60,6 +59,10 @@ namespace ff { +#ifdef DFF_ENABLED +struct GroupInterface; +#endif + static void* FF_EOS = (void*)(ULLONG_MAX); /// automatically propagated static void* FF_EOS_NOFREEZE = (void*)(ULLONG_MAX-1); /// not automatically propagated static void* FF_EOSW = (void*)(ULLONG_MAX-2); /// propagated only by farm's stages @@ -1248,8 +1251,12 @@ class ff_node { std::function serializeF; std::function deserializeF; - bool isSerializable(){ return (bool)serializeF; } - bool isDeserializable(){ return (bool)deserializeF; } + virtual bool isSerializable(){ return (bool)serializeF; } + virtual bool isDeserializable(){ return (bool)deserializeF; } + virtual decltype(serializeF) getSerializationFunction(){return serializeF;} + virtual decltype(deserializeF) getDeserializationFunction(){return deserializeF;} + + GroupInterface createGroup(std::string); #endif protected: diff --git a/ff/optimize.hpp b/ff/optimize.hpp index 4c5f48a2..034e0880 100644 --- a/ff/optimize.hpp +++ b/ff/optimize.hpp @@ -793,6 +793,5 @@ static inline int optimize_static(ff_pipeline& pipe, const OptLevel& opt=OptLeve } - -} // namespace ff +} // namespace ff #endif /* FF_OPTIMIZE_HPP */ diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 18e5eea9..06afe3dd 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -41,17 +41,11 @@ #include #endif -#ifdef DFF_ENABLED -#include - -#endif - namespace ff { // forward declarations class ff_pipeline; -class dGroup; static inline int optimize_static(ff_pipeline&, const OptLevel&); template static inline int combine_with_firststage(ff_pipeline&,T*,bool=false); @@ -1204,13 +1198,10 @@ class ff_pipeline: public ff_node { * * Blocking behaviour w.r.t. main thread to be clarified */ +#ifdef DFF_ENABLED + int run_and_wait_end(); +#else int run_and_wait_end() { - #ifdef DFF_ENABLED - dGroups::Instance()->run_and_wait_end(this); - return 0; - #endif - - if (isfrozen()) { // TODO error("PIPE: Error: feature not yet supported\n"); return -1; @@ -1220,6 +1211,7 @@ class ff_pipeline: public ff_node { if (wait()<0) return -1; return 0; } +#endif /** * \related ff_pipe @@ -1486,7 +1478,17 @@ class ff_pipeline: public ff_node { #endif #ifdef DFF_ENABLED - ff::dGroup& createGroup(std::string); + virtual bool isSerializable(){ + svector outputs; this->get_out_nodes(outputs); + for(ff_node* output: outputs) if (!output->isSerializable()) return false; + return true; + } + + virtual bool isDeserializable(){ + svector inputs; this->get_in_nodes(inputs); + for(ff_node* input: inputs) if(!input->isDeserializable()) return false; + return true; + } #endif protected: diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index b443ecc3..0149d1bd 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -25,7 +25,7 @@ CXXFLAGS += -std=c++20 ifdef DEBUG - OPTIMIZE_FLAGS += -g -fno-inline-functions + OPTIMIZE_FLAGS += -g -ggdb -fno-inline-functions else OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG endif @@ -45,12 +45,12 @@ endif ifdef FF_HOME INCS += -I$(FF_HOME) else - INCS += -I ~/fastflow + INCS += -I ~/SPM/fastflow endif ifdef CEREAL_HOME INCS += -I$(CEREAL_HOME) else - INCS += -I ~/cereal + INCS += -I ~/SPM/cereal endif diff --git a/tests/distributed/test_a2a_h4.cpp b/tests/distributed/test_a2a_h4.cpp index 68960e95..7afe22b5 100644 --- a/tests/distributed/test_a2a_h4.cpp +++ b/tests/distributed/test_a2a_h4.cpp @@ -7,9 +7,10 @@ * * * G0: Source - * G1: Forwarer1, Forwarder2, Sink1 - * G2: Forwarder3, Sink2 - * G3: StringPrinter + * G1: Forwarer1 + * G2: Sink1 + * G3: Forwarder3, Sink2 + * G4: StringPrinter * */ diff --git a/tests/distributed/test_complete1.cpp b/tests/distributed/test_complete1.cpp index f219c833..1b837fcc 100644 --- a/tests/distributed/test_complete1.cpp +++ b/tests/distributed/test_complete1.cpp @@ -15,7 +15,7 @@ #include #include #include - +using namespace ff; #define ITEMS 100 std::mutex mtx; // used only for pretty printing @@ -72,9 +72,7 @@ struct Sink : ff::ff_minode_t{ int main(int argc, char*argv[]){ - DFF_Init(argc, argv); - ff_pipeline mainPipe; ff::ff_a2a a2a; Source s; @@ -92,7 +90,6 @@ int main(int argc, char*argv[]){ a2a.add_secondset({&dx1, &dx2, &dx3}); //mainPipe.run_and_wait_end(); - auto g1 = sp.createGroup("G1"); auto g2 = a2a.createGroup("G2"); auto g3 = sinkp.createGroup("G3"); diff --git a/tests/distributed/test_complete9.cpp b/tests/distributed/test_complete9.cpp new file mode 100644 index 00000000..45140456 --- /dev/null +++ b/tests/distributed/test_complete9.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct Source : ff_monode_t{ + int numWorker, generatorID; + Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + std::string* svc(std::string* in){ + for(int i = 0; i < numWorker; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + return EOS; + } +}; + + +struct Sink : ff_minode_t{ + int sinkID; + Sink(int id): sinkID(id) {} + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + ff::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << ff::endl; + delete in; + return this->GO_ON; + } +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + + ff_a2a a2a; + std::vector firstSet; + std::vector secondSet; + auto g1 = a2a.createGroup("G1"); + auto g2 = a2a.createGroup("G2"); + + for(int i = 0 ; i < 3; i++){ + auto s = new Source(4, i); + firstSet.push_back(s); + g1.out << s; + } + + for(int i = 0; i < 4; i++){ + auto s = new Sink(i); + secondSet.push_back(s); + g2.in << s; + } + + a2a.add_firstset(firstSet); + a2a.add_secondset(secondSet); + + ff_Pipe p(&a2a); + p.run_and_wait_end(); +} \ No newline at end of file diff --git a/tests/distributed/test_complete9.json b/tests/distributed/test_complete9.json new file mode 100644 index 00000000..36ddce0b --- /dev/null +++ b/tests/distributed/test_complete9.json @@ -0,0 +1,13 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:9004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:9005" + } + ] +} diff --git a/tests/distributed/test_group1.cpp b/tests/distributed/test_group1.cpp index 81f44816..0b97fc51 100644 --- a/tests/distributed/test_group1.cpp +++ b/tests/distributed/test_group1.cpp @@ -113,17 +113,16 @@ int main(int argc, char*argv[]){ a2a.add_secondset({&dx1, &dx2, &dx3}); //----- defining the distributed groups ------ + auto g1 = source.createGroup("G1"); + auto g2 = a2a.createGroup("G2"); + auto g3 = a2a.createGroup("G3"); + auto g4 = sink.createGroup("G4"); - dGroup g1 = source.createGroup("G1"); - dGroup g2 = a2a.createGroup("G2"); - dGroup g3 = a2a.createGroup("G3"); - dGroup g4 = sink.createGroup("G4"); - - g2.in << &sx1; - g2.out << &dx1 << &dx2; + g2.in << &sx1 << &dx1 << &dx2; + g2.out << &sx1 << &dx1 << &dx2; - g3.in << &sx2; - g3.out << &dx3; + g3.in << &sx2 << &dx3; + g3.out << &dx3 << &sx2; // ------------------------------------------- // running the distributed groups diff --git a/tests/distributed/test_group2.cpp b/tests/distributed/test_group2.cpp index 557e3672..c4c9c411 100644 --- a/tests/distributed/test_group2.cpp +++ b/tests/distributed/test_group2.cpp @@ -115,10 +115,10 @@ int main(int argc, char*argv[]){ //----- defining the distributed groups ------ - dGroup g1 = source.createGroup("G1"); - dGroup g2 = a2a.createGroup("G2"); - dGroup g3 = a2a.createGroup("G3"); - dGroup g4 = sink.createGroup("G4"); + auto g1 = source.createGroup("G1"); + auto g2 = a2a.createGroup("G2"); + auto g3 = a2a.createGroup("G3"); + auto g4 = sink.createGroup("G4"); g2.in << &sx1; g2.out << &dx1 << &dx2 << &dx3; From a4837dbe3a20a84337a46892c383244041f077c9 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 28 Jan 2022 17:57:29 +0100 Subject: [PATCH 100/202] Working solution with the new annotation. Check test_group1.cpp to see the new interface --- ff/distributed/ff_dgroups.hpp | 10 +-- ff/distributed/ff_dinterface.hpp | 30 +++++++ ff/distributed/ff_dintermediate.hpp | 133 ++++++++++++++++++++++++++++ ff/distributed/ff_dutils.hpp | 62 +++++++++++++ tests/distributed/test_group1.cpp | 15 ++-- 5 files changed, 235 insertions(+), 15 deletions(-) create mode 100644 ff/distributed/ff_dinterface.hpp create mode 100644 ff/distributed/ff_dintermediate.hpp create mode 100644 ff/distributed/ff_dutils.hpp diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 3f244c21..97835527 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -31,7 +31,7 @@ enum Proto {TCP , MPI}; class dGroups { public: - template friend class SingleNodeAnnotator; + friend struct GroupInterface; static dGroups* Instance(){ static dGroups dg; return &dg; @@ -130,7 +130,7 @@ class dGroups { dGroups() : runningGroup() { // costruttore } - std::map> annotated; + std::map annotated; private: inline static dGroups* i = nullptr; std::map annotatedGroups; @@ -232,9 +232,9 @@ class dGroups { std::erase_if(children, [&](auto& p){ if (!annotated.contains(p.first)) return false; - auto& ann = annotated[p.first]; - if (((isSrc && p.second == SetEnum::L) || ann.first == NodeIOTypes::IN || ann.first == NodeIOTypes::INOUT) && (isSnk || ann.first == NodeIOTypes::OUT || ann.first == NodeIOTypes::INOUT)){ - annotatedGroups[ann.second].insertInList(std::make_pair(p.first, p.second)); return true; + std::string& groupName = annotated[p.first]; + if (((isSrc && p.second == SetEnum::L) || p.first->isDeserializable()) && (isSnk || p.first->isSerializable())){ + annotatedGroups[groupName].insertInList(std::make_pair(p.first, p.second)); return true; } return false; }); diff --git a/ff/distributed/ff_dinterface.hpp b/ff/distributed/ff_dinterface.hpp new file mode 100644 index 00000000..87b59113 --- /dev/null +++ b/ff/distributed/ff_dinterface.hpp @@ -0,0 +1,30 @@ +#include +#include +#include + +namespace ff { + +struct GroupInterface { + std::string name; + GroupInterface(std::string name) : name(name){} + + GroupInterface& operator<<(ff_node* node){ + auto& annotated = dGroups::Instance()->annotated; + auto handler = annotated.find(node); + if (handler == annotated.end()) + annotated[node] = name; + else if (handler->second != name){ + std::cerr << "Node has been annotated in group " << name << " and in group " << handler->second << "! Aborting\n"; + abort(); + } + return *this; + } +}; + + +GroupInterface ff_node::createGroup(std::string name){ + dGroups::Instance()->annotateGroup(name, this); + return GroupInterface(name); +} + +} \ No newline at end of file diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp new file mode 100644 index 00000000..107b599e --- /dev/null +++ b/ff/distributed/ff_dintermediate.hpp @@ -0,0 +1,133 @@ +#ifndef IR_HPP +#define IR_HPP +#include +#include +#include +#include +#include +#include + +namespace ff { + +class ff_IR { + friend class dGroups; + // set to true if the group contains the whole parent building block + bool wholeParent = false; +protected: + void computeCoverage(){ + if (!parentBB->isAll2All()) return; + ff_a2a* a2a = reinterpret_cast(parentBB); + coverageL = coverageR = true; + for(ff_node* n : a2a->getFirstSet()) + if (!L.contains(n)) {coverageL = false; break;} + for(ff_node* n : a2a->getSecondSet()) + if (!R.contains(n)) {coverageR = false; break;} + } + + void buildIndexes(){ + + if (!L.empty()){ + ff::svector parentInputs; + parentBB->get_in_nodes(parentInputs); + + ff::svector LOutputs; + if (!parentBB->isAll2All() || wholeParent) parentBB->get_out_nodes(LOutputs); + else for(ff_node* n : reinterpret_cast(parentBB)->getFirstSet()) n->get_out_nodes(LOutputs); + + for(ff_node* n : L){ + ff::svector bbInputs; n->get_in_nodes(bbInputs); + for(ff_node* bbInput : bbInputs) + inputL.push_back(std::find(parentInputs.begin(), parentInputs.end(), bbInput) - parentInputs.begin()); + + ff::svector bbOutputs; n->get_out_nodes(bbOutputs); + for(ff_node* bbOutput : bbOutputs) + outputL.push_back(std::find(LOutputs.begin(), LOutputs.end(), bbOutput) - LOutputs.begin()); + } + } + + if (!R.empty() && parentBB->isAll2All() && !wholeParent){ + ff::svector RInputs; + for(ff_node* n : reinterpret_cast(parentBB)->getSecondSet()) n->get_in_nodes(RInputs); + + ff::svector parentOutputs; + parentBB->get_out_nodes(parentOutputs); + + for(ff_node* n : R){ + ff::svector bbInputs; n->get_in_nodes(bbInputs); + for(ff_node* bbInput : bbInputs) + inputR.push_back(std::find(RInputs.begin(), RInputs.end(), bbInput) - RInputs.begin()); + + ff::svector bbOutputs; n->get_out_nodes(bbOutputs); + for(ff_node* bbOutput : bbOutputs) + outputR.push_back(std::find(parentOutputs.begin(), parentOutputs.end(), bbOutput) - parentOutputs.begin()); + } + } + + } + + +public: + std::set L, R; + bool coverageL = false, coverageR = false; + bool isSource = false, isSink = false; + + ff_node* parentBB; + + ff_endpoint listenEndpoint; + std::vector destinationEndpoints; + + size_t expectedEOS; + + // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table + std::vector inputL, outputL, inputR, outputR; + + bool isVertical(){return (L.empty() + R.empty()) == 1;} + + void insertInList(std::pair bb, bool _wholeParent = false){ + wholeParent = _wholeParent; + switch(bb.second){ + case SetEnum::L: L.insert(bb.first); return; + case SetEnum::R: R.insert(bb.first); return; + } + } + + void print(){ + std::cout << "###### BEGIN GROUP ######\n"; + std::cout << "Group Orientation: " << (isVertical() ? "vertical" : "horizontal") << std::endl; + std::cout << std::boolalpha << "Source group: " << isSource << std::endl; + std::cout << std::boolalpha << "Sink group: " << isSink << std::endl; + std::cout << std::boolalpha << "Coverage Left: " << coverageL << std::endl; + std::cout << std::boolalpha << "Coverage Right: " << coverageR << std::endl; + + std::cout << "Listen endpoint: " << listenEndpoint.address << ":" << listenEndpoint.port << std::endl; + std::cout << "Destination endpoints: " << std::endl; + for(ff_endpoint& e : destinationEndpoints) + std::cout << "\t* " << e.address << ":" << e.port << std::endl; + + std::cout << "Expected input connections: " << expectedEOS << std::endl; + std::cout << "Index Input Left: "; + for(int i : inputL) std::cout << i << " "; + std::cout << "\n"; + + std::cout << "Index Output Left: "; + for(int i : outputL) std::cout << i << " "; + std::cout << "\n"; + + std::cout << "Index Input Right: "; + for(int i : inputR) std::cout << i << " "; + std::cout << "\n"; + + std::cout << "Index Output Right: "; + for(int i : outputR) std::cout << i << " "; + std::cout << "\n"; + + std::cout << "###### END GROUP ######\n"; + } + + +}; + + +} + +#endif \ No newline at end of file diff --git a/ff/distributed/ff_dutils.hpp b/ff/distributed/ff_dutils.hpp new file mode 100644 index 00000000..b9e2bf7e --- /dev/null +++ b/ff/distributed/ff_dutils.hpp @@ -0,0 +1,62 @@ + +#include +#include "ff/pipeline.hpp" + +namespace ff { +enum NodeIOTypes { IN, OUT, INOUT}; +enum SetEnum {L, R}; + +/** + * Check the coverage of the first level building blocks of the main pipe, which is the building block needed to create a distributed FF program. + * Specifically this method, given the main pipe and and the set of buildingblock from which the user created groups, check if all stages of the pipe belong to a group. + **/ +static inline bool checkCoverageFirstLevel(ff_pipeline* mainPipe, const std::set& groupBuildingBlock) { + ff::svector stages = mainPipe->getStages(); + for(size_t i = 0; i< stages.size(); i++) + if (!groupBuildingBlock.contains(stages[i])){ + std::cerr << "Stage #" << i << " was not annotated in any group! Aborting!\n"; + abort(); + } + + return true; +} + +/** + * Helper function to detect sequential node from a bare node pointer. + **/ +static inline bool isSeq(const ff_node* n){return (!n->isAll2All() && !n->isComp() && !n->isFarm() && !n->isOFarm() && !n->isPipe());} + +/** + * Return the children builiding block of the given building block. We implemented only a2a and pipeline since, groups can be created just only from this two building block. + **/ +static inline std::set> getChildBB(ff_node* parent){ + std::set> out; + if (parent->isAll2All()){ + for (ff_node* bb : reinterpret_cast(parent)->getFirstSet()) + out.emplace(bb, SetEnum::L); + + for(ff_node* bb : reinterpret_cast(parent)->getSecondSet()) + out.emplace(bb, SetEnum::R); + } + + if (parent->isPipe()) + for(ff_node* bb : reinterpret_cast(parent)->getStages()) + out.emplace(bb, SetEnum::L); // for pipelines the default List is L (left) + + return out; +} + +static inline bool isSource(const ff_node* n, const ff_pipeline* p){ + return p->getStages().front() == n; +} + +static inline bool isSink(const ff_node* n, const ff_pipeline* p){ + return p->getStages().back() == n; +} + + + + + + +} \ No newline at end of file diff --git a/tests/distributed/test_group1.cpp b/tests/distributed/test_group1.cpp index 0b97fc51..24a658e9 100644 --- a/tests/distributed/test_group1.cpp +++ b/tests/distributed/test_group1.cpp @@ -113,16 +113,11 @@ int main(int argc, char*argv[]){ a2a.add_secondset({&dx1, &dx2, &dx3}); //----- defining the distributed groups ------ - auto g1 = source.createGroup("G1"); - auto g2 = a2a.createGroup("G2"); - auto g3 = a2a.createGroup("G3"); - auto g4 = sink.createGroup("G4"); - - g2.in << &sx1 << &dx1 << &dx2; - g2.out << &sx1 << &dx1 << &dx2; - - g3.in << &sx2 << &dx3; - g3.out << &dx3 << &sx2; + source.createGroup("G1"); + a2a.createGroup("G2") << &sx1 << &dx1 << &dx2; + a2a.createGroup("G3") << &sx2 << &dx3; + sink.createGroup("G4"); + // ------------------------------------------- // running the distributed groups From dd5d686f10ba3fa717678d9c69f1f742270dae04 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Tue, 1 Feb 2022 12:24:03 +0100 Subject: [PATCH 101/202] Fixed Source Sink fields and coverage in IR --- ff/distributed/ff_dgroups.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 97835527..eb290e71 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -217,13 +217,14 @@ class dGroups { if (!pair.second.contains(this->runningGroup)) continue; - bool isSrc = isSource(pair.first, parentPipe); - bool isSnk = isSink(pair.first, parentPipe); + bool isSrc = runningGroup_IR.isSource = isSource(pair.first, parentPipe); + bool isSnk = runningGroup_IR.isSink = isSink(pair.first, parentPipe); // check if from the under analysis 1st level building block it has been created just one group if (pair.second.size() == 1){ // if the unique group is not the one i'm going to run just skip this 1st level building block - if ((isSrc || pair.first->isDeserializable()) && (isSnk || pair.first->isSerializable())) + if ((isSrc || pair.first->isDeserializable()) && (isSnk || pair.first->isSerializable())){ runningGroup_IR.insertInList(std::make_pair(pair.first, SetEnum::L), true); + } continue; // skip anyway } @@ -239,11 +240,14 @@ class dGroups { return false; }); + // compute coverage for the running group + runningGroup_IR.computeCoverage(); + if (!children.empty()){ // seconda passata per verificare se ce da qualche parte c'è copertura completa altrimenti errore for(const std::string& gName : pair.second){ ff_IR& ir = annotatedGroups[gName]; - ir.computeCoverage(); + if (gName != runningGroup) ir.computeCoverage(); if (ir.coverageL) std::erase_if(children, [&](auto& p){ if (p.second == SetEnum::R && (isSnk || p.first->isSerializable()) && p.first->isDeserializable()){ From 1621ad3d7b426c7d3790158afe1de930c324f4c6 Mon Sep 17 00:00:00 2001 From: Gerardo Zinno Date: Fri, 4 Feb 2022 14:24:22 +0100 Subject: [PATCH 102/202] String matching bug fix --- ff/mapping_string.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ff/mapping_string.sh b/ff/mapping_string.sh index a5aac0e4..1d843118 100755 --- a/ff/mapping_string.sh +++ b/ff/mapping_string.sh @@ -62,7 +62,7 @@ read -p "Do you want that I change the ./config.hpp file for you? (y/N) " -n 1 - echo if [[ $REPLY =~ ^[Yy]$ ]] then - sed -i -e "s/#define FF_MAPPING_STRING \"\"/#define FF_MAPPING_STRING \"$string\"/1" ./config.hpp + sed -i -e "s/#define FF_MAPPING_STRING \".*\"/#define FF_MAPPING_STRING \"$string\"/1" config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_MAPPING_STRING variable in the ./config.hpp file:" echo -e "\e[1m $(grep -m1 "^#define FF_MAPPING_STRING \"" config.hpp) \e[0m" @@ -70,7 +70,7 @@ then echo "something went wrong when replacing the variable FF_MAPPING_STRING...." exit 1 fi - sed -i -e "s/#define FF_NUM_CORES -1/#define FF_NUM_CORES $logical/1" ./config.hpp + sed -i -e "s/#define FF_NUM_CORES [-]\{0,1\}[[:digit:]].*/#define FF_NUM_CORES $logical/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_NUM_CORES variable in the ./config.hpp file:" echo -e "\e[1m $(grep -m1 "^#define FF_NUM_CORES " config.hpp) \e[0m" @@ -78,7 +78,7 @@ then echo "something went wrong when replacing the variable FF_NUM_CORES...." exit 1 fi - sed -i -e "s/#define FF_NUM_REAL_CORES -1/#define FF_NUM_REAL_CORES $physical/1" ./config.hpp + sed -i -e "s/#define FF_NUM_REAL_CORES [-]\{0,1\}[[:digit:]].*/#define FF_NUM_REAL_CORES $physical/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_NUM_REAL_CORES variable in the ./config.hpp file:" echo -e "\e[1m $(grep -m1 "^#define FF_NUM_REAL_CORES " config.hpp) \e[0m" @@ -90,4 +90,4 @@ else echo "Ok, nothing has been changed" fi -exit 0 +exit 0 \ No newline at end of file From e6de391a7e2b90f7228d402ab02730fa6d991005 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sun, 6 Feb 2022 16:37:02 +0100 Subject: [PATCH 103/202] cosmetic modifications --- ff/mapping_string.sh | 8 ++++---- ff/node.hpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ff/mapping_string.sh b/ff/mapping_string.sh index dbe42fd5..82240d71 100755 --- a/ff/mapping_string.sh +++ b/ff/mapping_string.sh @@ -66,7 +66,7 @@ read -p "Do you want that I change the ./config.hpp file for you? (y/N) " -n 1 - echo if [[ $REPLY =~ ^[Yy]$ ]] then - sed -i -e "s/#define FF_MAPPING_STRING \".*\"/#define FF_MAPPING_STRING \"$string\"/1" config.hpp + sed -i -e "s/^#define FF_MAPPING_STRING \".*\"/#define FF_MAPPING_STRING \"$string\"/1" config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_MAPPING_STRING variable in the ./config.hpp file:" echo -e "\033[1m $(grep -m1 "^#define FF_MAPPING_STRING \"" config.hpp) \033[0m" @@ -74,7 +74,7 @@ then echo "something went wrong when replacing the variable FF_MAPPING_STRING...." exit 1 fi - sed -i -e "s/#define FF_NUM_CORES [-]\{0,1\}[[:digit:]].*/#define FF_NUM_CORES $logical/1" ./config.hpp + sed -i -e "s/^#define FF_NUM_CORES [-]\{0,1\}[[:digit:]].*/#define FF_NUM_CORES $logical/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_NUM_CORES variable in the ./config.hpp file:" echo -e "\033[1m $(grep -m1 "^#define FF_NUM_CORES " config.hpp) \033[0m" @@ -82,7 +82,7 @@ then echo "something went wrong when replacing the variable FF_NUM_CORES...." exit 1 fi - sed -i -e "s/#define FF_NUM_REAL_CORES [-]\{0,1\}[[:digit:]].*/#define FF_NUM_REAL_CORES $physical/1" ./config.hpp + sed -i -e "s/^#define FF_NUM_REAL_CORES [-]\{0,1\}[[:digit:]].*/#define FF_NUM_REAL_CORES $physical/1" ./config.hpp if [ $? -eq 0 ]; then echo "This is the new FF_NUM_REAL_CORES variable in the ./config.hpp file:" echo -e "\033[1m $(grep -m1 "^#define FF_NUM_REAL_CORES " config.hpp) \033[0m" @@ -94,4 +94,4 @@ else echo "Ok, nothing has been changed" fi -exit 0 \ No newline at end of file +exit 0 diff --git a/ff/node.hpp b/ff/node.hpp index 4813ad34..97e01923 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1346,7 +1346,7 @@ class ff_node { bool skipfirstpop = filter->skipfirstpop(); bool exit=false; bool filter_outpresent = false; - ssize_t neos=input_neos; + size_t neos=input_neos; // if the node is a combine where the last stage is a multi-output From 36e9162a446f15dff79508712258f4c4c18b3044 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Mon, 7 Feb 2022 18:51:17 +0100 Subject: [PATCH 104/202] First commit of the autoconf procedure to discover to which group the current process have connect to --- ff/distributed/ff_dgroups.hpp | 80 +++++++++++++++++++++++------ ff/distributed/ff_dintermediate.hpp | 6 ++- ff/distributed/ff_dutils.hpp | 18 +++++++ 3 files changed, 87 insertions(+), 17 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index eb290e71..7e4d9b6f 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -176,7 +176,8 @@ class dGroups { void prepareIR(ff_pipeline* parentPipe){ ff::ff_IR& runningGroup_IR = annotatedGroups[this->runningGroup]; - + ff_node* previousStage = getPreviousStage(parentPipe, runningGroup_IR.parentBB); + ff_node* nextStage = getNextStage(parentPipe, runningGroup_IR.parentBB); // TODO: check coverage all 1st level for(size_t i = 0; i < parsedGroups.size(); i++){ @@ -189,22 +190,21 @@ class dGroups { annotatedGroups[g.name].listenEndpoint = this->usedProtocol == Proto::TCP ? ff_endpoint(g.address, g.port) : ff_endpoint(i); } - auto g = std::find_if(parsedGroups.begin(), parsedGroups.end(), [this](const G& g){return g.name == getRunningGroup();}); + + /*auto g = std::find_if(parsedGroups.begin(), parsedGroups.end(), [this](const G& g){return g.name == getRunningGroup();}); for(std::string& conn : g->Oconn) // build the list of outgoing connections for each group if (annotatedGroups.find(conn) != annotatedGroups.end()) runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[conn].listenEndpoint); else throw FF_Exception("A specified destination has a wrong name! :("); - + */ // compute how many expected EOS the future running must wait before exiting - size_t expectedEOS = 0; + /*size_t expectedEOS = 0; for (const G& g : parsedGroups) if (g.name != this->runningGroup) for (const std::string& conn : g.Oconn) - if (conn == this->runningGroup) expectedEOS++; - runningGroup_IR.expectedEOS = expectedEOS; - + if (conn == this->runningGroup) expectedEOS++;*/ // build the map parentBB -> std::map> parentBB2GroupsName; @@ -213,17 +213,19 @@ class dGroups { // iterate over the 1st level building blocks and the list of create groups for(const auto& pair : parentBB2GroupsName){ - //if the current group is build from another building block we can just skip this! - if (!pair.second.contains(this->runningGroup)) + //just build the current previous the current and the next stage of the parentbuilding block i'm going to exeecute //// TODO: reprhase this comment! + if (!(pair.first == previousStage || pair.first == runningGroup_IR.parentBB || pair.first == nextStage)) continue; - bool isSrc = runningGroup_IR.isSource = isSource(pair.first, parentPipe); - bool isSnk = runningGroup_IR.isSink = isSink(pair.first, parentPipe); + bool isSrc = isSource(pair.first, parentPipe); + bool isSnk = isSink(pair.first, parentPipe); // check if from the under analysis 1st level building block it has been created just one group if (pair.second.size() == 1){ // if the unique group is not the one i'm going to run just skip this 1st level building block if ((isSrc || pair.first->isDeserializable()) && (isSnk || pair.first->isSerializable())){ - runningGroup_IR.insertInList(std::make_pair(pair.first, SetEnum::L), true); + auto& ir_ = annotatedGroups[*pair.second.begin()]; + ir_.insertInList(std::make_pair(pair.first, SetEnum::L), true); + ir_.isSink = isSnk; ir_.isSource = isSrc; } continue; // skip anyway } @@ -240,14 +242,11 @@ class dGroups { return false; }); - // compute coverage for the running group - runningGroup_IR.computeCoverage(); - if (!children.empty()){ // seconda passata per verificare se ce da qualche parte c'è copertura completa altrimenti errore for(const std::string& gName : pair.second){ ff_IR& ir = annotatedGroups[gName]; - if (gName != runningGroup) ir.computeCoverage(); + ir.computeCoverage(); if (ir.coverageL) std::erase_if(children, [&](auto& p){ if (p.second == SetEnum::R && (isSnk || p.first->isSerializable()) && p.first->isDeserializable()){ @@ -270,15 +269,64 @@ class dGroups { std::cerr << "Some building block has not been annotated and no coverage found! You missed something. Aborting now" << std::endl; abort(); } + } else // compute the coverage anyway + for(const std::string& gName : pair.second) annotatedGroups[gName].computeCoverage(); + + } + + //############# compute the number of excpected input connections + if (runningGroup_IR.hasRightChildren()){ + auto& currentGroups = parentBB2GroupsName[runningGroup_IR.parentBB]; + runningGroup_IR.expectedEOS = std::count_if(currentGroups.cbegin(), currentGroups.cend(), [&](auto& gName){return (!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasLeftChildren());}); + // if the current group is horizontal count out itsleft from the all horizontals + if (!runningGroup_IR.isVertical()) runningGroup_IR.expectedEOS -= 1; + } + // if the previousStage exists, count all the ouput groups pointing to the one i'm going to run + if (previousStage && runningGroup_IR.hasLeftChildren()) + runningGroup_IR.expectedEOS += outputGroups(parentBB2GroupsName[previousStage]).size(); + + + //############ compute the name of the outgoing connection groups + if (runningGroup_IR.parentBB->isAll2All() && runningGroup_IR.isVertical() && runningGroup_IR.hasLeftChildren()){ + // inserisci tutte i gruppi di questo bb a destra + for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) + if (!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasRightChildren()) + runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); + } else { + if (!runningGroup_IR.isVertical()){ + // inserisci tutti i gruppi come sopra + for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) + if ((!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasRightChildren()) && gName != runningGroup) + runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); } + + if (nextStage) + for(const auto& gName : inputGroups(parentBB2GroupsName[nextStage])) + runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); } + runningGroup_IR.buildIndexes(); runningGroup_IR.print(); } + + std::set outputGroups(std::set groupNames){ + if (groupNames.size() > 1) + std::erase_if(groupNames, [this](const auto& gName){return (annotatedGroups[gName].isVertical() && annotatedGroups[gName].hasLeftChildren());}); + return groupNames; + } + + std::set inputGroups(std::set groupNames){ + if (groupNames.size() > 1) + std::erase_if(groupNames,[this](const auto& gName){return (annotatedGroups[gName].isVertical() && annotatedGroups[gName].hasRightChildren());}); + return groupNames; + } + }; + + static inline int DFF_Init(int& argc, char**& argv){ std::string configFile, groupName; diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index 107b599e..406240d4 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -70,19 +70,23 @@ class ff_IR { std::set L, R; bool coverageL = false, coverageR = false; bool isSource = false, isSink = false; + bool hasReceiver = false, hasSender = false; // todo settare qui. ff_node* parentBB; ff_endpoint listenEndpoint; std::vector destinationEndpoints; - size_t expectedEOS; + size_t expectedEOS = 0; // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table std::vector inputL, outputL, inputR, outputR; bool isVertical(){return (L.empty() + R.empty()) == 1;} + bool hasLeftChildren() {return !L.empty();} + bool hasRightChildren() {return !R.empty();} + void insertInList(std::pair bb, bool _wholeParent = false){ wholeParent = _wholeParent; switch(bb.second){ diff --git a/ff/distributed/ff_dutils.hpp b/ff/distributed/ff_dutils.hpp index b9e2bf7e..a73977e6 100644 --- a/ff/distributed/ff_dutils.hpp +++ b/ff/distributed/ff_dutils.hpp @@ -54,6 +54,24 @@ static inline bool isSink(const ff_node* n, const ff_pipeline* p){ return p->getStages().back() == n; } +static inline ff_node* getPreviousStage(ff_pipeline* p, ff_node* s){ + ff::svector stages = p->getStages(); + for(size_t i = 1; i < stages.size(); i++) + if (stages[i] == s) return stages[--i]; + + return nullptr; +} + +static inline ff_node* getNextStage(ff_pipeline* p, ff_node* s){ + ff::svector stages = p->getStages(); + for(size_t i = 0; i < stages.size() - 1; i++) + if(stages[i] == s) return stages[++i]; + + return nullptr; +} + + + From 02d734f6c5adbb7821b8f633cfc0756dae21db38 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Mon, 7 Feb 2022 19:43:10 +0100 Subject: [PATCH 105/202] Fixed hasReceiver and hasSender fields in IR, added group name in list of output connection in IR --- ff/distributed/ff_dgroups.hpp | 9 +++++++-- ff/distributed/ff_dintermediate.hpp | 13 ++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 7e4d9b6f..c789a640 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -185,9 +185,11 @@ class dGroups { // throw an error if a group in the configuration has not been annotated in the current program if (!annotatedGroups.contains(g.name)) throw FF_Exception("present in the configuration file has not been implemented! :("); - + auto endpoint = this->usedProtocol == Proto::TCP ? ff_endpoint(g.address, g.port) : ff_endpoint(i); + endpoint.groupName = g.name; + // annotate the listen endpoint for the specified group - annotatedGroups[g.name].listenEndpoint = this->usedProtocol == Proto::TCP ? ff_endpoint(g.address, g.port) : ff_endpoint(i); + annotatedGroups[g.name].listenEndpoint = endpoint; } @@ -285,6 +287,7 @@ class dGroups { if (previousStage && runningGroup_IR.hasLeftChildren()) runningGroup_IR.expectedEOS += outputGroups(parentBB2GroupsName[previousStage]).size(); + if (runningGroup_IR.expectedEOS > 0) runningGroup_IR.hasReceiver = true; //############ compute the name of the outgoing connection groups if (runningGroup_IR.parentBB->isAll2All() && runningGroup_IR.isVertical() && runningGroup_IR.hasLeftChildren()){ @@ -304,6 +307,8 @@ class dGroups { for(const auto& gName : inputGroups(parentBB2GroupsName[nextStage])) runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); } + + if (!runningGroup_IR.destinationEndpoints.empty()) runningGroup_IR.hasSender = true; runningGroup_IR.buildIndexes(); diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index 406240d4..f269d848 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -101,15 +101,18 @@ class ff_IR { std::cout << std::boolalpha << "Source group: " << isSource << std::endl; std::cout << std::boolalpha << "Sink group: " << isSink << std::endl; std::cout << std::boolalpha << "Coverage Left: " << coverageL << std::endl; - std::cout << std::boolalpha << "Coverage Right: " << coverageR << std::endl; + std::cout << std::boolalpha << "Coverage Right: " << coverageR << std::endl << std::endl; - std::cout << "Listen endpoint: " << listenEndpoint.address << ":" << listenEndpoint.port << std::endl; + std::cout << std::boolalpha << "Has Receiver: " << hasReceiver << std::endl; + std::cout << "Expected input connections: " << expectedEOS << std::endl; + std::cout << "Listen endpoint: " << listenEndpoint.address << ":" << listenEndpoint.port << std::endl << std::endl; + + std::cout << std::boolalpha << "Has Sender: " << hasSender << std::endl; std::cout << "Destination endpoints: " << std::endl; for(ff_endpoint& e : destinationEndpoints) - std::cout << "\t* " << e.address << ":" << e.port << std::endl; + std::cout << "\t* " << e.groupName << "\t[[" << e.address << ":" << e.port << "]]" << std::endl; - std::cout << "Expected input connections: " << expectedEOS << std::endl; - std::cout << "Index Input Left: "; + std::cout << "\n\nIndex Input Left: "; for(int i : inputL) std::cout << i << " "; std::cout << "\n"; From e57cc60c423548084f25574d7e6119dad1daf502 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 8 Feb 2022 10:44:07 +0100 Subject: [PATCH 106/202] more tests --- tests/distributed/Makefile | 6 +- tests/distributed/dwordcount/dwordcount.cpp | 4 +- tests/distributed/test_group1.cpp | 8 +- tests/distributed/test_group1.json | 24 +++ tests/distributed/test_group2.cpp | 14 +- tests/distributed/test_group2.json | 24 +++ tests/distributed/test_group3.cpp | 8 +- tests/distributed/test_group3.json | 14 ++ tests/distributed/test_group4.cpp | 8 +- tests/distributed/test_group4.json | 14 ++ tests/distributed/test_group5.cpp | 12 +- tests/distributed/test_group5.json | 19 ++ tests/distributed/test_group6.cpp | 1 - tests/distributed/test_group6.json | 14 ++ tests/distributed/test_group7.cpp | 212 ++++++++++++++++++++ tests/distributed/test_group7.json | 24 +++ 16 files changed, 372 insertions(+), 34 deletions(-) create mode 100644 tests/distributed/test_group1.json create mode 100644 tests/distributed/test_group2.json create mode 100644 tests/distributed/test_group3.json create mode 100644 tests/distributed/test_group4.json create mode 100644 tests/distributed/test_group5.json create mode 100644 tests/distributed/test_group6.json create mode 100644 tests/distributed/test_group7.cpp create mode 100644 tests/distributed/test_group7.json diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index 0149d1bd..b443ecc3 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -25,7 +25,7 @@ CXXFLAGS += -std=c++20 ifdef DEBUG - OPTIMIZE_FLAGS += -g -ggdb -fno-inline-functions + OPTIMIZE_FLAGS += -g -fno-inline-functions else OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG endif @@ -45,12 +45,12 @@ endif ifdef FF_HOME INCS += -I$(FF_HOME) else - INCS += -I ~/SPM/fastflow + INCS += -I ~/fastflow endif ifdef CEREAL_HOME INCS += -I$(CEREAL_HOME) else - INCS += -I ~/SPM/cereal + INCS += -I ~/cereal endif diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index 14fdf6bc..d17f8113 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -354,7 +354,7 @@ int main(int argc, char* argv[]) { pipe0->add_stage(sp, true); L.push_back(pipe0); - G1.out << sp; + G1 << pipe0; } for (size_t i=0;iadd_stage(S[i]); R.push_back(pipe1); - G2.in << C[i]; + G2 << pipe1; } a2a.add_firstset(L, 0, true); diff --git a/tests/distributed/test_group1.cpp b/tests/distributed/test_group1.cpp index 24a658e9..07f172c2 100644 --- a/tests/distributed/test_group1.cpp +++ b/tests/distributed/test_group1.cpp @@ -21,9 +21,11 @@ * | Source | ----> | MoNode1 ->| | -->| ------ * | | | | |-> MiNode2 | | | | * -------- | ----------------------- |--> | Sink | - * | | | | - * | ----------------------- | ------ - * | --> | | -->| G4 + * | | ^ | | | + * | | | | ------ + * | v | | G4 + * | ----------------------- | + * ---> | | -->| * | MoNode2 ->|-> MiNode3 | * ----------------------- * G3 diff --git a/tests/distributed/test_group1.json b/tests/distributed/test_group1.json new file mode 100644 index 00000000..8282e4fa --- /dev/null +++ b/tests/distributed/test_group1.json @@ -0,0 +1,24 @@ +{ + "protocol" : "TCP", + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2", "G3"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G3", "G4"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006", + "OConn" : ["G2", "G4"] + }, + { + "name" : "G4", + "endpoint": "localhost:8007" + } + ] +} diff --git a/tests/distributed/test_group2.cpp b/tests/distributed/test_group2.cpp index c4c9c411..9dd1143f 100644 --- a/tests/distributed/test_group2.cpp +++ b/tests/distributed/test_group2.cpp @@ -23,8 +23,11 @@ * -------- | | | | | | | * | | |-> MinOde3 | |--> | Sink | * | ----------------------- | | | - * | ---------- | ------ - * | --------> | | | G4 + * | ^ | ------ + * | | | G4 + * | v | + * | ---------- | + * | --------> | | | * | MoNode2 |---------->| * ---------- * G3 @@ -120,11 +123,8 @@ int main(int argc, char*argv[]){ auto g3 = a2a.createGroup("G3"); auto g4 = sink.createGroup("G4"); - g2.in << &sx1; - g2.out << &dx1 << &dx2 << &dx3; - - g3.in << &sx2; - g3.out << &dx2; + g2 << &sx1 << &dx1 << &dx2 << &dx3; + g3 << &sx2; // ------------------------------------------- diff --git a/tests/distributed/test_group2.json b/tests/distributed/test_group2.json new file mode 100644 index 00000000..579293e3 --- /dev/null +++ b/tests/distributed/test_group2.json @@ -0,0 +1,24 @@ +{ + "protocol" : "TCP", + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2", "G3"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G3", "G4"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006", + "OConn" : ["G2", "G4"] + }, + { + "name" : "G4", + "endpoint": "localhost:8007" + } + ] +} diff --git a/tests/distributed/test_group3.cpp b/tests/distributed/test_group3.cpp index 4b1dc83b..f7d3a037 100644 --- a/tests/distributed/test_group3.cpp +++ b/tests/distributed/test_group3.cpp @@ -71,12 +71,8 @@ int main(int argc, char*argv[]){ //----- defining the distributed groups ------ - dGroup g1 = a2a.createGroup("G1"); - dGroup g2 = a2a.createGroup("G2"); - - g1.out << &source1 << &source2 << &source3; // &sink1 automatically added to G1 - //g1.in << &sink1; // redundant!!! - g2.in << &sink2; + a2a.createGroup("G1") << &source1 << &source2 << &source3 << &sink1; + a2a.createGroup("G2") << &sink2; // ------------------------------------------- diff --git a/tests/distributed/test_group3.json b/tests/distributed/test_group3.json new file mode 100644 index 00000000..2d846cba --- /dev/null +++ b/tests/distributed/test_group3.json @@ -0,0 +1,14 @@ +{ + "protocol" : "TCP", + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} diff --git a/tests/distributed/test_group4.cpp b/tests/distributed/test_group4.cpp index 3f583983..38e2b3e1 100644 --- a/tests/distributed/test_group4.cpp +++ b/tests/distributed/test_group4.cpp @@ -70,11 +70,11 @@ int main(int argc, char*argv[]){ //----- defining the distributed groups ------ - dGroup g1 = a2a.createGroup("G1"); - dGroup g2 = a2a.createGroup("G2"); + auto g1 = a2a.createGroup("G1"); + auto g2 = a2a.createGroup("G2"); - g1.out << &source1 << &source2; - g2.in << &sink1 << &sink2; // &source3 automatically added to the group + g1 << &source1 << &source2; + g2 << &source3 << &sink1 << &sink2; // ------------------------------------------- diff --git a/tests/distributed/test_group4.json b/tests/distributed/test_group4.json new file mode 100644 index 00000000..2d846cba --- /dev/null +++ b/tests/distributed/test_group4.json @@ -0,0 +1,14 @@ +{ + "protocol" : "TCP", + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} diff --git a/tests/distributed/test_group5.cpp b/tests/distributed/test_group5.cpp index 5b64ac86..b66857f5 100644 --- a/tests/distributed/test_group5.cpp +++ b/tests/distributed/test_group5.cpp @@ -18,7 +18,7 @@ * | | | T1 -->| | * | S1 -->| --> W1 | | | | * | | | ---> | T2 -->| --> K1 | - * | S2 -->| --> W2 | ^ | | | + * | S2 -->| --> W2 | --> | | | * | | | | T3 -->| | * ---------------- | ----------------- * | ^ | @@ -136,13 +136,9 @@ int main(int argc, char*argv[]){ auto g2 = a2a1.createGroup("G2"); auto g3 = a2a2.createGroup("G3"); - g1.out << &s1 << &s2 << &w1 << &w2; - g1.in << &w1 << &w2; - - g2.out << &s3 << &w3; - g2.in << &w3; - - //g3.in << &t1 << &t2 << &t3; + g1 << &s1 << &s2 << &w1 << &w2; + g2 << &s3 << &w3; + g3 << &t1 << &t2 << &t3; // ------------------------------------------- diff --git a/tests/distributed/test_group5.json b/tests/distributed/test_group5.json new file mode 100644 index 00000000..c9eb9a3c --- /dev/null +++ b/tests/distributed/test_group5.json @@ -0,0 +1,19 @@ +{ + "protocol" : "TCP", + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2", "G3"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G1", "G3"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006" + } + ] +} diff --git a/tests/distributed/test_group6.cpp b/tests/distributed/test_group6.cpp index 581a56e9..8eb8af1a 100644 --- a/tests/distributed/test_group6.cpp +++ b/tests/distributed/test_group6.cpp @@ -56,7 +56,6 @@ struct W_left: ff_minode_t { struct W_right: ff_monode_t { std::string* svc(std::string* in){ - const std::lock_guard lock(mtx); long outchannels = get_num_outchannels(); for(long i = 0; i < outchannels; i++) diff --git a/tests/distributed/test_group6.json b/tests/distributed/test_group6.json new file mode 100644 index 00000000..2d846cba --- /dev/null +++ b/tests/distributed/test_group6.json @@ -0,0 +1,14 @@ +{ + "protocol" : "TCP", + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} diff --git a/tests/distributed/test_group7.cpp b/tests/distributed/test_group7.cpp new file mode 100644 index 00000000..5df25991 --- /dev/null +++ b/tests/distributed/test_group7.cpp @@ -0,0 +1,212 @@ +/* + * FastFlow concurrent network: + * --------------------------------------- + * | pipeA2A1 | + * | -------------- | + * | pipe1 | T1 ->| T3 | | + * | ------------- | | | | + * | | W1 --> W2 | --> | | | | ---- + * ----------- | ------------- ^ | T2 ->| T4 | | | | + * | S1 --> S2 | --> | | -------------- | ---->| S3 | + * ----------- | ------------- v -------------- | ---- + * pipe0 | | W1 --> W2 | --> | T1 ->| T5 | | + * | ------------- | | | | + * | pipe2 | | | | + * | | T2 ->| T6 | | + * | -------------- | + * | pipeA2A2 | + * --------------------------------------- + * a2a + * /<-------------------------- pipeMain ------------------------------->/ + * + * distributed version: + * G2 + * --------------------------------------- + * | -------------- | + * | | T1 ->| T3 | | + * | ------------- | | | | G4 + * G1 | | W1 --> W2 | --> | | | | ---- + * ----------- | ------------- | T2 ->| T4 | | | | + * | S1 --> S2 | --> | -------------- | ---->| S3 | + * ----------- | --------------------------------------- ^ ---- + * | | ^ | + * | v | | + * | --------------------------------------- | + * | | ------------- -------------- | | + * | | | W1 --> W2 | --> | T1 ->| T5 | | | + * -->| ------------- | | | | -- + * | | | | | + * | | T2 ->| T6 | | + * | -------------- | + * --------------------------------------- + * G3 + */ + +#include +#include +#include + +using namespace ff; +std::mutex mtx; + +struct S1 : ff_node_t { + S1(long N):N(N) {} + std::string* svc(std::string*) { + for(long i = 0; i < N; i++) + ff_send_out(new std::string("[Task generated from S1 for W")); + + return EOS; + } + long N; +}; + +struct S2 : ff_monode_t{ + + std::string* svc(std::string* in){ + long idx = next % 2; + std::string* out = new std::string(*in + std::to_string(idx) + "]"); + + ff_send_out_to(out, idx); + delete in; + ++next; + return GO_ON; + + } + long next=0; +}; + +struct W1: ff_node_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[W1-" << get_my_id() << " received " << *in << " from S2]\n"; + + return in; + } +}; +struct W2: ff_monode_t { + + int svc_init() { + next = get_my_id(); + return 0; + } + + std::string* svc(std::string* in){ + long outchannels = get_num_outchannels(); + long idx = next % outchannels; + + ff_send_out_to(new std::string("[Task generated from W2-" + std::to_string(get_my_id()) + " to T" + std::to_string(idx) + "]"), idx); + + ++next; + return GO_ON; + } + long next; +}; + +struct T_left: ff_minode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[T" << get_my_id() << " reiceived " << *in << " from W2-" << get_channel_id() << "]" << std::endl; + + return in; + } +}; +struct T_right: ff_monode_t { + + std::string* svc(std::string* in){ + return in; + } +}; + +struct T: ff_minode_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + return in; + } +}; + + +struct S3 : ff_minode_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + + std::cout << "[S3 received " << *in << " from T" << (3+get_channel_id()) << "]" << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + int N=10; + if (argc==2) N=std::stol(argv[1]); + + + using T1 = ff_comb; + using T2 = ff_comb; + using T3 = T; + using T4 = T; + using T5 = T; + using T6 = T; + + ff_pipeline pipe0; + pipe0.add_stage(new S1(N), true); + pipe0.add_stage(new S2, true); + + ff_pipeline pipe1; + pipe1.add_stage(new W1, true); + pipe1.add_stage(new W2, true); + + ff_pipeline pipe2; + pipe2.add_stage(new W1, true); + pipe2.add_stage(new W2, true); + + ff_pipeline pipeA2A1; + ff_a2a a2a1; + a2a1.add_firstset( {new T1(new T_left, new T_right, true, true), + new T2(new T_left, new T_right, true, true)}, 0, true); + a2a1.add_secondset({new T3, new T4}, true); + pipeA2A1.add_stage(&a2a1); + + ff_pipeline pipeA2A2; + ff_a2a a2a2; + a2a2.add_firstset( {new T1(new T_left, new T_right, true, true), + new T2(new T_left, new T_right, true, true)}, 0, true); + a2a2.add_secondset({new T5, new T6}, true); + pipeA2A2.add_stage(&a2a2); + + ff_a2a a2a; + a2a.add_firstset( {&pipe1, &pipe2}); + a2a.add_secondset({&pipeA2A1, &pipeA2A2}); + + S3 s3; + + ff_pipeline pipeMain; + pipeMain.add_stage(&pipe0); + pipeMain.add_stage(&a2a); + pipeMain.add_stage(&s3); + + //----- defining the distributed groups ------ + auto G1 = pipe0.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + auto G3 = a2a.createGroup("G3"); + auto G4 = s3.createGroup("G4"); + + G2 << &pipe1 << &pipeA2A1; + G2 << &pipe2 << &pipeA2A2; + + // ------------------------------------------- + + // running the distributed groups + if (pipeMain.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group7.json b/tests/distributed/test_group7.json new file mode 100644 index 00000000..8282e4fa --- /dev/null +++ b/tests/distributed/test_group7.json @@ -0,0 +1,24 @@ +{ + "protocol" : "TCP", + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1", + "OConn" : ["G2", "G3"] + }, + { + "name" : "G2", + "endpoint": "localhost:8005", + "OConn" : ["G3", "G4"] + }, + { + "name" : "G3", + "endpoint": "localhost:8006", + "OConn" : ["G2", "G4"] + }, + { + "name" : "G4", + "endpoint": "localhost:8007" + } + ] +} From f7b6c8f9af546adf7838c9a4edd73ade17625f4f Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 8 Feb 2022 13:55:23 +0100 Subject: [PATCH 107/202] fixed typo in test_group7.cpp --- tests/distributed/test_group7.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/distributed/test_group7.cpp b/tests/distributed/test_group7.cpp index 5df25991..388d8878 100644 --- a/tests/distributed/test_group7.cpp +++ b/tests/distributed/test_group7.cpp @@ -199,7 +199,7 @@ int main(int argc, char*argv[]){ auto G4 = s3.createGroup("G4"); G2 << &pipe1 << &pipeA2A1; - G2 << &pipe2 << &pipeA2A2; + G3 << &pipe2 << &pipeA2A2; // ------------------------------------------- From 4274832dc5657fdc7290873d7a7d4b460df5429f Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 8 Feb 2022 14:14:54 +0100 Subject: [PATCH 108/202] moved test_complete1 to test_group8 --- .../{test_complete1.cpp => test_group8.cpp} | 30 ++++++++++++------- .../{test_complete1.json => test_group8.json} | 0 2 files changed, 20 insertions(+), 10 deletions(-) rename tests/distributed/{test_complete1.cpp => test_group8.cpp} (77%) rename tests/distributed/{test_complete1.json => test_group8.json} (100%) diff --git a/tests/distributed/test_complete1.cpp b/tests/distributed/test_group8.cpp similarity index 77% rename from tests/distributed/test_complete1.cpp rename to tests/distributed/test_group8.cpp index 1b837fcc..ac9872dd 100644 --- a/tests/distributed/test_complete1.cpp +++ b/tests/distributed/test_group8.cpp @@ -9,9 +9,21 @@ * * /<--------- a2a -------->/ * /<-------------------- pipeMain ------------------>/ + * + * distributed version: + * + * G1 G2 G3 + * -------- ----------------------- ------ + * | | | |-> MiNode1 | | | + * | Source | ----> | MoNode1 ->| | -->| Sink | + * | | | |-> MiNode2 | | | + * -------- | MoNode2 ->| | ------ + * | |-> MiNode3 | + * ----------------------- + * + * */ - #include #include #include @@ -89,19 +101,17 @@ int main(int argc, char*argv[]){ a2a.add_firstset({&sx1, &sx2, &sx3}); a2a.add_secondset({&dx1, &dx2, &dx3}); - //mainPipe.run_and_wait_end(); + //----- defining the distributed groups ------ + auto g1 = sp.createGroup("G1"); auto g2 = a2a.createGroup("G2"); auto g3 = sinkp.createGroup("G3"); - - - g1.out << &s; - g2.in << &sx1 << &sx2 << &sx3; g2.out << &dx1 << &dx2 << &dx3; - g3.in << &sink; - - if (mainPipe.run_and_wait_end()<0) { + + // ------------------------------------------- + + if (mainPipe.run_and_wait_end()<0) { error("running mainPipe\n"); return -1; } -} \ No newline at end of file +} diff --git a/tests/distributed/test_complete1.json b/tests/distributed/test_group8.json similarity index 100% rename from tests/distributed/test_complete1.json rename to tests/distributed/test_group8.json From 47add28f8cd0d74fe38121bca166e0ef909fcb46 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 10 Feb 2022 10:37:54 +0100 Subject: [PATCH 109/202] started to cleanup the tests/distributed directory by renaming previous test files --- .../{test_complete3.cpp => test_group10.cpp} | 6 ++-- ...{test_complete2.json => test_group10.json} | 0 .../{test_complete4.cpp => test_group11.cpp} | 12 ++++--- ...{test_complete3.json => test_group11.json} | 0 .../{test_complete5.cpp => test_group12.cpp} | 34 ++++++++++++------- ...{test_complete5.json => test_group12.json} | 0 .../{test_complete2.cpp => test_group9.cpp} | 8 +++-- .../{test_complete4.json => test_group9.json} | 0 8 files changed, 38 insertions(+), 22 deletions(-) rename tests/distributed/{test_complete3.cpp => test_group10.cpp} (95%) rename tests/distributed/{test_complete2.json => test_group10.json} (100%) rename tests/distributed/{test_complete4.cpp => test_group11.cpp} (95%) rename tests/distributed/{test_complete3.json => test_group11.json} (100%) rename tests/distributed/{test_complete5.cpp => test_group12.cpp} (79%) rename tests/distributed/{test_complete5.json => test_group12.json} (100%) rename tests/distributed/{test_complete2.cpp => test_group9.cpp} (93%) rename tests/distributed/{test_complete4.json => test_group9.json} (100%) diff --git a/tests/distributed/test_complete3.cpp b/tests/distributed/test_group10.cpp similarity index 95% rename from tests/distributed/test_complete3.cpp rename to tests/distributed/test_group10.cpp index ed52a3e8..f42e0087 100644 --- a/tests/distributed/test_complete3.cpp +++ b/tests/distributed/test_group10.cpp @@ -113,11 +113,13 @@ int main(int argc, char*argv[]){ pipe.add_stage(&pipe0); pipe.add_stage(&a2a); + //----- defining the distributed groups ------ + auto G1 = pipe0.createGroup("G1"); auto G2 = a2a.createGroup("G2"); - G1.out << &n2; - G2.in << &n31 << &n32 << &n33; + // ------------------------------------------- + if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); diff --git a/tests/distributed/test_complete2.json b/tests/distributed/test_group10.json similarity index 100% rename from tests/distributed/test_complete2.json rename to tests/distributed/test_group10.json diff --git a/tests/distributed/test_complete4.cpp b/tests/distributed/test_group11.cpp similarity index 95% rename from tests/distributed/test_complete4.cpp rename to tests/distributed/test_group11.cpp index 24c2c4ec..f4a9ffa7 100644 --- a/tests/distributed/test_complete4.cpp +++ b/tests/distributed/test_group11.cpp @@ -112,13 +112,15 @@ int main(int argc, char*argv[]){ a2a1.add_secondset({&n4}); pipe.add_stage(&a2a0); pipe.add_stage(&a2a1); - - auto G1 = a2a0.createGroup("G1"); + + //----- defining the distributed groups ------ + + auto G1 = a2a0.createGroup("G1"); auto G2 = a2a1.createGroup("G2"); - G1.out << &n21 << &n22; - G2.in << &h31 << &h32 << &h33; - + // ------------------------------------------- + + if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); return -1; diff --git a/tests/distributed/test_complete3.json b/tests/distributed/test_group11.json similarity index 100% rename from tests/distributed/test_complete3.json rename to tests/distributed/test_group11.json diff --git a/tests/distributed/test_complete5.cpp b/tests/distributed/test_group12.cpp similarity index 79% rename from tests/distributed/test_complete5.cpp rename to tests/distributed/test_group12.cpp index b16b0d7b..df09c1d5 100644 --- a/tests/distributed/test_complete5.cpp +++ b/tests/distributed/test_group12.cpp @@ -9,10 +9,10 @@ * /<---------------------------- pipe ---------------------------->/ * * G1: Node1 - * G2: Helper->Node21 and Helper->Node22 - * G3: Helper->Node23 - * G4: Helper->Node31 - * G5: Helper->Node32 and Helper->Node33 + * G2: Helper->Node21 (combL1) and Helper->Node22 (combL2) + * G3: Helper->Node23 (combL3) + * G4: Helper->Node31 (combR1) + * G5: Helper->Node32 (combR2) and Helper->Node33 (combR3) * G6: Node4 */ @@ -108,13 +108,21 @@ int main(int argc, char*argv[]){ Node4 n4(ntasks); ff_a2a a2a0; a2a0.add_firstset({&n1}); - a2a0.add_secondset({new ff_comb(&h21, &n21), new ff_comb(&h22, &n22), new ff_comb(&h23, &n23)}); + ff_comb combL1(&h21, &n21); + ff_comb combL2(&h22, &n22); + ff_comb combL3(&h23, &n23); + a2a0.add_secondset({&combL1, &combL2, &combL3}); ff_a2a a2a1; - a2a1.add_firstset({new ff_comb(&h31, &n31), new ff_comb(&h32, &n32), new ff_comb(&h33, &n33)}); + ff_comb combR1(&h31, &n31); + ff_comb combR2(&h32, &n32); + ff_comb combR3(&h33, &n33); + a2a1.add_firstset({&combR1, &combR2, &combR3}); a2a1.add_secondset({&n4}); pipe.add_stage(&a2a0); pipe.add_stage(&a2a1); + //----- defining the distributed groups ------ + auto G1 = a2a0.createGroup("G1"); auto G2 = a2a0.createGroup("G2"); auto G3 = a2a0.createGroup("G3"); @@ -122,13 +130,15 @@ int main(int argc, char*argv[]){ auto G5 = a2a1.createGroup("G5"); auto G6 = a2a1.createGroup("G6"); - /* ----------------- */ G1.out << &n1; - G2.in << &h21 << &h22; G2.out << &n21 << &n22; - G3.in << &h23; G3.out << &n23; - G4.in << &h31; G4.out << &n31; - G5.in << &h32 << &h33; G5.out << &n32 << &n33; - G6.in << &n4; /* ------------------- */ + G1 << &n1; + G2 << &combL1 << &combL2; + G3 << &combL3; + G4 << &combR1; + G5 << &combR2 << &combR3; + G6 << &n4; + // ------------------------------------------- + if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); return -1; diff --git a/tests/distributed/test_complete5.json b/tests/distributed/test_group12.json similarity index 100% rename from tests/distributed/test_complete5.json rename to tests/distributed/test_group12.json diff --git a/tests/distributed/test_complete2.cpp b/tests/distributed/test_group9.cpp similarity index 93% rename from tests/distributed/test_complete2.cpp rename to tests/distributed/test_group9.cpp index 1924b797..f4873289 100644 --- a/tests/distributed/test_complete2.cpp +++ b/tests/distributed/test_group9.cpp @@ -105,11 +105,13 @@ int main(int argc, char*argv[]){ pipe.add_stage(&pipe0); pipe.add_stage(&pipe1); - auto G1 = pipe0.createGroup("G1"); + + //----- defining the distributed groups ------ + + auto G1 = pipe0.createGroup("G1"); auto G2 = pipe1.createGroup("G2"); - G1.out << &n2; - G2.in << &n3; + // ------------------------------------------- if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); diff --git a/tests/distributed/test_complete4.json b/tests/distributed/test_group9.json similarity index 100% rename from tests/distributed/test_complete4.json rename to tests/distributed/test_group9.json From 526ce000f4b35ce48fc9eacc791a8d64b0382f11 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 11 Feb 2022 18:41:54 +0100 Subject: [PATCH 110/202] Moved to dual license model: LGPLv3 and MIT --- AUTHORS | 19 +- COPYING.LESSER => LICENSE.LGPL | 0 LICENSE.MIT | 19 + README.md | 4 + ff/all2all.hpp | 4 +- ff/allocator.hpp | 8 +- ff/barrier.hpp | 4 +- ff/bitflags.hpp | 4 +- ff/buffer.hpp | 4 +- ff/combine.hpp | 4 +- ff/config.hpp | 4 +- ff/d/inter.hpp | 163 -- ff/d/zmq.hpp | 761 --------- ff/d/zmqImpl.hpp | 2079 ------------------------ ff/d/zmqTransport.hpp | 297 ---- ff/dc.hpp | 4 +- ff/dff.hpp | 4 +- ff/dinout.hpp | 6 +- ff/distributed/ff_dgroup.hpp | 26 + ff/distributed/ff_dgroups.hpp | 24 + ff/distributed/ff_dreceiver.hpp | 24 + ff/distributed/ff_dsender.hpp | 24 + ff/distributed/ff_network.hpp | 24 + ff/distributed/ff_wrappers.hpp | 24 + ff/distributed/loader/dff_run.cpp | 23 + ff/dnode.hpp | 4 +- ff/dynlinkedlist.hpp | 4 +- ff/dynqueue.hpp | 4 +- ff/farm.hpp | 4 +- ff/ff.hpp | 4 +- ff/ff_queue.hpp | 6 +- ff/fftree.hpp | 4 +- ff/graph_utils.hpp | 4 +- ff/gsearch.hpp | 4 +- ff/gt.hpp | 6 +- ff/lb.hpp | 6 +- ff/make_unique.hpp | 21 + ff/map.hpp | 4 +- ff/mapCUDAManaged.hpp | 4 +- ff/mapper.hpp | 4 +- ff/mapping_string.sh | 18 + ff/mapping_utils.hpp | 4 +- ff/mdf.hpp | 4 +- ff/mpmc/MPMCqueues.hpp | 19 + ff/multinode.hpp | 4 +- ff/node.hpp | 4 +- ff/ocl/clEnvironment.hpp | 31 +- ff/oclallocator.hpp | 30 +- ff/oclnode.hpp | 27 +- ff/optimize.hpp | 4 +- ff/ordering_policies.hpp | 4 +- ff/parallel_for.hpp | 4 +- ff/parallel_for_internals.hpp | 4 +- ff/pipeline.hpp | 6 +- ff/platforms/platform.h | 6 +- ff/platforms/pthread_minport_windows.h | 6 +- ff/poolEvolution.hpp | 4 +- ff/poolEvolutionCUDA.hpp | 4 +- ff/repara/baseKernelTask.hpp | 31 +- ff/repara/rprkernels.hpp | 31 +- ff/selector.hpp | 4 +- ff/spin-lock.hpp | 4 +- ff/squeue.hpp | 4 +- ff/staticallocator.hpp | 8 +- ff/staticlinkedlist.hpp | 4 +- ff/stencilReduce.hpp | 4 +- ff/stencilReduceCUDA.hpp | 4 +- ff/stencilReduceOCL.hpp | 4 +- ff/stencilReduceOCL_macros.hpp | 19 + ff/svector.hpp | 4 +- ff/task_internals.hpp | 4 +- ff/taskf.hpp | 4 +- ff/tpc/tpcEnvironment.hpp | 32 +- ff/tpcallocator.hpp | 30 +- ff/tpcnode.hpp | 28 +- ff/ubuffer.hpp | 4 +- ff/utils.hpp | 4 +- ff/version.h | 4 +- tests/d/CMakeLists.txt | 24 - tests/d/Makefile | 112 -- tests/d/README | 214 --- tests/d/allgather.cpp | 51 - tests/d/broadcast.cpp | 50 - tests/d/bw11.cpp | 138 -- tests/d/dmap.cpp | 410 ----- tests/d/dmap2.cpp | 405 ----- tests/d/farm.cpp | 290 ---- tests/d/farm_farm.cpp | 422 ----- tests/d/fromany.cpp | 49 - tests/d/lat11.cpp | 193 --- tests/d/lat11_v2.cpp | 233 --- tests/d/ondemand.cpp | 73 - tests/d/pipe_farm.cpp | 284 ---- tests/d/pipe_farm2.cpp | 523 ------ tests/d/scatter.cpp | 58 - tests/d/test11_pipe.cpp | 184 --- tests/d/test11_torus.cpp | 219 --- tests/d/test_dinout.cpp | 151 -- tests/d/test_gw.cpp | 140 -- tests/d/test_marshal.cpp | 214 --- tests/d/unicast.cpp | 49 - 101 files changed, 538 insertions(+), 8002 deletions(-) rename COPYING.LESSER => LICENSE.LGPL (100%) create mode 100644 LICENSE.MIT delete mode 100644 ff/d/inter.hpp delete mode 100644 ff/d/zmq.hpp delete mode 100644 ff/d/zmqImpl.hpp delete mode 100644 ff/d/zmqTransport.hpp delete mode 100644 tests/d/CMakeLists.txt delete mode 100644 tests/d/Makefile delete mode 100644 tests/d/README delete mode 100644 tests/d/allgather.cpp delete mode 100644 tests/d/broadcast.cpp delete mode 100644 tests/d/bw11.cpp delete mode 100644 tests/d/dmap.cpp delete mode 100644 tests/d/dmap2.cpp delete mode 100644 tests/d/farm.cpp delete mode 100644 tests/d/farm_farm.cpp delete mode 100644 tests/d/fromany.cpp delete mode 100644 tests/d/lat11.cpp delete mode 100644 tests/d/lat11_v2.cpp delete mode 100644 tests/d/ondemand.cpp delete mode 100644 tests/d/pipe_farm.cpp delete mode 100644 tests/d/pipe_farm2.cpp delete mode 100644 tests/d/scatter.cpp delete mode 100644 tests/d/test11_pipe.cpp delete mode 100644 tests/d/test11_torus.cpp delete mode 100644 tests/d/test_dinout.cpp delete mode 100644 tests/d/test_gw.cpp delete mode 100644 tests/d/test_marshal.cpp delete mode 100644 tests/d/unicast.cpp diff --git a/AUTHORS b/AUTHORS index 9a895621..e7bf99d8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,16 +1,17 @@ FastFlow has been originally designed by: -Massimo Torquati ( ) and + +Massimo Torquati ( ) and Marco Aldinucci (). +Starting from version 3.0.1 FastFlow is released under the dual license +model LGPL-v3 and MIT. + ------------- -Massimo Torquati is the current mantainer (2010-) -Send any patches or fixes to the mantainer. Thanks. +Massimo Torquati is the mantainer and developer of the FastFlow library. +Send any improvements or fixes to the mantainer. Thanks. -Contributions +Contributors ------------- - -Fedor Sakharov -- -Mehdi Goli -- -Maurizio Drocco -- -Guilherme Peretti Pezzi -- \ No newline at end of file +Over the years many people contributed in different ways to the development +of the FastFlow library. Thanks to all of them! diff --git a/COPYING.LESSER b/LICENSE.LGPL similarity index 100% rename from COPYING.LESSER rename to LICENSE.LGPL diff --git a/LICENSE.MIT b/LICENSE.MIT new file mode 100644 index 00000000..187d7a38 --- /dev/null +++ b/LICENSE.MIT @@ -0,0 +1,19 @@ + Copyright (c) 2022- Massimo Torquati (massimo.torquati@unipi.it) + + Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 5476ffc3..a7253107 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GitHub tag](https://img.shields.io/github/tag/fastflow/fastflow.svg)](http://github.com/fastflow/fastflow/releases) [![GitHub Issues](https://img.shields.io/github/issues/fastflow/fastflow.svg)](http://github.com/fastflow/fastflow/issues) @@ -85,6 +86,9 @@ Marco Aldinucci (University of Turin). Over the years several other people (mainly from the Parallel Computing Groups of the University of Pisa and Turin) contributed with ideas and code to the development of the project. FastFlow has been used as run-time system in three EU founded research projects: ParaPhrase, REPARA and RePhrase. +## About the License +From version 3.0.1, FastFlow is released with a dual license: LGPL-3 and MIT. + ## How to cite FastFlow Aldinucci, M. , Danelutto, M. , Kilpatrick, P. and Torquati, M. (2017). Fastflow: High‐Level and Efficient Streaming on Multicore. In Programming multi‐core and many‐core computing systems (eds S. Pllana and F. Xhafa). [![FF_DOI_badge](https://img.shields.io/badge/DOI-https%3A%2F%2Fdoi.org%2F10.1002%2F9781119332015.ch13-blue.svg)](https://doi.org/10.1002/9781119332015.ch13) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 85c7c1ed..bd930b6a 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -16,9 +16,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/allocator.hpp b/ff/allocator.hpp index 02a0012f..a8b358ec 100644 --- a/ff/allocator.hpp +++ b/ff/allocator.hpp @@ -30,9 +30,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/barrier.hpp b/ff/barrier.hpp index d5fd3882..486fbd37 100644 --- a/ff/barrier.hpp +++ b/ff/barrier.hpp @@ -13,9 +13,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/bitflags.hpp b/ff/bitflags.hpp index b3bd2460..22cc47c4 100644 --- a/ff/bitflags.hpp +++ b/ff/bitflags.hpp @@ -9,9 +9,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/buffer.hpp b/ff/buffer.hpp index f2642080..7c037a54 100644 --- a/ff/buffer.hpp +++ b/ff/buffer.hpp @@ -30,9 +30,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/combine.hpp b/ff/combine.hpp index 0e7910c4..88343e1f 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -16,9 +16,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/config.hpp b/ff/config.hpp index e2c6c3cc..6f6d899f 100644 --- a/ff/config.hpp +++ b/ff/config.hpp @@ -3,9 +3,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/d/inter.hpp b/ff/d/inter.hpp deleted file mode 100644 index 7f938b00..00000000 --- a/ff/d/inter.hpp +++ /dev/null @@ -1,163 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ - -/** - * \link - * \file inter.hpp - * \ingroup basic_blocks - * - * \brief Communication patten interface (distributed) - * - */ - -/* *************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - **************************************************************************** - */ - -#ifndef FF_COMMINTERFACE_HPP -#define FF_COMMINTERFACE_HPP - - -namespace ff { - - -//************************************** -// Communication Pattern interface -//************************************** - -/* - * \class commPattern - * \ingroup streaming_network_simple_distributed_memory - * - * \brief This is a tempalte class which defines the communication pattern - * interface. - * - * This class is defined in file \ref inter.hpp - * - */ - -template -class commPattern { - -protected: - Impl impl; - -public: - typedef typename Impl::descriptor descriptor; - typedef typename Impl::tosend_t tosend_t; - typedef typename Impl::torecv_t torecv_t; - typedef typename Impl::TransportImpl TransportImpl; - - - commPattern():impl() {} - - commPattern(descriptor* D):impl(D) {} - - inline void setDescriptor(descriptor* D) { impl.setDescriptor(D); } - - inline descriptor* getDescriptor() { return impl.getDescriptor(); } - - /* - * It initializes communication pattern. - * - * \param address is the IP address of the sender node. - * \param nodeId is the unique identifier of the calling node in the range [0..inf[. - */ - inline bool init(const std::string& address,const int nodeId=-1) { return impl.init(address,nodeId); } - - /* - * It specifies that the message being sent is just a part of the - * entire message. Further message parts are to follow. - * - * \param msg is the message to be sent. - */ - inline bool putmore(const tosend_t& msg) { return impl.putmore(msg);} - - /* - * It sends one message to the targeted node. - * - * \param msgs is the message to be sent. - */ - inline bool put(const tosend_t& msg) { return impl.put(msg); } - - /* - * It sends one message to the targeted node. - * - * \param msgs is the message to be sent. - * \param toNode is the address of the node, where the message is intended - * to be sent. - */ - inline bool put(const tosend_t& msg, const int toNode) { return impl.put(msg,toNode);} - - /* - * It receives the message header. - * - * \param msg is the pointer of the message to be sent. - * \param peer is the address of the peer where the message is sent. - */ - inline bool gethdr(torecv_t& msg, int& peer) { return impl.gethdr(msg,peer); } - - /* - * It receives one message part. - * - * \param msg is the pointer of the message to be sent. - */ - inline bool get(torecv_t& msg) { return impl.get(msg); } - - /* - * It receives all messages. - */ - inline void done() { impl.done(); } - - /* - * It closes the communication pattern. - */ - inline bool close() { return impl.close(); } -}; - -//************************************** -// Communication Transport interface -//************************************** - - -template -class commTransport { -protected: - Impl impl; -public: - typedef typename Impl::endpoint_t endpoint_t; - typedef typename Impl::msg_t msg_t; - - commTransport(const int procId): impl(procId) {} - - int initTransport() { return impl.initTransport(); } - - int closeTransport() { return impl.closeTransport(); } - - endpoint_t * newEndPoint(const bool P) { - return impl.newEndPoint(P); - } - - int deleteEndPoint(endpoint_t* ep) { - return impl.deleteEndPoint(ep); - } - - int getProcId() const { return impl.getProcId();} -}; - - -} // namespace -#endif /* FF_COMMINTERFACE_HPP */ diff --git a/ff/d/zmq.hpp b/ff/d/zmq.hpp deleted file mode 100644 index 06fa9cd9..00000000 --- a/ff/d/zmq.hpp +++ /dev/null @@ -1,761 +0,0 @@ -/* - Copyright (c) 2009-2011 250bpm s.r.o. - Copyright (c) 2011 Botond Ballo - Copyright (c) 2007-2009 iMatix Corporation - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to - deal in the Software without restriction, including without limitation the - rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - sell copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - IN THE SOFTWARE. -*/ - -#ifndef __ZMQ_HPP_INCLUDED__ -#define __ZMQ_HPP_INCLUDED__ - -#if __cplusplus >= 201103L -#define ZMQ_CPP11 -#define ZMQ_NOTHROW noexcept -#define ZMQ_EXPLICIT explicit -#else - #define ZMQ_CPP03 - #define ZMQ_NOTHROW - #define ZMQ_EXPLICIT -#endif - -#include "zmq.h" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef ZMQ_CPP11 -#include -#include -#endif - -// Detect whether the compiler supports C++11 rvalue references. -#if (defined(__GNUC__) && (__GNUC__ > 4 || \ - (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && \ - defined(__GXX_EXPERIMENTAL_CXX0X__)) - #define ZMQ_HAS_RVALUE_REFS - #define ZMQ_DELETED_FUNCTION = delete -#elif defined(__clang__) - #if __has_feature(cxx_rvalue_references) - #define ZMQ_HAS_RVALUE_REFS - #endif - - #if __has_feature(cxx_deleted_functions) - #define ZMQ_DELETED_FUNCTION = delete - #else - #define ZMQ_DELETED_FUNCTION - #endif -#elif defined(_MSC_VER) && (_MSC_VER >= 1600) - #define ZMQ_HAS_RVALUE_REFS - #define ZMQ_DELETED_FUNCTION -#else - #define ZMQ_DELETED_FUNCTION -#endif - -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0) -#define ZMQ_NEW_MONITOR_EVENT_LAYOUT -#endif - -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) -#define ZMQ_HAS_PROXY_STEERABLE -/* Socket event data */ -typedef struct { - uint16_t event; // id of the event as bitfield - int32_t value ; // value is either error code, fd or reconnect interval -} zmq_event_t; -#endif - -// In order to prevent unused variable warnings when building in non-debug -// mode use this macro to make assertions. -#ifndef NDEBUG -# define ZMQ_ASSERT(expression) assert(expression) -#else -# define ZMQ_ASSERT(expression) (void)(expression) -#endif - -namespace zmq -{ - - typedef zmq_free_fn free_fn; - typedef zmq_pollitem_t pollitem_t; - - class error_t : public std::exception - { - public: - - error_t () : errnum (zmq_errno ()) {} - - virtual const char *what () const throw () - { - return zmq_strerror (errnum); - } - - int num () const - { - return errnum; - } - - private: - - int errnum; - }; - - inline int poll (zmq_pollitem_t const* items_, int nitems_, long timeout_ = -1) - { - int rc = zmq_poll (const_cast(items_), nitems_, timeout_); - if (rc < 0) - throw error_t (); - return rc; - } - - inline int poll(zmq_pollitem_t const* items, size_t nitems) - { - return poll(items, nitems, -1 ); - } - - #ifdef ZMQ_CPP11 - inline int poll(zmq_pollitem_t const* items, size_t nitems, std::chrono::milliseconds timeout) - { - return poll(items, nitems, timeout.count() ); - } - - inline int poll(std::vector const& items, std::chrono::milliseconds timeout) - { - return poll(items.data(), items.size(), timeout.count() ); - } - #endif - - inline int poll(std::vector const& items, long timeout_ = -1) - { - return poll(items.data(), items.size(), timeout_); - } - - - - inline void proxy (void *frontend, void *backend, void *capture) - { - int rc = zmq_proxy (frontend, backend, capture); - if (rc != 0) - throw error_t (); - } - -#ifdef ZMQ_HAS_PROXY_STEERABLE - inline void proxy_steerable (void *frontend, void *backend, void *capture, void *control) - { - int rc = zmq_proxy_steerable (frontend, backend, capture, control); - if (rc != 0) - throw error_t (); - } -#endif - - inline void version (int *major_, int *minor_, int *patch_) - { - zmq_version (major_, minor_, patch_); - } - - #ifdef ZMQ_CPP11 - inline std::tuple version() - { - std::tuple v; - zmq_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v) ); - return v; - } - #endif - - class message_t - { - friend class socket_t; - - public: - - inline message_t () - { - int rc = zmq_msg_init (&msg); - if (rc != 0) - throw error_t (); - } - - inline explicit message_t (size_t size_) - { - int rc = zmq_msg_init_size (&msg, size_); - if (rc != 0) - throw error_t (); - } - - template message_t(I first, I last): - msg() - { - typedef typename std::iterator_traits::difference_type size_type; - typedef typename std::iterator_traits::pointer pointer_t; - - size_type const size_ = std::distance(first, last); - int const rc = zmq_msg_init_size (&msg, size_); - if (rc != 0) - throw error_t (); - std::copy(first, last, static_cast(zmq_msg_data (&msg)) ); - } - - inline message_t (void *data_, size_t size_, free_fn *ffn_, - void *hint_ = NULL) - { - int rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); - if (rc != 0) - throw error_t (); - } - -#ifdef ZMQ_HAS_RVALUE_REFS - inline message_t (message_t &&rhs): msg (rhs.msg) - { - int rc = zmq_msg_init (&rhs.msg); - if (rc != 0) - throw error_t (); - } - - inline message_t &operator = (message_t &&rhs) ZMQ_NOTHROW - { - std::swap (msg, rhs.msg); - return *this; - } -#endif - - inline ~message_t () ZMQ_NOTHROW - { - int rc = zmq_msg_close (&msg); - ZMQ_ASSERT (rc == 0); - } - - inline void rebuild () - { - int rc = zmq_msg_close (&msg); - if (rc != 0) - throw error_t (); - rc = zmq_msg_init (&msg); - if (rc != 0) - throw error_t (); - } - - inline void rebuild (size_t size_) - { - int rc = zmq_msg_close (&msg); - if (rc != 0) - throw error_t (); - rc = zmq_msg_init_size (&msg, size_); - if (rc != 0) - throw error_t (); - } - - inline void rebuild (void *data_, size_t size_, free_fn *ffn_, - void *hint_ = NULL) - { - int rc = zmq_msg_close (&msg); - if (rc != 0) - throw error_t (); - rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); - if (rc != 0) - throw error_t (); - } - - inline void move (message_t const *msg_) - { - int rc = zmq_msg_move (&msg, const_cast(&(msg_->msg))); - if (rc != 0) - throw error_t (); - } - - inline void copy (message_t const *msg_) - { - int rc = zmq_msg_copy (&msg, const_cast(&(msg_->msg))); - if (rc != 0) - throw error_t (); - } - - inline bool more () const ZMQ_NOTHROW - { - int rc = zmq_msg_more (const_cast(&msg) ); - return rc != 0; - } - - inline void *data () ZMQ_NOTHROW - { - return zmq_msg_data (&msg); - } - - inline const void* data () const ZMQ_NOTHROW - { - return zmq_msg_data (const_cast(&msg)); - } - - inline size_t size () const ZMQ_NOTHROW - { - return zmq_msg_size (const_cast(&msg)); - } - - template T* data() ZMQ_NOTHROW - { - return static_cast( data() ); - } - - template T const* data() const ZMQ_NOTHROW - { - return static_cast( data() ); - } - - - private: - // The underlying message - zmq_msg_t msg; - - // Disable implicit message copying, so that users won't use shared - // messages (less efficient) without being aware of the fact. - message_t (const message_t&) ZMQ_DELETED_FUNCTION; - void operator = (const message_t&) ZMQ_DELETED_FUNCTION; - }; - - class context_t - { - friend class socket_t; - - public: - inline context_t () - { - ptr = zmq_ctx_new (); - if (ptr == NULL) - throw error_t (); - } - - - inline explicit context_t (int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT) - { - ptr = zmq_ctx_new (); - if (ptr == NULL) - throw error_t (); - - int rc = zmq_ctx_set (ptr, ZMQ_IO_THREADS, io_threads_); - ZMQ_ASSERT (rc == 0); - - rc = zmq_ctx_set (ptr, ZMQ_MAX_SOCKETS, max_sockets_); - ZMQ_ASSERT (rc == 0); - } - -#ifdef ZMQ_HAS_RVALUE_REFS - inline context_t (context_t &&rhs) ZMQ_NOTHROW : ptr (rhs.ptr) - { - rhs.ptr = NULL; - } - inline context_t &operator = (context_t &&rhs) ZMQ_NOTHROW - { - std::swap (ptr, rhs.ptr); - return *this; - } -#endif - - inline ~context_t () ZMQ_NOTHROW - { - close(); - } - - inline void close() ZMQ_NOTHROW - { - if (ptr == NULL) - return; - int rc = zmq_ctx_destroy (ptr); - ZMQ_ASSERT (rc == 0); - ptr = NULL; - } - - // Be careful with this, it's probably only useful for - // using the C api together with an existing C++ api. - // Normally you should never need to use this. - inline ZMQ_EXPLICIT operator void* () ZMQ_NOTHROW - { - return ptr; - } - - inline ZMQ_EXPLICIT operator void const* () const ZMQ_NOTHROW - { - return ptr; - } - private: - - void *ptr; - - context_t (const context_t&) ZMQ_DELETED_FUNCTION; - void operator = (const context_t&) ZMQ_DELETED_FUNCTION; - }; - - #ifdef ZMQ_CPP11 - enum class socket_type: int - { - req = ZMQ_REQ, - rep = ZMQ_REP, - dealer = ZMQ_DEALER, - router = ZMQ_ROUTER, - pub = ZMQ_PUB, - sub = ZMQ_SUB, - xpub = ZMQ_XPUB, - xsub = ZMQ_XSUB, - push = ZMQ_PUSH, - pull = ZMQ_PULL, -#if ZMQ_VERSION_MAJOR < 4 - pair = ZMQ_PAIR -#else - pair = ZMQ_PAIR, - stream = ZMQ_STREAM -#endif - }; - #endif - - class socket_t - { - friend class monitor_t; - public: - inline socket_t(context_t& context_, int type_) - { - init(context_, type_); - } - - #ifdef ZMQ_CPP11 - inline socket_t(context_t& context_, socket_type type_) - { - init(context_, static_cast(type_)); - } - #endif - -#ifdef ZMQ_HAS_RVALUE_REFS - inline socket_t(socket_t&& rhs) ZMQ_NOTHROW : ptr(rhs.ptr) - { - rhs.ptr = NULL; - } - inline socket_t& operator=(socket_t&& rhs) ZMQ_NOTHROW - { - std::swap(ptr, rhs.ptr); - return *this; - } -#endif - - inline ~socket_t () ZMQ_NOTHROW - { - close(); - } - - inline ZMQ_EXPLICIT operator void* () ZMQ_NOTHROW - { - return ptr; - } - - inline ZMQ_EXPLICIT operator void const* () const ZMQ_NOTHROW - { - return ptr; - } - - inline void close() ZMQ_NOTHROW - { - if(ptr == NULL) - // already closed - return ; - int rc = zmq_close (ptr); - ZMQ_ASSERT (rc == 0); - ptr = 0 ; - } - - template void setsockopt(int option_, T const& optval) - { - setsockopt(option_, &optval, sizeof(T) ); - } - - inline void setsockopt (int option_, const void *optval_, - size_t optvallen_) - { - int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_); - if (rc != 0) - throw error_t (); - } - - inline void getsockopt (int option_, void *optval_, - size_t *optvallen_) - { - int rc = zmq_getsockopt (ptr, option_, optval_, optvallen_); - if (rc != 0) - throw error_t (); - } - - template T getsockopt(int option_) - { - T optval; - size_t optlen = sizeof(T); - getsockopt(option_, &optval, &optlen ); - return optval; - } - - inline void bind(std::string const& addr) - { - bind(addr.c_str()); - } - - inline void bind (const char *addr_) - { - int rc = zmq_bind (ptr, addr_); - if (rc != 0) - throw error_t (); - } - - inline void unbind(std::string const& addr) - { - unbind(addr.c_str()); - } - - inline void unbind (const char *addr_) - { - int rc = zmq_unbind (ptr, addr_); - if (rc != 0) - throw error_t (); - } - - inline void connect(std::string const& addr) - { - connect(addr.c_str()); - } - - inline void connect (const char *addr_) - { - int rc = zmq_connect (ptr, addr_); - if (rc != 0) - throw error_t (); - } - - inline void disconnect(std::string const& addr) - { - disconnect(addr.c_str()); - } - - inline void disconnect (const char *addr_) - { - int rc = zmq_disconnect (ptr, addr_); - if (rc != 0) - throw error_t (); - } - - inline bool connected() const ZMQ_NOTHROW - { - return(ptr != NULL); - } - - inline size_t send (const void *buf_, size_t len_, int flags_ = 0) - { - int nbytes = zmq_send (ptr, buf_, len_, flags_); - if (nbytes >= 0) - return (size_t) nbytes; - if (zmq_errno () == EAGAIN) - return 0; - throw error_t (); - } - - inline bool send (message_t &msg_, int flags_ = 0) - { - int nbytes = zmq_msg_send (&(msg_.msg), ptr, flags_); - if (nbytes >= 0) - return true; - if (zmq_errno () == EAGAIN) - return false; - throw error_t (); - } - - template bool send(I first, I last, int flags_=0) - { - zmq::message_t msg(first, last); - return send(msg, flags_); - } - -#ifdef ZMQ_HAS_RVALUE_REFS - inline bool send (message_t &&msg_, int flags_ = 0) - { - return send(msg_, flags_); - } -#endif - - inline size_t recv (void *buf_, size_t len_, int flags_ = 0) - { - int nbytes = zmq_recv (ptr, buf_, len_, flags_); - if (nbytes >= 0) - return (size_t) nbytes; - if (zmq_errno () == EAGAIN) - return 0; - throw error_t (); - } - - inline bool recv (message_t *msg_, int flags_ = 0) - { - int nbytes = zmq_msg_recv (&(msg_->msg), ptr, flags_); - if (nbytes >= 0) - return true; - if (zmq_errno () == EAGAIN) - return false; - throw error_t (); - } - - private: - inline void init(context_t& context_, int type_) - { - ctxptr = context_.ptr; - ptr = zmq_socket (context_.ptr, type_ ); - if (ptr == NULL) - throw error_t (); - } - - void *ptr; - void *ctxptr; - - socket_t (const socket_t&) ZMQ_DELETED_FUNCTION; - void operator = (const socket_t&) ZMQ_DELETED_FUNCTION; - }; - - class monitor_t - { - public: - monitor_t() : socketPtr(NULL) {} - virtual ~monitor_t() {} - - void monitor(socket_t &socket, std::string const& addr, int events = ZMQ_EVENT_ALL) - { - monitor(socket, addr.c_str(), events); - } - - void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL) - { - int rc = zmq_socket_monitor(socket.ptr, addr_, events); - if (rc != 0) - throw error_t (); - - socketPtr = socket.ptr; - void *s = zmq_socket (socket.ctxptr, ZMQ_PAIR); - assert (s); - - rc = zmq_connect (s, addr_); - assert (rc == 0); - - on_monitor_started(); - - while (true) { - zmq_msg_t eventMsg; - zmq_msg_init (&eventMsg); - rc = zmq_recvmsg (s, &eventMsg, 0); - if (rc == -1 && zmq_errno() == ETERM) - break; - assert (rc != -1); -#if ZMQ_VERSION_MAJOR >= 4 - const char* data = static_cast(zmq_msg_data(&eventMsg)); - zmq_event_t msgEvent; - memcpy(&msgEvent.event, data, sizeof(uint16_t)); data += sizeof(uint16_t); - memcpy(&msgEvent.value, data, sizeof(int32_t)); - zmq_event_t* event = &msgEvent; -#else - zmq_event_t* event = static_cast(zmq_msg_data(&eventMsg)); -#endif - -#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT - zmq_msg_t addrMsg; - zmq_msg_init (&addrMsg); - rc = zmq_recvmsg (s, &addrMsg, 0); - if (rc == -1 && zmq_errno() == ETERM) - break; - assert (rc != -1); - const char* str = static_cast(zmq_msg_data (&addrMsg)); - std::string address(str, str + zmq_msg_size(&addrMsg)); - zmq_msg_close (&addrMsg); -#else - // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types. - std::string address = event->data.connected.addr; -#endif - -#ifdef ZMQ_EVENT_MONITOR_STOPPED - if (event->event == ZMQ_EVENT_MONITOR_STOPPED) - break; -#endif - - switch (event->event) { - case ZMQ_EVENT_CONNECTED: - on_event_connected(*event, address.c_str()); - break; - case ZMQ_EVENT_CONNECT_DELAYED: - on_event_connect_delayed(*event, address.c_str()); - break; - case ZMQ_EVENT_CONNECT_RETRIED: - on_event_connect_retried(*event, address.c_str()); - break; - case ZMQ_EVENT_LISTENING: - on_event_listening(*event, address.c_str()); - break; - case ZMQ_EVENT_BIND_FAILED: - on_event_bind_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_ACCEPTED: - on_event_accepted(*event, address.c_str()); - break; - case ZMQ_EVENT_ACCEPT_FAILED: - on_event_accept_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_CLOSED: - on_event_closed(*event, address.c_str()); - break; - case ZMQ_EVENT_CLOSE_FAILED: - on_event_close_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_DISCONNECTED: - on_event_disconnected(*event, address.c_str()); - break; - default: - on_event_unknown(*event, address.c_str()); - break; - } - zmq_msg_close (&eventMsg); - } - zmq_close (s); - socketPtr = NULL; - } - -#ifdef ZMQ_EVENT_MONITOR_STOPPED - void abort() - { - if (socketPtr) - zmq_socket_monitor(socketPtr, NULL, 0); - } -#endif - virtual void on_monitor_started() {} - virtual void on_event_connected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_connect_delayed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_connect_retried(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_listening(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_bind_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_accepted(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_accept_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_closed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_close_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_disconnected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - virtual void on_event_unknown(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } - private: - void* socketPtr; - }; -} - -#endif diff --git a/ff/d/zmqImpl.hpp b/ff/d/zmqImpl.hpp deleted file mode 100644 index a9152b9f..00000000 --- a/ff/d/zmqImpl.hpp +++ /dev/null @@ -1,2079 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ - -/* - * \file zmqImpl.hpp - * \ingroup building_blocks - * - * \brief This file describes the communication patterns of distributed - * FastFlow using ØMQ. - */ - -/* *************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - **************************************************************************** - */ - -#ifndef FF_ZMQIMPL_HPP -#define FF_ZMQIMPL_HPP - - -#include -#include -#include -#include -#include -#include -#include "zmq.hpp" -#include -#include - -namespace ff { - - -#define CONNECTION_PROTOCOL 1 -//#undef CONNECTION_PROTOCOL -#if defined(DEBUG) - #define zmqTDBG(x) x -#else - #define zmqTDBG(x) -#endif - -/* ---------------- Pattern macro definitions -------------------- */ - -#define UNICAST commPattern -#define UNICAST_DESC(name,trasp,P) UNICAST::descriptor(name,1,trasp,P) -#define BROADCAST commPattern -#define BROADCAST_DESC(name,n,trasp,P) BROADCAST::descriptor(name,n,trasp,P) -#define ALLGATHER commPattern -#define ALLGATHER_DESC(name,n,trasp,P) ALLGATHER::descriptor(name,n,trasp,P) -#define FROMANY commPattern -#define FROMANY_DESC(name,n,trasp,P) FROMANY::descriptor(name,n,trasp,P) -#define SCATTER commPattern -#define SCATTER_DESC(name,n,trasp,P) SCATTER::descriptor(name,n,trasp,P) -#define ONETOMANY commPattern -#define ONETOMANY_DESC(name,n,trasp,P) ONETOMANY::descriptor(name,n,trasp,P) -#define ONDEMAND commPattern -#define ONDEMAND_DESC(name,n,trasp,P) ONDEMAND::descriptor(name,n,trasp,P) - -/* TODO */ -#define MANYTOONE commPattern -#define MANYTOONE_DESC(name,n,trasp,P) MANYTOONE::descriptor(name,n,trasp,P) - -//************************************* -// One to Many communication -//************************************* - -/* - * \struct descriptor1_N - * \ingroup building_blocks - * - * \brief This struct is used in several communication patterns; Unicast, - * Broadcast, Scatter, On-Demand, OneToMany, FromAny etc. - * - */ -struct descriptor1_N { - typedef zmqTransportMsg_t msg_t; - - /* - * Constructor. - * - * It Creates a One-to-Many descriptor, that is, instantiate a - * transport layer using the first node as a router. - * - * \param name is the name of the descriptor. - * \param peers is the number of peers involved (receiving nodes). - * \param transport is a pointer to the transport layer object of class - * zmqTransport. - * \param P is a flag that identifies whether the descriptor refers to a \p - * sender (i.e. a Router) or to a \p receiver. - * - */ - descriptor1_N(const std::string name, const int peers, zmqTransport* const transport, const bool P): - name(name), socket(NULL), transport(transport), P(P), peers(P ? peers : 1), zmqHdrSet(false), transportIDs(peers) - { - if (transport) - socket = transport->newEndPoint(!P); - // NOTE: we want to use ZMQ_ROUTER at producer side !P means ROUTER - // => create a socket (a new end-point) acting as a ROUTER -- REW, - // P always true? - - if (P) { - std::stringstream identity; - for(int i = 0; i < peers; ++i) { - identity.str(""); - identity << name << ":" << i; - transportIDs[i]=identity.str(); - } - } - } - - /* - * Destructor - */ - ~descriptor1_N() { close(); socket=NULL; } - - /* - * It closes active connection and delete the existing socket. - * - * \return It delets the end point from socket and return NULL. If it is - * not succefful then a negative value is returned. - */ - inline int close() { - if (socket) return transport->deleteEndPoint(socket); - return -1; - } - - /* - * It initialises a socket and estabilishes a connection to the given address. - * - * \param addr is the IP address in the form 'ip/host:port' - * \param nodeId is the node identifier in the range [0..inf[ - * - * \exception TODO - * \return TODO - */ - inline int init(const std::string& addr, const int nodeId=-1) { - if (!socket) return -1; - - if (!P) { // if ROUTER: routing of messages to a specific connection - std::stringstream identity; - identity.str(""); - identity << name << ":" << ((nodeId!=-1) ? nodeId:transport->getProcId()); - // set durable identity. it must be set before connecting the socket - socket->setsockopt(ZMQ_IDENTITY, identity.str().c_str(), identity.str().length() + 1); - - std::stringstream address; - address.str(""); - address << "tcp://" << addr; - socket->connect(address.str().c_str()); - -#if defined(CONNECTION_PROTOCOL) - sendHelloRecvHi(); -#endif - } else { // if DEALER: fair-queuing on input and load-balancing on output - int i; - if ((i=addr.find(":"))<0) { - printf("init: ERROR -- wrong IP address format for %s\n",addr.c_str()); - return -1; - } - int port = atoi((addr.substr(i+1)).c_str()); - std::stringstream address; - address << "tcp://*:" << port; - try { - socket->bind(address.str().c_str()); - } catch(std::exception& e) { - printf("initTransport: ERROR -- binding address (%s): %s\n", - address.str().c_str(), e.what()); - return -1; - } -#if defined(CONNECTION_PROTOCOL) - recvHelloSendHi(); -#endif - } - return 0; - } - - /* - * It broadcast a message to all connected peers. - * - * \param msg is a eference to the message to be sent - * \param flags is a flag indcating whether the operation should be in - * non-blocking mode or if the message is a multi-part message. - * See ØMQ's API for details. - * - * \return TODO - * - * \exception TODO - */ - inline bool send(const msg_t& msg, int flags = 0) { - for(int i=0; i < peers; ++i) { - msg_t _msg; - _msg.copy(const_cast(msg)); - zmq::message_t to(const_cast(transportIDs[i].c_str()), transportIDs[i].length()+1,0,0); - try { - if (!zmqHdrSet) if (!socket->send(to,ZMQ_SNDMORE)) return false; - if (!socket->send(_msg, flags)) return false; - } catch (std::exception& e) { - return false; - } - } - zmqHdrSet=false; - return true; - } - - /* - * It broadcasts other parts of a multi-parts message to all connected peers. - * This method exits if the flag is not set to ZMQ_SNDMORE. - * - * \param msg eference to the message to be sent - * \param flags a flag indcating whether the operation should be in - * non-blocking mode or if the message is a multi-part message. - * See ØMQ's API for details. - * - * \return It returns the message. - */ - inline bool sendmore(const msg_t& msg, int flags) { - assert(flags==ZMQ_SNDMORE); - bool v=send(msg,flags); - zmqHdrSet=true; - return v; - } - - /* - * It unicasts a message to a specified destination - * - * \param msg is a reference to the message to be sent - * \param dest is the ID of the receiving peer. - * \param flags is a flag indcating whether the operation should be in - * non-blocking mode or if the message is a multi-part message. - * See ØMQ's API for details. - * - * \exception TODO - * \return TODO - * - */ - inline bool send(const msg_t& msg, const int dest, int flags) { - assert(dest(transportIDs[dest].c_str()), transportIDs[dest].length()+1,0,0); - try { - if (!zmqHdrSet) if (!socket->send(to,ZMQ_SNDMORE)) return false; - if (!socket->send(const_cast(msg), flags)) return false; - } catch (std::exception& e) { - return false; - } - zmqHdrSet=false; - return true; - } - - /* - * It unicasts other parts of a multi-parts message to a specified - * destination. This method exits if the flag is not set to ZMQ_SNDMORE. - * - * \param msg is a reference to the message to be sent - * \param dest is the ID of the receiving peer. - * \param flags is a flag indcating whether the operation should be in - * non-blocking mode or if the message is a multi-part message. - * See ØMQ's API for details. - * - * \return The contents of the message is returned. - */ - inline bool sendmore(const msg_t& msg, const int dest, int flags) { - assert(flags==ZMQ_SNDMORE); - bool v=send(msg,dest,flags); - zmqHdrSet=true; - return v; - } - - /* - * It receives a message header - * - * \return Contents of the message. - */ - inline bool recvhdr(msg_t& msg) { - return recv(msg); - } - - /* - * It receives a message after having received the header. - * - * \exceptionn TODO - * \return TODO - */ - inline bool recv(msg_t& msg) { - try { - if(!socket->recv(&msg)) { - zmqTDBG(printf("recv RETURN -1\n")); - return false; - } - } catch (std::exception & e) { - return false; - } - return true; - } - - /* - * It returns the total number of peers (NOTE: if !P, peers is 1). - * - * \return The number of peers. - */ - inline int getPeers() const { return peers;} - - // variables - const std::string name; // name of descriptor (?) - zmq::socket_t* socket; // zmq socket object - zmqTransport * const transport; - const bool P; - const int peers; - bool zmqHdrSet; - std::vector transportIDs; - -#if defined(CONNECTION_PROTOCOL) - /* - * It defines the sending of a message. - */ - inline void sendHelloRecvHi() { - const char hello[] = "HELLO"; - // sending Hello - zmqTDBG(printf("%s sending HELLO\n", name.c_str())); - zmqTransportMsg_t req(hello,6); - sendreq(req); - // receive Hi - zmqTransportMsg_t msg; - recvreply(msg); - zmqTDBG(printf("%s received %s\n", name.c_str(), static_cast(msg.getData()))); - } - - /* - * It defines the sending of requests. - * - * \exception TODO - * \return TODO - */ - inline bool sendreq(const msg_t& msg) { - try { - if (!socket->send(const_cast(msg))) return false; - } catch (std::exception& e) { - return false; - } - return true; - } - - /* - * It defines the receiving of reply. - * - * \exception TODO - * \return TODO - */ - inline int recvreply(msg_t& msg) { - try { - if(!socket->recv(&msg)) { - zmqTDBG(printf("recv RETURN -1\n")); - return false; - } - } catch (std::exception & e) { - return false; - } - return true; - } - - /* - * It receives a message from a ZMQ_ROUTER - * - * \exception TODO - * \return TODO - */ - inline bool recvreq(msg_t& msg) { - msg_t from; - for(int i = 0; i < peers; ++i) { - try { - if(!socket->recv(&from)) { - zmqTDBG(printf("recvreq RETURN -1\n")); - return false; - } - if(!socket->recv(&msg)) { - zmqTDBG(printf("recvreq RETURN -1\n")); - return false; - } - zmqTDBG(printf("%s received from %s %s\n",name.c_str(), - static_cast(from.getData()), - static_cast(msg.getData()))); - } catch (std::exception & e) { - return false; - } - } - return true; - } - - /* - * It is used in the on-demand pattern by the producer (i.e. the on-demand consumer). - * - * \return The contents of the message. - */ - inline bool sendReq(const msg_t& msg) { - return sendreq(msg); - } - /* - * It is used in the on-demand pattern by the consumer (i.e. the on-demand producer). - * - * \param flags can be ZMQ_NOBLOCK - * - * \exception TODO - * \return TODO - */ - inline bool recvReq(int& peer, int flags=0) { - zmq::message_t from; - zmq::message_t msg; - try { - if(!socket->recv(&from, flags)) return false; - const std::string & f=static_cast(from.data()); - int i; - if ((i=f.find(":")) < 0) { - printf("recvhdr: ERROR: wrong header\n"); - return false; - } - peer = atoi((f.substr(i+1)).c_str()); - if(!socket->recv(&msg)) { - zmqTDBG(printf("recvreq RETURN -1\n")); - return false; - } - } catch (std::exception & e) { - return false; - } - return true; - } - - /* - * It is used for sending back the message to the sender. - * - * \exception TODO - * \return TODO - */ - inline bool sendreply(const msg_t& msg) { - for(int i = 0; i < peers; ++i) { - msg_t _msg; - _msg.copy(const_cast(msg)); - zmq::message_t to(const_cast(transportIDs[i].c_str()), transportIDs[i].length()+1,0,0); - try { - if (!socket->send(to,ZMQ_SNDMORE)) return false; - if (!socket->send(_msg)) return false; - } catch (std::exception& e) { - return false; - } - } - return true; - } - - /* - * It defines the receiving of hi. - */ - inline void recvHelloSendHi() { - static const char hi[] = "HI"; // need to be static - // receives Hello and sends Hi - zmqTransportMsg_t req; - recvreq(req); - zmqTransportMsg_t rep(hi,3); - send(rep); - zmqTDBG(printf("%s sending HI\n", name.c_str())); - } -#endif -}; // end descriptor1_N - -//************************************** -// Many to One Communication -// -// used in ALLgather, collect fromANY, ManyToOne -//************************************** - -/* - * \struct descriptorN_1 - * \ingroup building_blocks - * - * \brief Descriptor used in several communication patterns: AllGather, - * Collect FromAny, ManyToOne. - * - * This class is defined in zmqImpl.hpp - * - */ -struct descriptorN_1 { - typedef zmqTransportMsg_t msg_t; - - /* - * Constructor. - * - * It creates a Many-to-One descriptor, that is, instantiate a transport - * layer using the first node as a router. - * - * \param name is the name of the descriptor - * \param peers is the number of peers involved (sending nodes) - * \param transport is the pointer to the transport layer object of class - * zmqTransport - * \param P is the flag that identifies whether the descriptor refers to a \p sender - * (i.e. a Router) or to a \p receiver. - */ - descriptorN_1(const std::string name, const int peers, zmqTransport* const transport, const bool P ) : - name(name), socket(NULL), transport(transport), P(P), peers(P?1:peers), recvHdr(true), transportIDs(peers) - { - if (transport) socket = transport->newEndPoint(P); - - if (!P) { - std::stringstream identity; - for(int i = 0; i < peers; ++i) { - identity.str(""); - identity << name << ":" << i; - transportIDs[i] = identity.str(); - } - } - } - - /* - * Destructor - */ - ~descriptorN_1() { close(); socket=NULL; } - - /* - * It closes active connection and delete the existing socket. - * - * \return Deletes the end point and a null pointer is returned, otherwise - * a negative value is returned. - */ - inline int close() { - if (socket) return transport->deleteEndPoint(socket); - return -1; - } - - /* - * It initialises a socket and estabilish a connection to the given - * address. - * - * \param addr is the IP address in the form 'ip/host:port'. - * \param nodeId is the node identifier in the range [0..inf[. - * - * \exception TODO - * \return TODO - */ - inline int init(const std::string& addr, const int nodeId = -1) { - if (!socket) return -1; - - if (P) { - std::stringstream identity; - identity.str(""); - identity << name << ":" << ((nodeId != -1) ? nodeId:transport->getProcId()); - // set durable identity. it must be set before connecting the socket - socket->setsockopt(ZMQ_IDENTITY, identity.str().c_str(), identity.str().length() + 1); - - std::stringstream address; - address.str(""); - address << "tcp://" << addr; - socket->connect(address.str().c_str()); - -#if defined(CONNECTION_PROTOCOL) - sendHelloRecvHi(); -#endif - } else { - int i; - if ((i = addr.find(":")) < 0) { - printf("init: ERROR -- wrong IP address format for %s\n",addr.c_str()); - return -1; - } - int port = atoi((addr.substr(i + 1)).c_str()); - std::stringstream address; - address << "tcp://*:" << port; - try { - socket->bind(address.str().c_str()); - } catch(std::exception& e) { - printf("initTransport: ERROR -- binding address (%s): %s\n", - address.str().c_str(), e.what()); - return -1; - } -#if defined(CONNECTION_PROTOCOL) - recvHelloSendHi(); -#endif - } - return 0; - } - - /* - * It sends a message. - * - * \param msg is a reference to the message to be sent. - * \param flags is a flag indicating whether the operation should be in - * non-blocking mode or if the message is a multi-part message. - * See ØMQ's API for details. - * - * \exception TODO - * \return TODO - */ - inline bool send(const msg_t& msg, int flags = 0) { - try { - if (!socket->send(const_cast(msg), flags)) return false; - } catch (std::exception& e) { - return false; - } - return true; - } - - /* - * It receives a message header. - * - * \exception TODO - * \return TODO - */ - inline bool recvhdr(msg_t& msg, int& peer) { - zmq::message_t from; - try { - if(!socket->recv(&from)) { - zmqTDBG(printf("recvhdr RETURN -1\n")); - return false; - } - const std::string & f = static_cast(from.data()); - int i; - if ((i = f.find(":")) < 0) { - printf("recvhdr: ERROR -- wrong header\n"); - return false; - } - peer = atoi((f.substr(i + 1)).c_str()); - recvHdr = false; - if(!socket->recv(&msg)) { - zmqTDBG(printf("recvhdr RETURN -1\n")); - return false; - } - } catch (std::exception & e) { - return false; - } - return true; - } - - /* - * \p It receives a message after having received the header. - * - * \parm msg is a reference to the message to be sent. - * \param peers is the number of peers involved (sending nodes). - * - * \exception TODO - * \return TODO - */ - inline bool recv(msg_t& msg, int& peer) { - try { - if (recvHdr) { - zmq::message_t from; - if(!socket->recv(&from)) { - zmqTDBG(printf("recv RETURN -1\n")); - return false; - } - - const std::string & f=static_cast(from.data()); - int i; - if ((i = f.find(":")) < 0) { - printf("recvhdr: ERROR: wrong header\n"); - return false; - } - peer = atoi((f.substr(i + 1)).c_str()); - } - - if(!socket->recv(&msg)) { - zmqTDBG(printf("recv RETURN -1\n")); - return false; - } - } catch (std::exception & e) { - return false; - } - return true; - } - - /* - * It receives the request. - * - * \exception TODO - * \return TODO - */ - inline bool recvreq() { - try { - msg_t useless; - if(!socket->recv(&useless)) { - zmqTDBG(printf("recvreq RETURN -1\n")); - return false; - } - } catch (std::exception & e) { - return false; - } - return true; - } - - /* - * It resets recvHdr. - * - * \exception TODO - * \return TODO - */ - inline bool done(bool sendready=false) { - if (sendready) { - static const char ready[] = "READY"; - for(int i = 0; i < peers; ++i) { - msg_t msg(ready, 6); - zmq::message_t to(const_cast(transportIDs[i].c_str()), transportIDs[i].length()+1,0,0); - try { - if (!socket->send(to,ZMQ_SNDMORE)) return false; - if (!socket->send(msg)) return false; - } catch (std::exception& e) { - return false; - } - } - } - recvHdr=true; - return true; - } - - /* - * It returns the number of partners of the single communication (NOTE: if P, peers is 1). - * - * \return The number of peers are returned. - */ - inline int getPeers() const { return peers;} - - const std::string name; - zmq::socket_t* socket; - zmqTransport * const transport; - const bool P; - const int peers; - bool recvHdr; - std::vector transportIDs; - -#if defined(CONNECTION_PROTOCOL) - - /* - * It sends hello to receiving node. - */ - inline void sendHelloRecvHi() { - const char hello[] = "HELLO"; - // sending Hello - zmqTDBG(printf("%s sending HELLO\n", name.c_str())); - zmqTransportMsg_t req(hello, 6); - send(req); - // receive Hi - zmqTransportMsg_t msg; - recvreply(msg); - zmqTDBG(printf("%s received %s\n", name.c_str(), static_cast(msg.getData()))); - } - - /* - * It receives a reply. - * - * \exception TODO - * \return TODO - */ - inline bool recvreply(msg_t& msg) { - try { - if(!socket->recv(&msg)) { - zmqTDBG(printf("recv RETURN -1\n")); - return false; - } - } catch (std::exception & e) { - return false; - } - return true; - } - - /* - * It sends a reply. - * - * \exception TODO - * \return TODO - */ - inline bool sendreply(const msg_t& msg) { - for(int i = 0; i < peers; ++i) { - msg_t _msg; - _msg.copy(const_cast(msg)); - zmq::message_t to(const_cast(transportIDs[i].c_str()), transportIDs[i].length()+1,0,0); - try { - if (!socket->send(to,ZMQ_SNDMORE)) return false; - if (!socket->send(_msg)) return false; - } catch (std::exception& e) { - return false; - } - } - return true; - } - - /* - * It receive hello to sening hi. - */ - inline void recvHelloSendHi() { - static const char hi[] = "HI"; // need to be static - // receives Hello and sends Hi - int useless; - for(int i = 0; i < peers; ++i) { - zmqTransportMsg_t req; - recv(req,useless); - zmqTDBG(printf("%s received %s\n",name.c_str(), static_cast(req.getData()))); - } - zmqTransportMsg_t rep(hi,3); - sendreply(rep); - zmqTDBG(printf("%s sending HI\n", name.c_str())); - } -#endif -}; // end descriptorN_1 - -//************************************** -// One to one communication -//************************************** - -/* - * \class zmq1_1 - * \ingroup streaming_network_simple_distributed_memory - * - * \brief This class provides ZeroMQ implementation of the 1 to 1 - * communication pattern. - * - * This class is defined in zmqImpl.hpp - * - */ -class zmq1_1 { -public: - typedef zmqTransportMsg_t msg_t; /// a ØMQ message (extends zmq::message_t) - typedef zmqTransport TransportImpl; /// to the transpoort layer - typedef msg_t tosend_t; /// ut supra - typedef msg_t torecv_t; /// ut supra - typedef descriptor1_N descriptor; ///< descriptor used in Unicast, Bcast, Scatter, - ///< On-Demand, OneToMany - - enum {MULTIPUT = 0}; - - /* - * Constructor - */ - zmq1_1():desc(NULL),active(false) {} - - /* - * Constructor (2) - * - * \param D is a pointer to a 1_N descriptor object. - */ - zmq1_1(descriptor* D):desc(D),active(false) {} - - /* - * It sets the descriptor. - * - * \param D is the descriptor. - */ - inline void setDescriptor(descriptor* D) { - if (desc) desc->close(); - desc = D; - } - - /* - * It gets the descriptor. - * - * \return It returns the descriptor. - */ - inline descriptor* getDescriptor() { return desc; } - - /* - * It initializes the communication pattern. - * - * \return TODO - */ - inline bool init(const std::string& address,const int nodeId=-1) { - if (active) return false; - // we force 0 to be the nodeId for the consumer - if(!desc->init(address,(desc->P)?nodeId:0)) active = true; - return active; - } - - /* - * It sends one message. - * - * \param msg is a reference to the message to be sent. - * - * \return TODO - */ - inline bool put(const tosend_t& msg) { return desc->send(msg, 0, 0); } - - /* - * It sends other parts of a multi-part message. - * - * \param msg is a reference to the message to be sent. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg) { return desc->sendmore(msg,0,ZMQ_SNDMORE);} - - /* - * TODO - * - * \param msg TODO - * \parm x TODO - * - * \return TODO - */ - inline bool put(const msg_t& msg, const int) { return desc->send(msg, 0, 0); } - - /* - * TODO - * - * \parm msg TODO - * \parm x TODO - * - * \return TODO - */ - inline bool putmore(const msg_t& msg, const int) { return desc->send(msg, 0, ZMQ_SNDMORE); } - - /* - * It receives the message header (should be called before get). - * - * \parm msg TODO - * \parm peer TODO - * - * \return TODO - */ - inline bool gethdr(torecv_t& msg, int& peer) { peer=0; return desc->recvhdr(msg); } - - /* - * It returns the number of disctint messages from one single - * communication. - * - * \parm msg TODO - * - * \return TODO - */ - inline bool get(torecv_t& msg, int=0) { return desc->recv(msg); } - - /* - * It put the node in waiting state. - * - * \return TODO - */ - inline int getToWait() const { return 1;} - - /* - * It put the node to performing state. - * - * \return TODO - */ - inline int putToPerform() const { return 1;} - - /* - * The communicaiton is finishe. - */ - inline void done() { } - - /* - * It closes the communication pattern. - * - * \return TODO - */ - inline bool close() { - if (!active) return false; - if (!desc->close()) return false; - active = false; - return true; - } - -protected: - descriptor* desc; - bool active; -}; - -//************************************** -// Broadcast communication -//************************************** - -/* - * \class zmqBcast - * \ingroup streaming_network_simple_distributed_memory - * - * \brief It implements the broadcast communication pattern of - * FastFlow in ZeroMQ. - * - * This class is defined in \ref zmqImpl.hpp - */ -class zmqBcast { -public: - typedef zmqTransportMsg_t msg_t; /// a ØMQ message (extends zmq::message_t) - typedef zmqTransport TransportImpl; /// to the transpoort layer - typedef msg_t tosend_t; /// ut supra - typedef msg_t torecv_t; /// ut supra - typedef descriptor1_N descriptor; ///< descriptor used in Unicast, Bcast, Scatter, - ///< On-Demand, OneToMany - - enum {MULTIPUT = 0}; - - /* - * Constructor - */ - zmqBcast():desc(NULL),active(false) {} - - /* - * Constructor (2) - * - * \param D pointer to a 1_N descriptor object. - */ - zmqBcast(descriptor* D):desc(D),active(false) {} - - /* - * It sets the descriptor. - * - * \parm D is the descriptor. - */ - inline void setDescriptor(descriptor* D) { - if (desc) desc->close(); - desc = D; - } - - /* - * It returns the descriptor. - * - * \return It returns the descriptor. - */ - inline descriptor* getDescriptor() { return desc; } - - /* - * It initializes the communication pattern. - * - * \param addr is the IP address in the form 'ip/host:port' - * \param nodeId is the node identifier in the range [0..inf[ - * - * \return TODO - */ - inline bool init(const std::string& address,const int nodeId=-1) { - if (active) return false; - if(!desc->init(address,nodeId)) active = true; - return active; - } - - /* - * It sends one message. - * - * \param msg is a reference to the message to be sent. - * - * \return TODO - */ - inline bool put(const tosend_t& msg) { return desc->send(msg, 0); } - - /* - * It sends other parts of a multi-part message. - * - * \param msg is a reference to the message to be sent. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg) { return desc->sendmore(msg,ZMQ_SNDMORE);} - - /* - * It sens the message. - * - * \parm msg is the reference to the message. - * \parm to is the address of the node where the message should be sent. - * - * \return TODO - */ - inline bool put(const msg_t& msg, const int to) { - return desc->send(msg, to, 0); - } - - /* - * It sen the message to more nodes. - * - * \parm msg is the reference to the message. - * \parm to is the address of the node where the message should be sent. - * - * \return TODO - */ - inline bool putmore(const msg_t& msg, const int to) { - return desc->sendmore(msg,to,ZMQ_SNDMORE); - } - - /* - * It receives the message header (should be called before get). - * - * \parm msg is the reference to the message. - * \parm peer is the number of peers. - * - * \return TODO - */ - inline bool gethdr(torecv_t& msg, int& peer) { peer=0; return desc->recvhdr(msg); } - - /* - * It receives one message. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool get(torecv_t& msg, int=0) { return desc->recv(msg); } - - /* - * It puts the node in the waiting state. - * - * \return TODO - */ - inline int getToWait() const { return 1;} - - /* - * It puts the node to peforming state. - * - * \return TODO - */ - inline int putToPerform() const { return 1;} - - /* - * It closes the communication pattern. - * - * \return TODO - */ - inline bool close() { - if (!active) return false; - if (!desc->close()) return false; - active=false; - return true; - } - - /* - * It finishes the communication. - */ - inline void done() {} - -protected: - descriptor* desc; - bool active; -}; - - -//************************************** -// Communication Transport interface -// -// ZeroMQ implementation of the ALL_GATHER communication patter -//************************************** - -/* - * \class zmqAllGather - * \ingroup streaming_network_simple_distributed_memory - * - * \brief It provides implementation of the ALL_GATHER communication pattern. - * - * This class is defined in \ref zmqImpl.hpp - * - */ -class zmqAllGather { -public: - typedef zmqTransportMsg_t msg_t; - typedef zmqTransport TransportImpl; - typedef msg_t tosend_t; - typedef svector torecv_t; - typedef descriptorN_1 descriptor; - - enum {MULTIPUT = 0}; - - /* - * Constructor (1) - */ - zmqAllGather():desc(NULL),active(false) {} - - /* - * Constructor (2) - * - * \parm D is the descriptor. - */ - zmqAllGather(descriptor* D):desc(D),active(false) {} - - /* - * It sets the descriptor. - */ - inline void setDescriptor(descriptor* D) { - if (desc) desc->close(); - desc = D; - } - - /* - * It returns the descriptor. - * - * \return It returns the descriptor. - */ - inline descriptor* getDescriptor() { return desc; } - - /* - * It initializes communication pattern. - * - * \return TODO - */ - inline bool init(const std::string& address,const int nodeId=-1) { - if (active) return false; - if(!desc->init(address,nodeId)) active = true; - if (!desc->P) done(); - return active; - } - - /* - * It sends one message. - * - * \parm msg is the reference of the message. - * - * \return TODO - */ - inline bool put(const tosend_t& msg) { - if (!desc->recvreq()) return false; - return desc->send(msg, 0); - } - - /* - * It puts more messages in the sening queue. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg) { return desc->send(msg,ZMQ_SNDMORE);} - - /* - * It puts single message. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool put(const msg_t& msg, const int) { - return put(msg); - } - - /* - * It put more messages. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg, const int) { - return putmore(msg); - } - - /* - * It receives the message header ONLY from one peer (should be called before get). - * - * \parm msg is the reference to the message. - * \param peer is the number of peers. - * - * \return TODO - */ - inline bool gethdr(msg_t& msg, int& peer) { - return desc->recvhdr(msg, peer); - } - - /* - * It receives one message ONLY from one peer (gethdr should be called before). - * - * \param msg is the reference to the message. - * \param peer is the number of peers. - * - * \return TODO - */ - inline bool get(msg_t& msg, int& peer) { - return desc->recv(msg,peer); - } - - /* - * It receives one message. - * - * \return TODO - */ - inline bool get(torecv_t& msg) { - const int peers = desc->getPeers(); - int useless; - msg.resize(peers); - for(int i = 0; i < peers; ++i) - if (!desc->recv(msg[i], useless)) return false; - done(); - return true; - } - - /* - * It returns the number of distinct messages for one single communication. - * - * \return TODO - */ - inline int getToWait() const { return desc->getPeers();} - - /* - * It puts the node in performing state. - * - * \return TODO - */ - inline int putToPerform() const { return 1;} - - /* - * It finishes the communication process. - */ - inline void done() { desc->done(true); } - - /* - * It closes communication pattern. - * - * \return TODO - */ - inline bool close() { - if (!active) return false; - if (desc->P) if (!desc->recvreq()) return false; - if (!desc->close()) return false; - active=false; - return true; - } - -protected: - descriptor* desc; - bool active; -}; - -//************************************** -// FromAny communication pattern -//************************************** - -/* - * \class zmqFromAny - * \ingroup streaming_network_simple_distributed_memory - * - * \brief It provides implementation of the collect from ANY communication - * pattern. - * - * This class is defined in \ref zmqImpl.hpp - * - */ -class zmqFromAny { -public: - typedef zmqTransportMsg_t msg_t; - typedef zmqTransport TransportImpl; - typedef msg_t tosend_t; - typedef msg_t torecv_t; - typedef descriptorN_1 descriptor; - - enum {MULTIPUT = 0}; - - zmqFromAny():desc(NULL),active(false) {} - zmqFromAny(descriptor* D):desc(D),active(false) {} - - /* - * It sets the descriptor. - * - * \parm D is the descriptor. - */ - inline void setDescriptor(descriptor* D) { - if (desc) desc->close(); - desc = D; - } - - /* - * It returns the descriptor. - * - * \return TODO - */ - inline descriptor* getDescriptor() { return desc; } - - /* - * It initializes communication pattern. - * - * \parm address - * \param nodeID - * - * \return TODO - */ - inline bool init(const std::string& address,const int nodeId=-1) { - if (active) return false; - if(!desc->init(address,nodeId)) active = true; - return active; - } - - /* - * It sends one message. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool put(const tosend_t& msg) { - return desc->send(msg, 0); - } - - /* - * It put more messages. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg) { - return desc->send(msg,ZMQ_SNDMORE); - } - - /* - * It places a message in the node. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool put(const msg_t& msg, const int) { - return desc->send(msg, 0); - } - - /* - * It places more messages in the node. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg, const int) { - return desc->send(msg,ZMQ_SNDMORE); - } - - /* - * It receives the message header (should be called before get). - * - * \parm msg is the reference to the message. - * \parm peer is the number of peers. - * - * \return TODO - */ - inline bool gethdr(msg_t& msg, int& peer) { - return desc->recvhdr(msg,peer); - } - - /* - * It receives one message. - * - * \parm msg is the reference to the message. - * \parm peer is the number of peers. - * - * \return TODO - */ - inline bool get(msg_t& msg, int& peer) { - return desc->recv(msg,peer); - } - - /* - * It receives one message. - * - * \param msg is the reference to the message. - * - * \return TODO - */ - inline bool get(msg_t& msg) { - int useless = 0; - return desc->recv(msg,useless); - } - - /* - * It places the node in the waiting state. - * - * \return TODO - */ - inline int getToWait() const { return 1;} - - /* - * It places the node in the waiting state. - * - * \return TODO - */ - inline int putToPerform() const { return 1;} - - /* - * It performs the completion operation. - */ - inline void done() { desc->done(); } - - /* - * It closes communication pattern. - * - * \return TODO - */ - inline bool close() { - if (!active) return false; - if (!desc->close()) return false; - active = false; - return true; - } - -protected: - descriptor* desc; - bool active; -}; - -//************************************* -// Scatter communication pattern -//************************************* - -/* - * \class zmqScatter - * \ingroup streaming_network_simple_distributed_memory - * - * \brief It provides implementation of the SCATTER communication pattern. - * - * This class is defined in \ref zmqImpl.hpp - * - */ -class zmqScatter { -public: - typedef zmqTransportMsg_t msg_t; - typedef zmqTransport TransportImpl; - typedef svector tosend_t; - typedef msg_t torecv_t; - typedef descriptor1_N descriptor; - enum {MULTIPUT = 1}; - - /* - * Constructor (1) - * - * It creates an empty scatter. - */ - zmqScatter():desc(NULL),active(false) {} - - /* - * Constructor (2) - * - * It creates a scatter with a scriptor. - * - * \parm D is the descriptor. - */ - zmqScatter(descriptor* D):desc(D),active(false) {} - - /* - * It sets the descriptor. - * - * \parm D is the descriptor. - */ - inline void setDescriptor(descriptor* D) { - if (desc) desc->close(); - desc = D; - } - - /* - * It returns the descriptor. - * - * \return It returns the descriptor. - */ - inline descriptor* getDescriptor() { return desc; } - - /* - * It initializes communication pattern. - * - * \parm address TOOD - * \parm nodeId TODO - * - * \return TODO - */ - inline bool init(const std::string& address,const int nodeId=-1) { - if (active) return false; - if(!desc->init(address,nodeId)) active = true; - return active; - } - - /* - * It sends one message. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool put(const tosend_t& msg) { - const int peers = desc->getPeers(); - assert(msg.size()==(size_t)peers); - for(int i = 0; i < peers; ++i) - if (!desc->send(msg[i], i, 0)) return false; - return true; - } - - /* - * It places more messages. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg) { - return -1; - } - - /* - * It places message. - * - * \parm msg is the reference to the message. - * \parm to is the address of the receiving node. - * - * \return TODO - */ - inline bool put(const msg_t& msg, const int to) { - return desc->send(msg, to, 0); - } - - /* - * It sends a message. - * - * \parm msg is the reference to message - * - * \return A negative value is returned. - */ - inline bool put(const msg_t& msg) { - return -1; - } - - /* - * It places many messages. - * - * \parm msg is the reference to the message. - * \parm to is the address of the receiving node. - * - * \return TODO - */ - inline bool putmore(const msg_t& msg, const int to) { - return desc->sendmore(msg,to,ZMQ_SNDMORE); - } - - /* - * It places more messages to the node. - * - * \parm msg is the reference to the message. - * - * \return A negative value is returned. - */ - inline bool putmore(const msg_t& msg) { - return -1; - } - - /* - * It receives the message header (should be called before get). - * - * \parm msg is the reference to the message. - * \parm peer is the number of peers. - * - * \return TODO - */ - inline bool gethdr(torecv_t& msg, int& peer) { peer=0; return desc->recvhdr(msg); } - - /* - * It receives one message. - * - * \return TODO - */ - inline bool get(torecv_t& msg, int=0) { return desc->recv(msg); } - - /* - * It returns the number of distinct messages for one single communication. - * - * \return 1 is always returned - */ - inline int getToWait() const { return 1;} - - /* - * \return TODO - */ - inline int putToPerform() const { return desc->getPeers();} - - /* - * It closes communication pattern - * - * \return TODO - */ - inline bool close() { - if (!active) return false; - if (!desc->close()) return false; - active = false; - return true; - } - - /* - * The communication finished. - */ - inline void done() {} - -protected: - descriptor* desc; - bool active; -}; - -//************************************* -// OnDemand communication transport -//************************************* - -/* - * \class zmqOnDemand - * \ingroup streaming_network_simple_distributed_memory - * - * \brief ZeroMQ implementation of the ON-DEMAND communication pattern. - * - * This class is defined in \ref zmqImpl.hpp - * - */ -class zmqOnDemand { -protected: - - /* - * It defines the sending request. - * - * \return TODO - */ - inline bool sendReq() { - static const char ready[] = "R"; - msg_t request(ready,2); - if (!desc->sendReq(request)) return false; - requestSent = true; - return true; - } - -public: - typedef zmqTransportMsg_t msg_t; - typedef zmqTransport TransportImpl; - typedef msg_t tosend_t; - typedef msg_t torecv_t; - typedef descriptor1_N descriptor; - enum {MULTIPUT = 0}; - - /* - * Constructur (1) - * - */ - zmqOnDemand():desc(NULL),active(false),to(-1),requestsToWait(0),requestSent(false) {} - - /* - * Constructor (2) - * - */ - zmqOnDemand(descriptor* D):desc(D),active(false),to(-1),requestsToWait(0),requestSent(false) {} - - /* - * \p It sets the descriptor. - * - * \parm D is the descriptor. - */ - inline void setDescriptor(descriptor* D) { - if (desc) desc->close(); - desc = D; - } - - /* - * It returns the descriptor. - * - * \return TODO - */ - inline descriptor* getDescriptor() { return desc; } - - /* - * It initializes communication pattern. - * - * \parm address if the address of the node. - * \parm nodeID is the addrecess of the receiving node. - * - * \return TODO - */ - inline bool init(const std::string& address,const int nodeId=-1) { - if (active) return false; - if(!desc->init(address,nodeId)) { - if (!desc->P) if (!sendReq()) return false; - active = true; - } - - To.resize(desc->getPeers()); - for(int i = 0; i < desc->getPeers(); ++i) To[i] = false; - - return active; - } - - /* - * It sends one message. - * - * \parm msg is the reference of the message. - * - * \return TODO - */ - inline bool put(const tosend_t& msg) { - // FIX: It would be better to use a non-blocking calls to receive requests, and - // in case recvReq returns false the msg should be inserted into a local queue. - // It is required an extra threads for handling incoming requests..... - if (to<0) if (!desc->recvReq(to)) return false; - int r = desc->send(msg, to, 0); - to=-1; - return r; - } - - /* - * It sends one message, more messages have to come. - * - * \parm msg is the reference of the message. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg) { - // see FIX comment above - if (to<0) if (!desc->recvReq(to)) return false; - return desc->sendmore(msg, to, ZMQ_SNDMORE); - } - - /* - * It places a message. - * - * \parm msg is the reference of the message. - * \parm dest is the destination node. - * \parm flag TODO - * - * \return TODO - */ - inline bool put(const tosend_t& msg, const int dest, int flag=0) { - if (dest<0) { // sends the same message to all peers, usefull for termination - for(int i=0;igetPeers();++i) { - to = i; - tosend_t _msg; - _msg.copy(const_cast(msg)); - if (!desc->send(_msg, to, flag)) { to=-1; return false;} - ++requestsToWait; - } - return true; - } - if (To[dest]) { - if (!desc->send(msg, dest, flag)) { to=-1; return false;} - To[dest]=false; - return true; - } - do { - if (!desc->recvReq(to)) { to=-1; return false;} - if (to == dest) { - if (!desc->send(msg, to, flag)) { to=-1; return false;} - return true; - } - assert(To[to]==false); - To[to]=true; - } while(1); - - // not reached - return false; - } - - /* - * It places many messages. - * - * \parm msg is the reference of the message. - * \parm dest is the address of the destination. - * - * \return TODO - */ - inline bool putmore(const tosend_t& msg, const int dest) { - return put(msg,dest,ZMQ_SNDMORE); - } - - /* - * It receives the message header (should be called before get). - * - * \parm msg is the reference of the message. - * \parm peer is the number of peers. - * - * \return TODO - */ - inline bool gethdr(torecv_t& msg, int& peer) { - peer=0; - bool r = desc->recvhdr(msg); - if (r) sendReq(); - return r; - } - - /* - * It receives one message part. - * - * \parm msg is the reference of the message. - * - * \return TODO - */ - inline bool get(torecv_t& msg) { - bool r=desc->recv(msg); - if (!requestSent && r) sendReq(); - return r; - } - /* - * It receives one message part. - * - * \parm msg is the reference of the message. - * \parm peer is the number of peer. - * - * \return TODO - * - */ - inline bool get(torecv_t& msg,int& peer) { - peer=0; - return get(msg); - } - - /* - * It closes communication pattern. - * - * \return TODO - */ - inline bool close() { - if (!active) return false; - if (desc->P) { - int useless; - requestsToWait+=desc->getPeers(); - for(int i=0;igetPeers();++i) - if (To[i]) --requestsToWait; - for(int i=0;irecvReq(useless)) return false; - } - } - if (!desc->close()) return false; - active=false; - return true; - } - - /* - * It returns the number of distinct messages for one single communication. - * - * \return 1 is always returned. - */ - inline int getToWait() const { return 1;} - - /* - * It completes the communication. - */ - inline void done() { requestSent=false; } - -protected: - descriptor* desc; - bool active; - int to; - int requestsToWait; - bool requestSent; - svector To; -}; - -//************************************* -// One to many communication -//************************************* - -/* - * \class zmq1_N - * \ingroup streaming_network_simple_distributed_memory - * - * \brief It provides implementation of the ONE_TO_MANY communication pattern. - * - * This pattern can be used to dynamically change among UNICAST, BROADCAST and - * SCATTER patterns. - * - * This class is define in \ref zmqImpl.hpp - */ -class zmq1_N { -public: - typedef zmqTransportMsg_t msg_t; - typedef svector tosend_t; - typedef msg_t torecv_t; - typedef descriptor1_N descriptor; - - enum {MULTIPUT=0 }; - - zmq1_N():desc(NULL),active(false) {} - zmq1_N(descriptor* D):desc(D),active(false) {} - - /* - * It sets the descriptor. - * - * \parm D is the descriptor. - */ - inline void setDescriptor(descriptor* D) { - if (desc) desc->close(); - desc = D; - } - - /* - * It returns the descriptor. - * - * \return Descriptor of the file is returned. - */ - inline descriptor* getDescriptor() { return desc; } - - /* - * It initializes communication pattern. - * - * \parm address if the IP address - * \parm nodeId is the address of the node - * - * \return TODO - */ - inline bool init(const std::string& address,const int nodeId=-1) { - if (active) return false; - if(!desc->init(address,nodeId)) active = true; - return active; - } - - /* - * It sends one message. - * - * \parm msg is the reference to the message. - * - * \return TODO - */ - inline bool put(const tosend_t& msg) { - const int peers=desc->getPeers(); - assert(msg.size()==(size_t)peers); - for(int i=0;isend(msg[i], i, 0)) return false; - return true; - } - - /* - * It sends one message. - * - * \parm msgs is the reference to the message. - * \parm to is the address of the receiving node. - * - * \return TODO - * - */ - inline bool put(const msg_t& msg, const int to) { - return desc->send(msg, to, to); - } - - /* - * It sends message to many nodes. - * - * \parm msg is the reference to the message. - * \parm to is the address of the receiving node. - * - * \return TODO - */ - inline bool putmore(const msg_t& msg, const int to) { - return desc->sendmore(msg,to,ZMQ_SNDMORE); - } - - /* - * It receives the message header (should be called before get). - * - * \parm msg is the reference to the message. - * \parm peer is the number of peer. - * - * \return TODO - */ - inline bool gethdr(torecv_t& msg, int& peer) { peer=0; return desc->recvhdr(msg); } - - /* - * It receives one message. - * - * \parm msg is the reference to the message - * - * \return TODO - */ - inline bool get(torecv_t& msg, int=0) { return desc->recv(msg); } - - /* - * It closes communication pattern. - * - * \return TODO - */ - inline bool close() { - if (!active) return false; - if (!desc->close()) return false; - active=false; - return true; - } - - /* - * It finishes the communicaiton. - */ - inline void done() {} - -protected: - descriptor* desc; - bool active; -}; - -/* - * - * @} - * \endlink - */ -} -#endif /* FF_zmqIMPL_HPP */ diff --git a/ff/d/zmqTransport.hpp b/ff/d/zmqTransport.hpp deleted file mode 100644 index fc59caa2..00000000 --- a/ff/d/zmqTransport.hpp +++ /dev/null @@ -1,297 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ - -/* *************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - **************************************************************************** - */ - - /* - * \file zmqTransport.hpp - * \ingroup building_blocks - * - * \brief This file provides the definition of the external transport layer - * based on ØMQ. - * - */ - -#ifndef FF_ZMQTRANSPORT_HPP -#define FF_ZMQTRANSPORT_HPP - -/* - * The manual of 0MQ can be found at : http://zguide.zeromq.org/ - * You MUST NOT share ØMQ sockets between threads. ØMQ sockets are not - * threadsafe. Technically it's possible to do this, but it demands semaphores, - * locks, or mutexes. This will make your application slow and fragile. The - * only place where it's remotely sane to share sockets between threads are in - * language bindings that need to do magic like garbage collection on - * sockets. - */ - -#include -#include -#include - -#include "zmq.hpp" - -namespace ff { - - -/* - * \class zmqTransportMsg_t - * \ingroup streaming_network_simple_distributed_memory - * - * \brief It describes the sructure of 0MQ transport message. - * - * This class describes the layout of a message used for inter-thread - * communications, inter-process communications, TCP/IP and multicast sockets - * in a distrubuted system running on the FastFlow framework. - * - * This class is defined in \link zmqTransport.hpp - * - */ - -class zmqTransportMsg_t: public zmq::message_t { -public: - /* - * It provides the callback definition. - */ - typedef void (msg_callback_t) (void *, void *); - - /* - * \p HEADER_LENGTH is the enumberation of the number of bytes. - */ - enum {HEADER_LENGTH=4}; // n. of bytes - -public: - /* - * Constructor (1) - * - * It creates an empty ØMQ message. - */ - inline zmqTransportMsg_t() {} - - /* - * Constructor (2) - * - * It creates a structured ØMQ message. - * - * \param data has the content of the message. - * \param size is the amount of memory to be allocated for the message. - * \param cb is the callback function. - * \param arg has additional arguments. - */ - inline zmqTransportMsg_t( const void * data, size_t size, - msg_callback_t cb=0, void * arg=0 ) : - zmq::message_t( const_cast(data), size, cb, arg ) - { } - - /* - * It initialises the message object (It is like the constructor but it - * rebuilds the object from scratch using new values). - * - * \param data has the content of the message. - * \param size is the amount of memory to be allocated for the message. - * \param cb is the callback function. - * \param arg has additional arguments. - */ - inline void init(const void * data, size_t size, msg_callback_t cb=0, void * arg=0) { - zmq::message_t::rebuild( const_cast(data), size, cb, arg ); - } - - /* - * It copies the message. - * - * \param msg is the reference to the 0MQ transport message. - */ - inline void copy(zmq::message_t &msg) { - zmq::message_t::copy(&msg); - } - - /* - * It retrieves the content of the message object. - * - * \return The contents of the message object. - */ - inline void * getData() { - return zmq::message_t::data(); - } - - /* - * It retrieves the size in bytes of the content of the message object. - * - * \return The size of the contexts of the message in bytes. - */ - inline size_t size() { - return zmq::message_t::size(); - } -}; - -/* - * \class zmqTransport - * \ingroup streaming_network_simple_distributed_memory - * - * \brief This class describes the transport layer used in a distributed - * FastFlow environment. - * - * This class is defined in zmqTransport.hpp - */ - -class zmqTransport { -private: - /* - * \p NUM_IO_THREADS is the enumberation of the number of IO threads. - */ - enum {NUM_IO_THREADS=1}; - -protected: - - /* - * It closes all existing connections. - * - * \return It returns 0 to show the successful closing of connection. - */ - inline int closeConnections() { - /* WARNING: Instead of setting a longer period of some minutes, it - * would be better to implement a shoutdown protocol and to set the - * longer period to 0. - */ - int zero = 1000000; //milliseconds - for(unsigned i = 0; i < Socks.size(); ++i) { - if (Socks[i]) { - Socks[i]->setsockopt(ZMQ_LINGER, &zero, sizeof(zero)); - Socks[i]->close(); - delete Socks[i]; - Socks[i]=NULL; - } - } - return 0; - } - -public: - /* - * It defines zmq::socket_t. - */ - typedef zmq::socket_t endpoint_t; - - /* - * It defines zmqTransportMsg_t. - */ - typedef zmqTransportMsg_t msg_t; - - /* - * It constructs a transport connection. - * - * \param procId is the process (or thread) ID. - */ - zmqTransport(const int procId) : - procId(procId),context(NULL) { - }; - - /* - * It closes the transport connection. - */ - ~zmqTransport() { closeTransport(); } - - /* - * It initializes the transport layer: creates a new ØMQ context and - * cleans the list of active end-points. - * - * \return If successful 0, otherwise a negative value is returned. - */ - int initTransport() { - if (context) return -1; - context = new zmq::context_t(NUM_IO_THREADS); - if (!context) return -1; - if (Socks.size()>0) closeConnections(); - return 0; - } - - /* - * It closes the transport layer, close all connections to any active - * end-point and delete the existing context. - * - * \returns 0 is returned to show the successful closing of transport - * connection. - */ - int closeTransport() { - closeConnections(); - if (context) {delete context; context=NULL;} - return 0; - } - - /* - * It creates a new socket and pushes it into the active sockets list. - * - * \param P is a flag to specify the role of the socket: if \p false, the - * new socket acts as a \p ROUTER (allows routing of messages to specific - * connections); if \p true the new socket acts as a \p DEALER (used for - * fair-queuing on input and for performing load-balancing on output - * toward a pool of collections). - * - * \returns If successful a pointer \p s to the newly created endpoint is - * reutned otherwise NULL is returned. - */ - endpoint_t * newEndPoint(const bool P) { - endpoint_t * s = new endpoint_t(*context, (P ? ZMQ_DEALER : ZMQ_ROUTER)); - if (!s) return NULL; - Socks.push_back(s); - return s; - } - - /* - * It deletes the socket pointed by \p s. It removes the - * socket from the list of active sockets and destroys the socket. - * - * \param s is a pointer to the socket to be deleted. - * - * \returns 0 if successful; otherwise a negative value is returned. - */ - int deleteEndPoint(endpoint_t *s) { - if (s) { - std::deque::iterator it = std::find(Socks.begin(), Socks.end(), s); - if (it != Socks.end()) { - Socks.erase(it); - - // WARNING: see closeConnections. - int zero = 1000000; //milliseconds - s->setsockopt(ZMQ_LINGER, &zero, sizeof(zero)); - s->close(); - delete s; - return 0; - } - } - return -1; - } - - /* - * \p It retrieves the process (or thread) ID. - * - * \return the process (or thread) ID - * - */ - int getProcId() const { return procId;} - -protected: - const int procId; // Process (or thread) ID - zmq::context_t * context; /* A context encapsulates functionality - dealing with the initialisation and - termination of a ØMQ context. */ - std::deque Socks; // all active end-points (i.e. sockets) -}; - - -} // namespace -#endif /* FF_ZMQTRANSPORT_HPP */ diff --git a/ff/dc.hpp b/ff/dc.hpp index 6232ce1a..0e81b462 100644 --- a/ff/dc.hpp +++ b/ff/dc.hpp @@ -4,9 +4,11 @@ #define FF_DAC_MDF_HPP /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/dff.hpp b/ff/dff.hpp index 422733f2..472ad400 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -3,9 +3,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/dinout.hpp b/ff/dinout.hpp index fef622ec..5f1527ab 100644 --- a/ff/dinout.hpp +++ b/ff/dinout.hpp @@ -5,9 +5,11 @@ * of the base class \p ff_node, with features oriented to distributed systems. */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 467118af..1b7e93bd 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -1,3 +1,29 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ + +/* Authors: + * Nicolo' Tonci + * Massimo Torquati + */ + + #ifndef FF_DGROUP_H #define FF_DGROUP_H diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 9e7ce1ce..0902eaca 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -1,3 +1,27 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ +/* Authors: + * Nicolo' Tonci + * Massimo Torquati + */ + #ifndef FF_DGROUPS_H #define FF_DGROUPS_H diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 1317e280..713fe599 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -1,3 +1,27 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ +/* Authors: + * Nicolo' Tonci + * Massimo Torquati + */ + #ifndef FF_DRECEIVER_H #define FF_DRECEIVER_H diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index d02ba944..b601a4c5 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -1,3 +1,27 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ +/* Authors: + * Nicolo' Tonci + * Massimo Torquati + */ + #ifndef FF_DSENDER_H #define FF_DSENDER_H diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index e2dc7482..bdb42a06 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -1,3 +1,27 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ +/* Authors: + * Nicolo' Tonci + * Massimo Torquati + */ + #ifndef FF_NETWORK #define FF_NETWORK diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index f52edc23..249df883 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -1,3 +1,27 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ +/* Authors: + * Nicolo' Tonci + * Massimo Torquati + */ + #ifndef WRAPPER_H #define WRAPPER_H diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index f4e0ded5..bd8e981d 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -1,3 +1,26 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ +/* Author: + * Nicolo' Tonci + */ + #include #include #include diff --git a/ff/dnode.hpp b/ff/dnode.hpp index ce225454..58388b83 100644 --- a/ff/dnode.hpp +++ b/ff/dnode.hpp @@ -16,9 +16,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/dynlinkedlist.hpp b/ff/dynlinkedlist.hpp index b8275cf3..b834910c 100644 --- a/ff/dynlinkedlist.hpp +++ b/ff/dynlinkedlist.hpp @@ -16,9 +16,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/dynqueue.hpp b/ff/dynqueue.hpp index 667626f9..0235d904 100644 --- a/ff/dynqueue.hpp +++ b/ff/dynqueue.hpp @@ -25,9 +25,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/farm.hpp b/ff/farm.hpp index 6d5349e5..53839b99 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -31,9 +31,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/ff.hpp b/ff/ff.hpp index 4431f1ea..ba9260f9 100644 --- a/ff/ff.hpp +++ b/ff/ff.hpp @@ -3,9 +3,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/ff_queue.hpp b/ff/ff_queue.hpp index 1b2a1fee..2c0a5ef7 100644 --- a/ff/ff_queue.hpp +++ b/ff/ff_queue.hpp @@ -11,9 +11,11 @@ * */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/ff/fftree.hpp b/ff/fftree.hpp index 0d71b2b0..ce18432d 100644 --- a/ff/fftree.hpp +++ b/ff/fftree.hpp @@ -3,9 +3,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/graph_utils.hpp b/ff/graph_utils.hpp index 362089e2..b4177912 100644 --- a/ff/graph_utils.hpp +++ b/ff/graph_utils.hpp @@ -9,9 +9,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/gsearch.hpp b/ff/gsearch.hpp index 138f693b..376f2f93 100644 --- a/ff/gsearch.hpp +++ b/ff/gsearch.hpp @@ -11,9 +11,11 @@ #define FF_GSEARCH_HPP /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/gt.hpp b/ff/gt.hpp index c70b64be..548c2339 100644 --- a/ff/gt.hpp +++ b/ff/gt.hpp @@ -15,9 +15,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/ff/lb.hpp b/ff/lb.hpp index be715d64..b8c863b0 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -12,9 +12,11 @@ */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/ff/make_unique.hpp b/ff/make_unique.hpp index c44be4f8..4428ee01 100644 --- a/ff/make_unique.hpp +++ b/ff/make_unique.hpp @@ -1,3 +1,24 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ + + #ifndef FF_MAKEUNIQUE_HPP #define FF_MAKEUNIQUE_HPP diff --git a/ff/map.hpp b/ff/map.hpp index 8cef1164..ecfe1dba 100644 --- a/ff/map.hpp +++ b/ff/map.hpp @@ -11,9 +11,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT_t * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/mapCUDAManaged.hpp b/ff/mapCUDAManaged.hpp index 06f60955..57722910 100644 --- a/ff/mapCUDAManaged.hpp +++ b/ff/mapCUDAManaged.hpp @@ -15,9 +15,11 @@ #define _FF_MAPCUDAMANAGED_HPP_ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/mapper.hpp b/ff/mapper.hpp index bf7bf8d4..bcd847e5 100644 --- a/ff/mapper.hpp +++ b/ff/mapper.hpp @@ -13,9 +13,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/mapping_string.sh b/ff/mapping_string.sh index 82240d71..83dc8209 100755 --- a/ff/mapping_string.sh +++ b/ff/mapping_string.sh @@ -1,5 +1,23 @@ #!/bin/bash # +# +# +# FastFlow is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License version 3 as +# published by the Free Software Foundation. +# Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 +# or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# # Author: Massimo Torquati # # Requires: bash >=3.2, hwloc diff --git a/ff/mapping_utils.hpp b/ff/mapping_utils.hpp index 794ef313..7489e24a 100644 --- a/ff/mapping_utils.hpp +++ b/ff/mapping_utils.hpp @@ -12,9 +12,11 @@ */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/mdf.hpp b/ff/mdf.hpp index 3724b24a..ca48a2ed 100644 --- a/ff/mdf.hpp +++ b/ff/mdf.hpp @@ -11,9 +11,11 @@ #define FF_MDF_HPP /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/mpmc/MPMCqueues.hpp b/ff/mpmc/MPMCqueues.hpp index 0cef78bf..e457fba0 100644 --- a/ff/mpmc/MPMCqueues.hpp +++ b/ff/mpmc/MPMCqueues.hpp @@ -13,6 +13,25 @@ * \li uMPMC_Ptr_Queue unbounded MPMC queue by Massimo Torquati * \li uMPMC_Ptr_Queue unbounded MPMC queue by Massimo Torquati */ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ #ifndef FF_MPMCQUEUE_HPP #define FF_MPMCQUEUE_HPP diff --git a/ff/multinode.hpp b/ff/multinode.hpp index fc6217a8..5cdc91b3 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -16,9 +16,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/node.hpp b/ff/node.hpp index 97e01923..a008b3da 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -16,9 +16,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/ocl/clEnvironment.hpp b/ff/ocl/clEnvironment.hpp index d0adb2f4..a5dcaca8 100644 --- a/ff/ocl/clEnvironment.hpp +++ b/ff/ocl/clEnvironment.hpp @@ -10,29 +10,22 @@ * creates contexts, command queues etc. */ - /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this - * file does not by itself cause the resulting executable to be covered by - * the GNU General Public License. This exception does not however - * invalidate any other reasons why the executable file might be covered by - * the GNU General Public License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************** */ diff --git a/ff/oclallocator.hpp b/ff/oclallocator.hpp index 1e197adf..9174b10e 100644 --- a/ff/oclallocator.hpp +++ b/ff/oclallocator.hpp @@ -2,29 +2,23 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this file - * does not by itself cause the resulting executable to be covered by the GNU - * General Public License. This exception does not however invalidate any - * other reasons why the executable file might be covered by the GNU General - * Public License. - * - * **************************************************************************/ + **************************************************************************** + */ /* * Author: diff --git a/ff/oclnode.hpp b/ff/oclnode.hpp index 65abce1e..9d59ce7c 100644 --- a/ff/oclnode.hpp +++ b/ff/oclnode.hpp @@ -12,27 +12,20 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this file - * does not by itself cause the resulting executable to be covered by the GNU - * General Public License. This exception does not however invalidate any - * other reasons why the executable file might be covered by the GNU General - * Public License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************** */ diff --git a/ff/optimize.hpp b/ff/optimize.hpp index 4c5f48a2..b327bda5 100644 --- a/ff/optimize.hpp +++ b/ff/optimize.hpp @@ -16,9 +16,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/ordering_policies.hpp b/ff/ordering_policies.hpp index d26fff37..76bc984e 100644 --- a/ff/ordering_policies.hpp +++ b/ff/ordering_policies.hpp @@ -9,9 +9,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/parallel_for.hpp b/ff/parallel_for.hpp index fc0a1727..f895c515 100644 --- a/ff/parallel_for.hpp +++ b/ff/parallel_for.hpp @@ -11,9 +11,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/parallel_for_internals.hpp b/ff/parallel_for_internals.hpp index 37b53f04..0101accd 100644 --- a/ff/parallel_for_internals.hpp +++ b/ff/parallel_for_internals.hpp @@ -10,9 +10,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index b631d7eb..ca8ba906 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -9,9 +9,11 @@ * */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/ff/platforms/platform.h b/ff/platforms/platform.h index 3e1cabce..be76f0ee 100644 --- a/ff/platforms/platform.h +++ b/ff/platforms/platform.h @@ -1,8 +1,10 @@ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/ff/platforms/pthread_minport_windows.h b/ff/platforms/pthread_minport_windows.h index fdd7fed0..ae5d7beb 100644 --- a/ff/platforms/pthread_minport_windows.h +++ b/ff/platforms/pthread_minport_windows.h @@ -1,7 +1,9 @@ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/ff/poolEvolution.hpp b/ff/poolEvolution.hpp index 3d7198ca..d04048b5 100644 --- a/ff/poolEvolution.hpp +++ b/ff/poolEvolution.hpp @@ -11,9 +11,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/poolEvolutionCUDA.hpp b/ff/poolEvolutionCUDA.hpp index 79c527b6..1279db7a 100644 --- a/ff/poolEvolutionCUDA.hpp +++ b/ff/poolEvolutionCUDA.hpp @@ -10,9 +10,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/repara/baseKernelTask.hpp b/ff/repara/baseKernelTask.hpp index 513c703d..bc2bb2aa 100644 --- a/ff/repara/baseKernelTask.hpp +++ b/ff/repara/baseKernelTask.hpp @@ -1,29 +1,24 @@ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this - * file does not by itself cause the resulting executable to be covered by - * the GNU General Public License. This exception does not however - * invalidate any other reasons why the executable file might be covered by - * the GNU General Public License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************** */ + /* * Author: Massimo Torquati * diff --git a/ff/repara/rprkernels.hpp b/ff/repara/rprkernels.hpp index 5734405c..10abafaf 100644 --- a/ff/repara/rprkernels.hpp +++ b/ff/repara/rprkernels.hpp @@ -1,29 +1,24 @@ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this - * file does not by itself cause the resulting executable to be covered by - * the GNU General Public License. This exception does not however - * invalidate any other reasons why the executable file might be covered by - * the GNU General Public License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************** */ + /* * Authors: Massimo Torquati * Rafael Sotomayor diff --git a/ff/selector.hpp b/ff/selector.hpp index e39120a4..0a602625 100644 --- a/ff/selector.hpp +++ b/ff/selector.hpp @@ -11,9 +11,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/spin-lock.hpp b/ff/spin-lock.hpp index c532c20f..f9b85d16 100644 --- a/ff/spin-lock.hpp +++ b/ff/spin-lock.hpp @@ -14,9 +14,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/squeue.hpp b/ff/squeue.hpp index f8aa0dad..5fcc280b 100644 --- a/ff/squeue.hpp +++ b/ff/squeue.hpp @@ -15,9 +15,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/staticallocator.hpp b/ff/staticallocator.hpp index f8dcb081..1f4f233e 100644 --- a/ff/staticallocator.hpp +++ b/ff/staticallocator.hpp @@ -2,9 +2,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/staticlinkedlist.hpp b/ff/staticlinkedlist.hpp index 251054af..06c49aa2 100644 --- a/ff/staticlinkedlist.hpp +++ b/ff/staticlinkedlist.hpp @@ -10,9 +10,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/stencilReduce.hpp b/ff/stencilReduce.hpp index 2b8b540b..526ccdda 100644 --- a/ff/stencilReduce.hpp +++ b/ff/stencilReduce.hpp @@ -2,9 +2,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/stencilReduceCUDA.hpp b/ff/stencilReduceCUDA.hpp index 43283451..3a486a77 100644 --- a/ff/stencilReduceCUDA.hpp +++ b/ff/stencilReduceCUDA.hpp @@ -1,9 +1,11 @@ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/stencilReduceOCL.hpp b/ff/stencilReduceOCL.hpp index 0ddcc884..da062016 100644 --- a/ff/stencilReduceOCL.hpp +++ b/ff/stencilReduceOCL.hpp @@ -10,9 +10,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/stencilReduceOCL_macros.hpp b/ff/stencilReduceOCL_macros.hpp index 0ecfb90a..ec1bbdd6 100644 --- a/ff/stencilReduceOCL_macros.hpp +++ b/ff/stencilReduceOCL_macros.hpp @@ -1,3 +1,22 @@ +/* *************************************************************************** + * + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** + */ /* * stencilReduceOCL_macros.hpp * diff --git a/ff/svector.hpp b/ff/svector.hpp index 9bb97b03..a3cf9633 100644 --- a/ff/svector.hpp +++ b/ff/svector.hpp @@ -11,9 +11,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/task_internals.hpp b/ff/task_internals.hpp index 134f345e..13a51bfc 100644 --- a/ff/task_internals.hpp +++ b/ff/task_internals.hpp @@ -13,9 +13,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/taskf.hpp b/ff/taskf.hpp index 74da3f8d..adf21923 100644 --- a/ff/taskf.hpp +++ b/ff/taskf.hpp @@ -11,9 +11,11 @@ #define FF_TASKF_HPP /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/tpc/tpcEnvironment.hpp b/ff/tpc/tpcEnvironment.hpp index ac91a60d..d6598961 100644 --- a/ff/tpc/tpcEnvironment.hpp +++ b/ff/tpc/tpcEnvironment.hpp @@ -10,33 +10,27 @@ * creates contexts, command queues etc. */ - /* *************************************************************************** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this - * file does not by itself cause the resulting executable to be covered by - * the GNU General Public License. This exception does not however - * invalidate any other reasons why the executable file might be covered by - * the GNU General Public License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************** */ + /* * Massimo Torquati: torquati@di.unipi.it * diff --git a/ff/tpcallocator.hpp b/ff/tpcallocator.hpp index ab20a7f3..08b9d8eb 100644 --- a/ff/tpcallocator.hpp +++ b/ff/tpcallocator.hpp @@ -2,29 +2,23 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this file - * does not by itself cause the resulting executable to be covered by the GNU - * General Public License. This exception does not however invalidate any - * other reasons why the executable file might be covered by the GNU General - * Public License. - * - * **************************************************************************/ + **************************************************************************** + */ /* * Author: diff --git a/ff/tpcnode.hpp b/ff/tpcnode.hpp index 1ecfcbd9..ebad6b5a 100644 --- a/ff/tpcnode.hpp +++ b/ff/tpcnode.hpp @@ -12,31 +12,25 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * FastFlow is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * As a special exception, you may use this file as part of a free software - * library without restriction. Specifically, if other files instantiate - * templates or use macros or inline functions from this file, or you compile - * this file and link it with other files to produce an executable, this file - * does not by itself cause the resulting executable to be covered by the GNU - * General Public License. This exception does not however invalidate any - * other reasons why the executable file might be covered by the GNU General - * Public License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************** */ + /* Author: Massimo Torquati torquati@di.unipi.it * - September 2015 first version * diff --git a/ff/ubuffer.hpp b/ff/ubuffer.hpp index e9c97d08..303fc044 100644 --- a/ff/ubuffer.hpp +++ b/ff/ubuffer.hpp @@ -43,9 +43,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/utils.hpp b/ff/utils.hpp index 5f083b32..52dd5a44 100644 --- a/ff/utils.hpp +++ b/ff/utils.hpp @@ -9,9 +9,11 @@ /* *************************************************************************** * - * This program is free software; you can redistribute it and/or modify it + * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. + * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 + * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/ff/version.h b/ff/version.h index cff8c9f0..1162da86 100644 --- a/ff/version.h +++ b/ff/version.h @@ -3,7 +3,7 @@ #define FF_MAJOR_VERSION 3 #define FF_MINOR_VERSION 0 -#define FF_BETA_VERSION 0 -#define FF_VERSION "3.0.0" +#define FF_BETA_VERSION 1 +#define FF_VERSION "3.0.1" #endif /* FF_VERSION_HPP */ diff --git a/tests/d/CMakeLists.txt b/tests/d/CMakeLists.txt deleted file mode 100644 index 5ed2f30b..00000000 --- a/tests/d/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -include_directories( - ${PROJECT_SOURCE_DIR} - ${ZeroMQ_INCLUDE_DIRS} - ) - -include_directories( ${ZMQ_INCLUDE_DIRS} ) - -#link_directories(${ZMQ_LIBRARIES}) - - -set( DTESTS -allgather dmap2 lat11 pipe_farm2 test_dinout -broadcast farm lat11_v2 scatter test_gw -bw11 farm_farm ondemand test11_pipe test_marshal -dmap fromany pipe_farm test11_torus unicast -) - -foreach( t ${DTESTS} ) - add_executable( ${t} ${t}.cpp ) - target_link_libraries( ${t} ${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIBRARIES}) -endforeach( t ) - -#add_test( allgather01 ${CMAKE_CURRENT_BINARY_DIR}/allgather ciccio 1 0 localhost:10002 & ) -#add_test( allgather11 ${CMAKE_CURRENT_BINARY_DIR}/allgather ciccio 0 1 localhost:10002 ) diff --git a/tests/d/Makefile b/tests/d/Makefile deleted file mode 100644 index 7bfe70c0..00000000 --- a/tests/d/Makefile +++ /dev/null @@ -1,112 +0,0 @@ -# --------------------------------------------------------------------------- -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# As a special exception, you may use this file as part of a free software -# library without restriction. Specifically, if other files instantiate -# templates or use macros or inline functions from this file, or you compile -# this file and link it with other files to produce an executable, this -# file does not by itself cause the resulting executable to be covered by -# the GNU General Public License. This exception does not however -# invalidate any other reasons why the executable file might be covered by -# the GNU General Public License. -# -# --------------------------------------------------------------------------- - -ifndef ZMQ_PATH -$(error The ZMQ_PATH variable is not defined in your environment) -endif - -CC = gcc -CXX = g++ -#CC = icc -#CXX = icpc -LINK_OPT = -VERSION = -OPTIMIZE_FLAGS = -O3 -finline-functions -DEBUG #-DBLOCKING_MODE -CXXFLAGS = -DNO_CMAKE_CONFIG -Wall -std=c++11 -DEBUG -CFLAGS = -LDFLAGS = -L ${ZMQ_PATH}/lib -INCS = -I../.. -I${ZMQ_PATH}/include -LIBS = -lzmq -lpthread #-lrt -ARCH = -march=$(shell uname -m) -OS = $(shell uname) -ifeq ($(ARCH),-march=x86_64) - ARCH = -march=core2 -endif - -ifeq ($(strip $(OS)),Darwin) - ifeq ($(strip $(ARCH)),x86_64 ) - ARCH = -march=core2 - else - ARCH = -arch ppc - endif -endif - -INCLUDES = -I. $(INCS) -TARGET = unicast broadcast allgather fromany scatter ondemand test11_pipe test11_torus test_marshal test_dinout pipe_farm pipe_farm2 farm farm_farm dmap lat11 lat11_v2 bw11 - - -.PHONY: all clean cleanall distclean install uninstall -.SUFFIXES: .c .cpp .o - -%.d: %.cpp - set -e; $(CXX) -MM $(INCLUDES) $(CXXFLAGS) $< \ - | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ - [ -s $@ ] || rm -f $@ -%.d: %.c - set -e; $(CC) -MM $(INCLUDES) $(CFLAGS) $< \ - | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ - [ -s $@ ] || rm -f $@ -%.o: %.cpp - $(CXX) $(INCLUDES) $(CXXFLAGS) $(OPTIMIZE_FLAGS) -c -o $@ $< -%.o: %.c - $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< -%: %.o - $(CXX) $< -o $@ $(LDFLAGS) $(LIBS) - - -all: $(TARGET) - -test: all - @echo "*****************************************************" - @echo "* *" - @echo "* TODO: 'make test' is not being implemented. Sorry!*" - @echo "* *" - @echo "*****************************************************" - -unicast:unicast.o -broadcast:broadcast.o -allgather:allgather.o -fromany:fromany.o -scatter:scatter.o -ondemand:ondemand.o -test11_pipe:test11_pipe.o -test11_torus:test11_torus.o -test_marshal:test_marshal.o -test_dinout:test_dinout.o -pipe_farm:pipe_farm.o -pipe_farm2:pipe_farm2.o -farm:farm.o -farm_farm:farm_farm.o -dmap:dmap.o -lat11:lat11.o -lat11_v2:lat11_v2.o -bw11:bw11.o - -clean: - -rm -fr *.o *~ -cleanall: clean - -rm -fr $(TARGET) *.d - -include $(OBJS:.o=.d) diff --git a/tests/d/README b/tests/d/README deleted file mode 100644 index a2182575..00000000 --- a/tests/d/README +++ /dev/null @@ -1,214 +0,0 @@ -Compiling tests under Linux -=========================== - -Prerequisite: ZeroMQ (aka: 0mq, zmq). -^^^^^^^^^^^^ -Please install zeromq version 2.2.0 from www.zeromq.org. -Note that if you want to install newer versions, starting from -version 3.x.x you need also to download the zmq.hpp C++ file binding -from https://github.com/zeromq/cppzmq -Copy the zmq.hpp file in the /include> directory. -With the old stable version 2.2.0 the file zmq.hpp is already in the -tarball. - -When running the make command inside the tests/d directory you may -obtain something similar to the following: - -Makefile:27: *** The ZMQ_PATH variable is not defined in your environment. Stop. - -This means that you have to define the ZMQ_PATH env variable with -the zeromq install path. For example is zeromq is installed in the -default directory (/usr/local) the command which defines ZMQ_PATH -is either - export ZMQ_PATH=/usr/local for bash-like shells -or - setenv ZMQ_PATH /usr/local for csh-like shells. - -If the zmq install path is not in the linker search path you have to define -olso LD_LIBRARY_PATH before executing the program otherwise the linker -won't be able to find the libzmq shared library. -The command is: - export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" for bash-like shells -or - setenv LD_LIBRARY_PATH "$LD_LIBRARY_PATH:/usr/local/lib" for csh-like shells. - -In order to avoid to define the env variable each time you open a new shell, -you may either add the command at the end of the bashrc file -(actually the .bashrc or .cshrc file in your home directory) or to insert the path -in the ld.so.conf system file and then run (you need root privileges) -the command ldconfig to rebuild the linker cache (see man (8) ldconfig for more -info). - -******************************************************* -EXAMPLE RUN - -This section gives an example run of the tests in the distributed environment. - -localhost can be replaced by any host name in a network. - ------------------------------------------ -allgather.cpp - -Producer: -./allgather gather 1 0 localhost:8080 - -Consumer: -./allgather gather 0 0 localhost:8080 - -NOTE: This does not work ------------------------------------------ -broadcast.cpp - -Producer: -./broadcast bc 1 1 localhost:8080 - -Consumer: -./broadcast bc 0 0 localhost:8080 - ------------------------------------------ -bw11.cpp - -Producer: -./bw11 1 localhost:8080 64 - -Consumer: -./bw11 0 localhost:8080 64 - ------------------------------------------ -dmap.cpp - -Producer: -./dmap 10 10 1 1 localhost:8080 localhost:8081 - -Consumer: -./dmap 10 10 0 0 localhost:8080 localhost:8081 - ------------------------------------------ -farm.cpp - -Producer: -./farm 10 10 1 1 localhost:8080 localhost:8081 - -Consumer: -./farm 10 10 0 0 localhost:8080 localhost:8081 - ------------------------------------------ -farm_farm.cpp - -Producer: -./farm_farm 10 10 1 1 2 localhost:8080 localhost:8081 - -Consumer: -./farm_farm 10 10 0 0 2 localhost:8080 localhost:8081 - ------------------------------------------ -fromany.cpp - -Producer: -./fromany fromany 1 1 localhost:8080 - -Consumer: -./fromany fromany 0 0 localhost:8080 - -Note: The program stucks - ------------------------------------------ -lat11.cpp - -Producer: -./lat11 1 localhost:8080 localhost:8081 - -Consumer: -./lat11 0 localhost:8080 localhost:8081 - -Note: The program keeps waiting - ------------------------------------------ -lat11_v2.cpp - -Producer: -./lat11_v2 1 localhost:8080 localhost:8081 64 - -Consumer: -./lat11_v2 0 localhost:8080 localhost:8081 64 - -Note: Producer finishes, but consumer remain waiting - ------------------------------------------ -ondemand.cpp - -Producer: -./ondemand ondemand 1 1 localhost:8080 - -Consumer: -./ondemand ondemand 0 0 localhost:8080 - -Note: Consumer keeps waiting. However, if I start new consumer -./ondemand ondemand 0 1 localhost:8080 -then the first consumer finishes along with the producer, but the second -consumer keeps waiting. - ------------------------------------------ -pipe_farm.cpp - -Producer: -./pipe_farm 2 10 10 pipe 1 localhost:8080 - -Consumer: -./pipe_farm 2 10 10 pipe 0 localhost:8080 - ------------------------------------------ -scatter.cpp - -Producer: -./scatter scatter 1 1 localhost:8080 - -Consumer: -./scatter scatter 0 0 localhost:8080 - ------------------------------------------ -test11_pipe.cpp - -Producer: -./test11_pipe pipe 1 localhost:8080 - -Consumer: -./test11_pipe pipe 0 localhost:8080 - ------------------------------------------ -test11_torus.cpp - -Producer: -./test11_torus torus1 torus2 1 localhost:8080 localhost:8081 - -Consumer: -./test11_torus torus1 torus2 0 localhost:8080 localhost:8081 - ------------------------------------------ -test_dinout.cpp - -Producer: -./test_dinout test1 test2 1 localhost:8080 localhost:8081 - -Consumer: -./test_dinout test1 test2 0 localhost:8080 localhost:8081 - -NOTE: I do not see the output values as expected. - ------------------------------------------ -test_marshal.cpp - -Producer: -./test_marshal marshal 1 localhost:8080 - -Consumer: -./test_marshal marshal 0 localhost:8080 - ------------------------------------------ -unicast.cpp - -Producer: -./unicast unicast 1 localhost:8080 - -Consumer: -./unicast unicast 0 localhost:8080 diff --git a/tests/d/allgather.cpp b/tests/d/allgather.cpp deleted file mode 100644 index ff94385a..00000000 --- a/tests/d/allgather.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -using namespace ff; - -int main(int argc, char * argv[]) { - if (argc != 5) { - std::cerr << "use: " << argv[0] << " name 1|0 nhosts master-host:port\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - int n = atoi(argv[3]); // NOTE: It is the num peers for the consumer, and the node id for the producers - char * address = argv[4]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(n); - if (transport.initTransport()<0) abort(); - - ALLGATHER Gather(new ALLGATHER_DESC(name,(atoi(P)?1:n),&transport,atoi(P))); - if (!Gather.init(address)) abort(); - - if (atoi(P)) { - ALLGATHER::tosend_t msg; - for(int i=0;i<100;++i) { - msg.init(new int(i),sizeof(int)); - Gather.put(msg); - } - } else { - ALLGATHER::torecv_t msg; - for(int i=0;i<100;++i) { - Gather.get(msg); - assert(msg.size()==(size_t)n); - for(int j=0;j(msg[j].getData())); - } - } - - Gather.close(); - delete Gather.getDescriptor(); - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/broadcast.cpp b/tests/d/broadcast.cpp deleted file mode 100644 index 829229bd..00000000 --- a/tests/d/broadcast.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -using namespace ff; - -typedef zmqTransportMsg_t msg_t; - -int main(int argc, char * argv[]) { - if (argc != 5) { - std::cerr << "use: " << argv[0] << " name 1|0 nhosts master-host:port\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - int n = atoi(argv[3]); - char * address = argv[4]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(n); - if (transport.initTransport()<0) abort(); - - BROADCAST Broadcast(new BROADCAST_DESC(name,(atoi(P)?n:1),&transport,atoi(P))); - if (!Broadcast.init(address)) abort(); - - msg_t msg; - if (atoi(P)) { - for(int i=0;i<100;++i) { - msg.init(new int(i),sizeof(int)); - Broadcast.put(msg); - } - } else { - for(int i=0;i<100;++i) { - Broadcast.get(msg); - printf("received %d\n", *static_cast(msg.getData())); - } - } - - Broadcast.close(); - delete Broadcast.getDescriptor(); - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/bw11.cpp b/tests/d/bw11.cpp deleted file mode 100644 index 08c24ada..00000000 --- a/tests/d/bw11.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * - * host0 host1 - * --------- ---------- - * | | | | - * | | UNICAST | | - * | Node1 -|---------- |-> Node2 | - * | | | | - * --------- ---------- - * - * - * This simple test measures the bandwidith (Mb/s) of sending 'size' - * bytes from host0 to host1. - * - * ((COUNT/ Node2 computation time IN SECS)*size*8)/1000000 - * - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -const long int COUNT=1000; - -using namespace ff; - -#define COMM zmqOnDemand - -class Node1: public ff_dnode { -protected: - static void callback(void * e,void*) { - delete [] ((char*)e); - } -public: - Node1(const unsigned size, const std::string& name, const std::string& address, zmqTransport* const transp): - size(size),name(name), address(address), transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address, 1,transp,true, 0); //, callback); - printf("Node1 start\n"); - return 0; - } - void * svc(void*) { - char* data=new char[size]; - for(int i=0;i& v, void* ptr, const int sender=-1) { - struct iovec iov={ptr,size*sizeof(char)}; - v.push_back(iov); - setCallbackArg(NULL); - } -private: - const unsigned size; - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -class Node2: public ff_dnode { -public: - typedef zmqTransport::msg_t msg_t; - - Node2(const unsigned size, const std::string& name, const std::string& address, zmqTransport* const transp): - size(size),name(name),address(address),transp(transp) {} - - int svc_init() { - ff_dnode::init(name,address, 1, transp, false, 0); - printf("Node2 start\n"); - ff::ffTime(START_TIME); - return 0; - } - void * svc(void *task) { - COMM::TransportImpl::msg_t* msg=(COMM::TransportImpl::msg_t*)task; - assert(size == msg->size()); - delete msg; - return GO_ON; - } - void svc_end() { - ff::ffTime(STOP_TIME); - printf("Time = %f ms\n", ff::ffTime(GET_TIME)); - printf("Bandwidth = %.3f Mb/s\n", - (((double)(COUNT*1000) /(double)ff::ffTime(GET_TIME))*size*8)/1000000.0); - } - void unmarshalling(svector* const v[], const int vlen, void *& task) { - // returns the msg - task = v[0]->operator[](0); - delete v[0]; - } -private: - const unsigned size; - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -int main(int argc, char * argv[]) { - if (argc != 4) { - std::cerr << "use: " << argv[0] << " 1|0 host:port data-size(bytes)\n"; - return -1; - } - - char * P = argv[1]; // 1 producer 0 consumer - char * address1 = argv[2]; // no check, this is the address of the producer - unsigned size = atoi(argv[3]); - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - Node1* n1 = new Node1(size, "A", address1, &transport); - n1->run(); - n1->wait(); - delete n1; - } else { - Node2 * n2 = new Node2(size, "A", address1, &transport); - n2->run(); - n2->wait(); - delete n2; - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/dmap.cpp b/tests/d/dmap.cpp deleted file mode 100644 index 823799d3..00000000 --- a/tests/d/dmap.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/* - * ------------------------------------------------------------------- - * | host0 | - * | ---------------------- | - * | | | | - * | | | | - * | master ------|-> Worker --> Sender -|-- - * | -------------------------- | | | - * | | | | | (ff_pipeline) | - * v | | | ---------------------- - * ---|-> Collector --> Emitter -|--- C P - * ^ | | | SCATTER (COMM1) - * | | (ff_pipeline) | | - * | -------------------------- | host1 - * | C P | ---------------------- - * | ALLGATHER (COMM2) | | | - * | | | | - * | ------|-> Worker --> Sender -|-- - * | | | | - * | | (ff_pipeline) | | - * | ---------------------- | - * | C P | - * ------------------------------------------------------------------- - * - * COMM1, the server address is master:port1 (the address1 parameter) - * COMM2, the server address is master:port2 (the address2 parameter) - * - * Distributed map example. - * - */ - -#include -#ifdef __linux -#include -#endif -#include -#include -#include -#include -#include -#include - -// On win dnode.h should be included as first header to ensure winsock2.h comes before windows.h -// ZMQ requires winsock2.h and conflicts with windows.h -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; - -#define COMM1 zmqScatter -#define COMM2 zmqAllGather - -// gloabals just to save some coding -unsigned matSize=0; // this is the number of rows and columns of the matrix - -struct ff_task_t { - ff_task_t(double* t, unsigned numRows): - numRows(numRows),task(t) {} - unsigned numRows; - double* task; -}; - -// used to recycle memory -struct taskPtr_t { - ff_task_t* t; - void* ptr; -}; - -// the following SPSC unbounded queue is shared between the Worker and the Sender threads -// to free unused memory -uSWSR_Ptr_Buffer RECYCLE(1024); // NOTE: See also the farm_farm test for another solution to the same problem ! - - -class Collector: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -public: - Collector(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { - myt = new double[matSize*matSize]; // set maximum size - ff_dnode::init(name, address, nHosts, transp, RECEIVER,0); - ff::ffTime(START_TIME); - return 0; - } - - void *svc(void *task) { - if (task == NULL) { - srandom(0); //::getpid()+(getusec()%4999)); - for(unsigned i=0;i* const v[], const int vlen, void *& task) { - assert(vlen == (int)nHosts); - for(int i=0;isize() == 2); - ff_task_t* t = static_cast(v[i]->operator[](0)->getData()); - t->task = static_cast(v[0]->operator[](1)->getData()); - memcpy(myt+(i*t->numRows),t->task, t->numRows*matSize*sizeof(double)); - } - task=myt; - } - -private: - unsigned nTasks; - unsigned nHosts; - double * myt; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -class Emitter: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -protected: - static void callback(void * e,void* arg) { - taskPtr_t* t = (taskPtr_t*)arg; - if (!t) return; - delete t->t; - if ( --M[t->ptr] == 0) { - delete [] (double*)t->ptr; - } - delete t; - } -public: - Emitter(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, nHosts, transp, SENDER, 0, callback); - return 0; - } - - void * svc(void* task) { - if (--nTasks == 0) { - ff_send_out(task); - return NULL; // generates EOS - } - return task; - } - - // Since COMM1 is a scatter pattern, the prepare method will be called - // nHosts times, once for each message to send. - void prepare(svector& v, void* ptr, const int sender) { - assert(sender != -1); - unsigned start=0, numRows=0; - unsigned rowsize=matSize*sizeof(double); - - getPartition(sender,start,numRows); - ff_task_t* t = new ff_task_t((double*)ptr+(start*matSize),numRows+1); - - struct iovec iov={t, sizeof(ff_task_t)}; - v.push_back(iov); - setCallbackArg(NULL); - - iov.iov_base=t->task; - iov.iov_len =t->numRows*rowsize; - v.push_back(iov); - taskPtr_t* tptr = new taskPtr_t; - tptr->t=t; - tptr->ptr=ptr; - setCallbackArg(tptr); - - if (M.find(ptr) == M.end()) { - M[ptr]=nHosts; - } - } -private: - inline void getPartition(const int Id, unsigned& start, unsigned& size) { - int r = matSize / nHosts; - const int m = matSize % nHosts; - const int st = (Id * r) + ((m >= Id) ? Id : m); - if (Id < m) ++r; - start = st; size= r-1; - } -private: - unsigned nTasks; - unsigned nHosts; - static std::map M; - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; -std::map Emitter::M; - - -class Worker: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -public: - Worker(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - myt = new double[matSize*matSize]; // set maximum size - assert(myt); - - ff_dnode::init(name, address, 1, transp, RECEIVER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - ff_task_t* _t = (ff_task_t*)task; - double* t = _t->task; - const unsigned numRows=_t->numRows; - - //printf("Worker: get one task, numRows=%d\n", numRows); - memset(myt,0,matSize*matSize*sizeof(double)); - for(unsigned i=0;itask = t; - return _t; - } - void svc_end() { - delete [] myt; - } - - void prepare(svector*& v, size_t len, const int sender=-1) { - svector * v2 = new svector(len); - assert(v2); - for(size_t i=0;ipush_back(m); - RECYCLE.push(m); - } - v = v2; - } - - virtual void unmarshalling(svector* const v[], const int vlen, void *& task) { - assert(vlen==1 && v[0]->size()==2); - ff_task_t* t =static_cast(v[0]->operator[](0)->getData()); - t->task =static_cast(v[0]->operator[](1)->getData()); - task=t; - } - -private: - double * myt; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Sender: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -protected: - static void callback(void *e,void*) { - msg_t* p; - if (!RECYCLE.pop((void**)&p)) abort(); - - delete p; - } -public: - Sender(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, 1, transp, SENDER, transp->getProcId(),callback); - return 0; - } - - void* svc(void* task) { - //printf("Sender, sending the task, numRows=%d\n", ((ff_task_t*)task)->numRows); - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - ff_task_t* t = (ff_task_t*)ptr; - struct iovec iov={t, sizeof(ff_task_t)}; - v.push_back(iov); - setCallbackArg(NULL); - iov.iov_base= t->task; - iov.iov_len = t->numRows*matSize*sizeof(double); - v.push_back(iov); - setCallbackArg(NULL); - } -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -int main(int argc, char * argv[]) { - if (argc == 2 && (atoi(argv[1])<0)) { - printf( - " ------------------------------------------------------------------- \n" - " | host0 | \n" - " | ---------------------- | \n" - " | | | | \n" - " | | | | \n" - " | host-master ------|-> Worker --> Sender -|-- \n" - " | -------------------------- | | | \n" - " | | | | | (ff_pipeline) | \n" - " v | | | ---------------------- \n" - " ---|-> Collector --> Emitter -|--- C P \n" - " ^ | | | SCATTER \n" - " | | (ff_pipeline) | | (COMM1) \n" - " | -------------------------- | host1 \n" - " | C P | ---------------------- \n" - " | ALLGATHER | | | \n" - " | (COMM2) | | | \n" - " | ------|-> Worker --> Sender -|-- \n" - " | | | | \n" - " | | (ff_pipeline) | | \n" - " | ---------------------- | \n" - " | C P | \n" - " ------------------------------------------------------------------- \n" - " \n" - " COMM1, the server address is master:port1 (the address1 parameter) \n" - " COMM2, the server address is master:port2 (the address2 parameter) \n" - "\n"); - return 0; - } - if (argc != 7) { - std::cerr << "use: " << argv[0] << " matsize stream-length 1|0 nhosts masterhost:port1 masterhost:port2\n"; - std::cerr << " 1 for the master 0 for other hosts\n"; - std::cerr << " nhosts: is the number of hosts for the master and the hostID for other hosts\n"; - std::cerr << " masterhost: is the hostname or IP address of the master node.\n"; - - return -1; - } - - matSize = atoi(argv[1]); - unsigned numTasks=atoi(argv[2]); - char* P = argv[3]; // 1 for the master 0 for other hosts - unsigned nhosts = atoi(argv[4]); - char* address1 = argv[5]; // no check - char* address2 = argv[6]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)?-1 : nhosts); - if (transport.initTransport()<0) abort(); - - if (atoi(P)==1) { // master node - ff_pipeline pipe; - Collector C(numTasks, nhosts, "B", address2, &transport); - Emitter E(numTasks, nhosts, "A", address1, &transport); - - C.skipfirstpop(true); - - pipe.add_stage(&C); - pipe.add_stage(&E); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - printf("Master node Total Time= %.2f\n", C.ffTime()); - } else { // worker nodes - ff_pipeline pipe; - Worker W("A", address1, &transport); - Sender S("B", address2, &transport); - pipe.add_stage(&W); - pipe.add_stage(&S); - - if (!RECYCLE.init()) abort(); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/dmap2.cpp b/tests/d/dmap2.cpp deleted file mode 100644 index a9083cb1..00000000 --- a/tests/d/dmap2.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* - * ------------------------------------------------------------------- - * | host0 | - * | ---------------------- | - * | | | | - * | | | | - * | master ------|-> Worker --> Sender -|-- - * | -------------------------- | | | - * | | | | | (ff_pipeline) | - * v | | | ---------------------- - * ---|-> Collector --> Emitter -|--- C P - * ^ | | | SCATTER (COMM1) - * | | (ff_pipeline) | | - * | -------------------------- | host1 - * | C P | ---------------------- - * | ALLGATHER (COMM2) | | | - * | | | | - * | ------|-> Worker --> Sender -|-- - * | | | | - * | | (ff_pipeline) | | - * | ---------------------- | - * | C P | - * ------------------------------------------------------------------- - * - * COMM1, the server address is master:port1 (the address1 parameter) - * COMM2, the server address is master:port2 (the address2 parameter) - * - * Distributed map example. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; - -#define COMM1 zmqScatter -#define COMM2 zmqAllGather - -// gloabals just to save some coding -unsigned matSize=0; // this is the number of rows and columns of the matrix - -struct ff_task_t { - ff_task_t(double* t, unsigned numRows): - numRows(numRows),task(t) {} - unsigned numRows; - double* task; -}; - -// used to recycle memory -struct taskPtr_t { - ff_task_t* t; - void* ptr; -}; - -// the following SPSC unbounded queue is shared between the Worker and the Sender threads -// to free unused memory -uSWSR_Ptr_Buffer RECYCLE(1024); // NOTE: See also the farm_farm test for another solution to the same problem ! - - -class Collector: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -public: - Collector(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { - myt = new double[matSize*matSize]; // set maximum size - ff_dnode::init(name, address, nHosts, transp, RECEIVER,0); - ff::ffTime(START_TIME); - return 0; - } - - void *svc(void *task) { - if (task == NULL) { - srandom(0); //::getpid()+(getusec()%4999)); - for(unsigned i=0;i* const v[], const int vlen, void *& task) { - assert(vlen == (int)nHosts); - for(int i=0;isize() == 2); - ff_task_t* t = static_cast(v[i]->operator[](0)->getData()); - t->task = static_cast(v[0]->operator[](1)->getData()); - memcpy(myt+(i*t->numRows),t->task, t->numRows*matSize*sizeof(double)); - } - task=myt; - } - -private: - unsigned nTasks; - unsigned nHosts; - double * myt; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -class Emitter: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -protected: - static void callback(void * e,void* arg) { - taskPtr_t* t = (taskPtr_t*)arg; - if (!t) return; - delete t->t; - if ( --M[t->ptr] == 0) { - delete [] (double*)t->ptr; - } - delete t; - } -public: - Emitter(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, nHosts, transp, SENDER, 0, callback); - return 0; - } - - void * svc(void* task) { - if (--nTasks == 0) { - ff_send_out(task); - return NULL; // generates EOS - } - return task; - } - - // Since COMM1 is a scatter pattern, the prepare method will be called - // nHosts times, once for each message to send. - void prepare(svector& v, void* ptr, const int sender) { - assert(sender != -1); - unsigned start=0, numRows=0; - unsigned rowsize=matSize*sizeof(double); - - getPartition(sender,start,numRows); - ff_task_t* t = new ff_task_t((double*)ptr+(start*matSize),numRows+1); - - struct iovec iov={t, sizeof(ff_task_t)}; - v.push_back(iov); - setCallbackArg(NULL); - - iov.iov_base=t->task; - iov.iov_len =t->numRows*rowsize; - v.push_back(iov); - taskPtr_t* tptr = new taskPtr_t; - tptr->t=t; - tptr->ptr=ptr; - setCallbackArg(tptr); - - if (M.find(ptr) == M.end()) { - M[ptr]=nHosts; - } - } -private: - inline void getPartition(const int Id, unsigned& start, unsigned& size) { - int r = matSize / nHosts; - const int m = matSize % nHosts; - const int st = (Id * r) + ((m >= Id) ? Id : m); - if (Id < m) ++r; - start = st; size= r-1; - } -private: - unsigned nTasks; - unsigned nHosts; - static std::map M; - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; -std::map Emitter::M; - - -class Worker: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -public: - Worker(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - myt = new double[matSize*matSize]; // set maximum size - assert(myt); - - ff_dnode::init(name, address, 1, transp, RECEIVER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - ff_task_t* _t = (ff_task_t*)task; - double* t = _t->task; - const unsigned numRows=_t->numRows; - - //printf("Worker: get one task, numRows=%d\n", numRows); - bzero(myt,matSize*matSize*sizeof(double)); - for(unsigned i=0;itask = t; - return _t; - } - void svc_end() { - delete [] myt; - } - - void prepare(svector*& v, size_t len, const int sender=-1) { - svector * v2 = new svector(len); - assert(v2); - for(size_t i=0;ipush_back(m); - RECYCLE.push(m); - } - v = v2; - } - - virtual void unmarshalling(svector* const v[], const int vlen, void *& task) { - assert(vlen==1 && v[0]->size()==2); - ff_task_t* t =static_cast(v[0]->operator[](0)->getData()); - t->task =static_cast(v[0]->operator[](1)->getData()); - task=t; - } - -private: - double * myt; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Sender: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -protected: - static void callback(void *e,void*) { - msg_t* p; - if (!RECYCLE.pop((void**)&p)) abort(); - - delete p; - } -public: - Sender(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, 1, transp, SENDER, transp->getProcId(),callback); - return 0; - } - - void* svc(void* task) { - //printf("Sender, sending the task, numRows=%d\n", ((ff_task_t*)task)->numRows); - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - ff_task_t* t = (ff_task_t*)ptr; - struct iovec iov={t, sizeof(ff_task_t)}; - v.push_back(iov); - setCallbackArg(NULL); - iov.iov_base= t->task; - iov.iov_len = t->numRows*matSize*sizeof(double); - v.push_back(iov); - setCallbackArg(NULL); - } -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -int main(int argc, char * argv[]) { - if (argc == 2 && (atoi(argv[1])<0)) { - printf( - " ------------------------------------------------------------------- \n" - " | host0 | \n" - " | ---------------------- | \n" - " | | | | \n" - " | | | | \n" - " | host-master ------|-> Worker --> Sender -|-- \n" - " | -------------------------- | | | \n" - " | | | | | (ff_pipeline) | \n" - " v | | | ---------------------- \n" - " ---|-> Collector --> Emitter -|--- C P \n" - " ^ | | | SCATTER \n" - " | | (ff_pipeline) | | (COMM1) \n" - " | -------------------------- | host1 \n" - " | C P | ---------------------- \n" - " | ALLGATHER | | | \n" - " | (COMM2) | | | \n" - " | ------|-> Worker --> Sender -|-- \n" - " | | | | \n" - " | | (ff_pipeline) | | \n" - " | ---------------------- | \n" - " | C P | \n" - " ------------------------------------------------------------------- \n" - " \n" - " COMM1, the server address is master:port1 (the address1 parameter) \n" - " COMM2, the server address is master:port2 (the address2 parameter) \n" - "\n"); - return 0; - } - if (argc != 7) { - std::cerr << "use: " << argv[0] << " matsize stream-length 1|0 nhosts masterhost:port1 masterhost:port2\n"; - std::cerr << " 1 for the master 0 for other hosts\n"; - std::cerr << " nhosts: is the number of hosts for the master and the hostID for other hosts\n"; - std::cerr << " masterhost: is the hostname or IP address of the master node.\n"; - - return -1; - } - - matSize = atoi(argv[1]); - unsigned numTasks=atoi(argv[2]); - char* P = argv[3]; // 1 for the master 0 for other hosts - unsigned nhosts = atoi(argv[4]); - char* address1 = argv[5]; // no check - char* address2 = argv[6]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)?-1 : nhosts); - if (transport.initTransport()<0) abort(); - - if (atoi(P)==1) { // master node - ff_pipeline pipe; - Collector C(numTasks, nhosts, "B", address2, &transport); - Emitter E(numTasks, nhosts, "A", address1, &transport); - - C.skipfirstpop(true); - - pipe.add_stage(&C); - pipe.add_stage(&E); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - printf("Master node Total Time= %.2f\n", C.ffTime()); - } else { // worker nodes - ff_pipeline pipe; - Worker W("A", address1, &transport); - Sender S("B", address2, &transport); - pipe.add_stage(&W); - pipe.add_stage(&S); - - if (!RECYCLE.init()) abort(); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/farm.cpp b/tests/d/farm.cpp deleted file mode 100644 index 3b2e04e9..00000000 --- a/tests/d/farm.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - * - * ------------------------------------------------------------------- - * | host0 | - * | ---------------------- | - * | | | | - * | | | | - * | master ------|-> Worker --> Sender -|-- - * | -------------------------- | | | - * | | | | | (ff_pipeline) | - * v | | | ---------------------- - * ---|-> Collector --> Emitter -|--- C P - * ^ | | | ON-DEMAND (COMM1) - * | | (ff_pipeline) | | - * | -------------------------- | host1 - * | C P | ---------------------- - * | FROM ANY (COMM2) | | | - * | | | | - * | ------|-> Worker --> Sender -|-- - * | | | | - * | | (ff_pipeline) | | - * | ---------------------- | - * | C P | - * ------------------------------------------------------------------- - * - * COMM1, the server address is master:port1 (the address1 parameter) - * COMM2, the server address is master:port2 (the address2 parameter) - * - */ - -#ifdef __linux -#include -#endif -#include -#include -#include -#include -#include -#include - -// On win dnode.h should be included as first header to ensure winsock2.h comes before windows.h -// ZMQ requires winsock2.h and conflicts with windows.h -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; - -#define COMM1 zmqOnDemand -#define COMM2 zmqFromAny -// gloabals just to save some coding -unsigned taskSize=0; -// the following SPSC unbounded queue is shared between the Worker and the Sender threads -// to free unused memory -uSWSR_Ptr_Buffer RECYCLE(1024); // NOTE: See also the farm_farm test for another solution to the same problem ! - - -class Collector: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -public: - Collector(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { - ff_dnode::init(name, address, nHosts, transp, RECEIVER,0); - return 0; - } - - void *svc(void *task) { - if (task == NULL) { - srandom(0); //::getpid()+(getusec()%4999)); - for(unsigned i=0;i { - typedef COMM1::TransportImpl transport_t; -protected: - static void callback(void * e,void*) { - delete [] (double*)e; - } -public: - Emitter(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, nHosts, transp, SENDER, 0, callback); - return 0; - } - - void * svc(void* task) { - if (--nTasks == 0) { - ff_send_out(task); - return EOS; // generates EOS - } - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={ptr,taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - } -private: - unsigned nTasks; - unsigned nHosts; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Worker: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -public: - Worker(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - myt = new double[taskSize*taskSize]; - assert(myt); - c = new double[taskSize*taskSize]; - assert(c); - for(unsigned j=0;j<(taskSize*taskSize);++j) - c[j] = j*1.0; - - ff_dnode::init(name, address, 1, transp, RECEIVER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - static unsigned c=0; - double* t = (double*)task; - printf("Worker: get one task %d\n",++c); - - memset(myt,0,taskSize*taskSize*sizeof(double)); - - for(unsigned i=0;i*& v, size_t len, const int sender=-1) { - svector * v2 = new svector(len); - assert(v2); - for(size_t i=0;ipush_back(m); - RECYCLE.push(m); - } - v = v2; - } - -private: - double * myt; - double * c; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Sender: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -protected: - static void callback(void *e,void*) { - msg_t* p; - if (!RECYCLE.pop((void**)&p)) abort(); - delete p; - } -public: - Sender(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, 1, transp, SENDER, transp->getProcId(),callback); - return 0; - } - - void* svc(void* task) { - printf("Sender, sending the task\n"); - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={ptr,taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - } -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -int main(int argc, char * argv[]) { - if (argc != 7) { - std::cerr << "use: " << argv[0] << " tasksize streamlen 1|0 nhosts host:port1 host:port2\n"; - std::cerr << " 1 for the master 0 for other hosts\n"; - std::cerr << " nhosts: is the number of hosts for the master and the hostID for the others\n"; - return -1; - } - - taskSize = atoi(argv[1]); - unsigned numTasks=atoi(argv[2]); - char* P = argv[3]; // 1 for the master 0 for other hosts - unsigned nhosts = atoi(argv[4]); - char* address1 = argv[5]; // no check - char* address2 = argv[6]; // no check - - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)?-1 : nhosts); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - ff_pipeline pipe; - Collector C(numTasks, nhosts, "B", address2, &transport); - Emitter E(numTasks, nhosts, "A", address1, &transport); - - C.skipfirstpop(true); - - pipe.add_stage(&C); - pipe.add_stage(&E); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - printf("Time= %f\n", pipe.ffwTime()); - } else { - ff_pipeline pipe; - Worker W("A", address1, &transport); - Sender S("B", address2, &transport); - pipe.add_stage(&W); - pipe.add_stage(&S); - - if (!RECYCLE.init()) abort(); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/farm_farm.cpp b/tests/d/farm_farm.cpp deleted file mode 100644 index 50e26c0f..00000000 --- a/tests/d/farm_farm.cpp +++ /dev/null @@ -1,422 +0,0 @@ -/* - * - * --------------------------------------------------------------- - * | host0 | - * | ------------------ | - * | | | | - * | | | | - * | master ------|-----> Farm ------|-- - * | -------------------------- | | | - * | | | | | (ff_farm) | - * v | | | ------------------ - * ---|-> Collector --> Emitter--|--- - * ^ | | | ON-DEMAND (COMM1) - * | | (ff_pipeline) | | - * | -------------------------- | host1 - * | C P | ------------------ - * | FROM ANY (COMM2) | | | - * | | | | - * | ------|------> Farm -----|-- - * | | | | - * | | (ff_farm) | | - * | ------------------ | - * | | - * --------------------------------------------------------------- - * NOTE: - Each Farm has the same number of workers (nw) - * - The Farm does not have the collector thus each worker sends - * data to the Collector. - * - * COMM1, the server address is master:port1 (the address1 parameter) - * COMM2, the server address is master:port2 (the address2 parameter) - * - * NOTE: - * If SINGLE_FARM is defined at compile time, then a single master-worker - * farm skeleton is used to compute all the matrices. - * - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#if defined(USE_PROC_AFFINITY) -#include -#endif -#include -#include -#include - -using namespace ff; - -#define COMM1 zmqOnDemand -#define COMM2 zmqFromAny -// gloabals just to save some coding -unsigned taskSize=0; - -#if defined(USE_PROC_AFFINITY) -//WARNING: the following mapping targets dual-eight core Intel Sandy-Bridge -const int worker_mapping[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; -const int emitter_mapping = 31; -const int PHYCORES = 16; -#endif - - -class Collector: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -public: - Collector(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { -#if !defined(SINGLE_FARM) - struct timeval start,stop; - gettimeofday(&start,NULL); - ff_dnode::init(name, address, nHosts, transp, RECEIVER,0); - gettimeofday(&stop,NULL); - - printf("Collector init time %f ms\n", diffmsec(stop,start)); -#endif - -#if defined(USE_PROC_AFFINITY) - if (ff_mapThreadToCpu(emitter_mapping)!=0) - printf("Cannot map Emitter to CPU %d\n",emitter_mapping); - //else printf("Emitter mapped to CPU %d\n", emitter_mapping); -#endif - - cnt=0; - return 0; - } -#if !defined(SINGLE_FARM) - void *svc(void *task) { - if (task == NULL) { - srandom(0); //::getpid()+(getusec()%4999)); - for(unsigned i=0;i { - typedef COMM1::TransportImpl transport_t; -protected: - static void callback(void* e, void* arg) { - delete [] (double*)e; - } -public: - Emitter(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - struct timeval start,stop; - gettimeofday(&start,NULL); - ff_dnode::init(name, address, nHosts, transp, SENDER, 0, callback); - - gettimeofday(&stop,NULL); - printf("Emitter init time %f ms\n", diffmsec(stop,start)); - return 0; - } - - void * svc(void* task) { - if (--nTasks == 0) { - ff_send_out(task); - return NULL; // generates EOS - } - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={ptr,taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - setCallbackArg(NULL); - } -private: - unsigned nTasks; - unsigned nHosts; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -struct ff_task_t { - double* getData() { return (double*)(msg->getData()); } - COMM1::TransportImpl::msg_t* msg; - ff_task_t* self; -}; - - -class Emitter2: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -public: - Emitter2(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - ff_dnode::init(name, address, 1, transp, RECEIVER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - return task; - } - - void prepare(svector*& v, size_t len, const int sender=-1) { - svector * v2 = new svector(len); - assert(v2); - for(size_t i=0;ipush_back(m); - } - v = v2; - } - - virtual void unmarshalling(svector* const v[], const int vlen, void *& task) { - assert(vlen==1 && v[0]->size()==1); - ff_task_t* t = new ff_task_t; - t->msg = v[0]->operator[](0); - t->self= t; - task = t; - //task = v[0]->operator[](0)->getData(); - delete v[0]; - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Worker: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -protected: - static void callback(void *e, void* arg) { - ff_task_t* t = (ff_task_t*)arg; - assert(t); - delete (t->msg); - delete (t->self); - } -public: - Worker(const int nw, const std::string& name, const std::string& address, transport_t* const transp): - nw(nw),name(name),address(address),transp(transp) { - } - - int svc_init() { - myt = new double[taskSize*taskSize]; - assert(myt); - c = new double[taskSize*taskSize]; - assert(c); - for(unsigned j=0;j<(taskSize*taskSize);++j) - c[j] = j*1.0; - -#if !defined(SINGLE_FARM) - ff_dnode::init(name, address, 1, transp, SENDER, transp->getProcId()*nw+get_my_id()); -#endif - -#if defined(USE_PROC_AFFINITY) - if (ff_mapThreadToCpu(worker_mapping[get_my_id() % PHYCORES])!=0) - printf("Cannot map Worker %d CPU %d\n",get_my_id(), - worker_mapping[get_my_id() % PHYCORES]); - //else printf("Thread %d mapped to CPU %d\n",get_my_id(), worker_mapping[get_my_id() % PHYCORES]); -#endif - - cnt=0; - return 0; - } - - void * svc(void * task) { -#if !defined(SINGLE_FARM) - double* t = ((ff_task_t*)task)->getData(); -#else - double* t = (double*)task; -#endif - bzero(myt,taskSize*taskSize*sizeof(double)); - - for(unsigned i=0;i& v, void* ptr, const int sender=-1) { - struct iovec iov={((ff_task_t*)ptr)->getData(),taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - setCallbackArg(ptr); - } - -private: - double * myt; - double * c; -protected: - const int nw; - const std::string name; - const std::string address; - transport_t * transp; - - unsigned cnt; -}; - - - -int main(int argc, char * argv[]) { - if (argc != 8) { - std::cerr << "use: " << argv[0] << " tasksize streamlen 1|0 nhosts nw host:port1 host:port2\n"; - std::cerr << " 1 for the master 0 for other hosts\n"; - std::cerr << " nhosts: is the number of hosts for the master and the hostID for the others\n"; - std::cerr << " nw: number of farm's worker\n"; - return -1; - } - - taskSize = atoi(argv[1]); - unsigned numTasks=atoi(argv[2]); - char* P = argv[3]; // 1 for the master 0 for other hosts - unsigned nhosts = atoi(argv[4]); - unsigned nw = atoi(argv[5]); - char* address1 = argv[6]; // no check - char* address2 = argv[7]; // no check - - -#if defined(SINGLE_FARM) - ff_farm<> farm; - Collector C(numTasks, nhosts, "A", address1, NULL); - farm.add_emitter(&C); - std::vector w; - for(unsigned i=0;i farm; - Emitter2 E2("A", address1, &transport); - farm.add_emitter(&E2); - std::vector w; - for(unsigned i=0;i -#include -#include -#include - -#include -#include -#include - -using namespace ff; - -int main(int argc, char * argv[]) { - if (argc != 5) { - std::cerr << "use: " << argv[0] << " name 1|0 nhosts master-host:port\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - int n = atoi(argv[3]); // num peers for the consumer node id for the producers - char * address = argv[4]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(n); // NOTE: n is used as node id - if (transport.initTransport()<0) abort(); - - FROMANY FromAny(new FROMANY_DESC(name,(atoi(P)?1:n),&transport,atoi(P))); - if (!FromAny.init(address)) abort(); - - if (atoi(P)) { - FROMANY::tosend_t msg; - for(int i=0;i<100;++i) { - msg.init(new int(i),sizeof(int)); - FromAny.put(msg); - } - } else { - FROMANY::torecv_t msg; - for(int i=0;i<100*n;++i) { - FromAny.get(msg); - printf("received %d\n", *static_cast(msg.getData())); - } - } - - FromAny.close(); - delete FromAny.getDescriptor(); - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/lat11.cpp b/tests/d/lat11.cpp deleted file mode 100644 index d12414f5..00000000 --- a/tests/d/lat11.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * - * host0 host1 - * -------------------- ------------------- - * | | | | - * | | UNICAST | | - * --|-> Node0 --> Node1 -|---------- |-> Node2 --> Node3 |-- - * | | | | | | - * | | (ff_pipeline) | | (ff_pipeline) | | - * | -------------------- ------------------- | - * | UNICAST | - * ---------------------------------------------------------- - * - * - * This simple test measures the latency of sending one long integer - * from host0 to host1. The Node0 sends ROUNDTRIP_COUNT integers out - * and wait to receive them back from Node3. - * When it receives the last one in input, then sends out the EOS. - * The latency is calculated as: - * Node0 computation time / (ROUNDTRIP_COUNT*2) - * - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -const long int ROUNDTRIP_COUNT=1000000; - -using namespace ff; - -class Node0: public ff_dnode { -public: - Node0(const std::string& name, const std::string& address, zmqTransport* const transp): cnt(0),sum(0), - name(name), address(address), transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address, 1,transp,false, 0); - printf("Node0 start\n"); - ff::ffTime(START_TIME); - return 0; - } - - void * svc(void* task) { - if (task==NULL) { - ff_send_out(new long(++cnt)); // will be deleted in Node1 - return GO_ON; - } - if (cnt > ROUNDTRIP_COUNT) return NULL; // generates EOS - sum+=*(long*)task; // just to check the final result - ff_send_out(new long(++cnt)); // will be deleted in Node1 - return GO_ON; - } - void svc_end() { - ff::ffTime(STOP_TIME); - printf("The sum of %ld integers is %ld\n", ROUNDTRIP_COUNT, sum); - printf("Latency = %f ms\n", (ff::ffTime(GET_TIME)/(ROUNDTRIP_COUNT*2))); - } -private: - long cnt,sum; - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -class Node1: public ff_dnode { -protected: - static void callback(void * e,void*) { - delete ((long*)e); - } -public: - Node1(const std::string& name, const std::string& address, zmqTransport* const transp): - name(name), address(address), transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address, 1,transp,true, 0, callback); - printf("Node1 start\n"); - return 0; - } - void * svc(void * task) { return task;} -private: - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -class Node2: public ff_dnode { -public: - typedef zmqTransport::msg_t msg_t; - - Node2(const std::string& name, const std::string& address, zmqTransport* const transp): - name(name),address(address),transp(transp) {} - - int svc_init() { - ff_dnode::init(name,address, 1, transp, false, 0); - printf("Node2 start\n"); - return 0; - } - void * svc(void *task) { return task;} - - void unmarshalling(svector* const v[], const int vlen, void *& task) { - // returns the msg - // the message (allocated by the run-time) will be deleted in Node3 - task = v[0]->operator[](0); - delete v[0]; - } -private: - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -class Node3: public ff_dnode { -protected: - static void callback(void * e,void* arg) { - delete (zmq1_1::TransportImpl::msg_t*)arg; - } -public: - typedef zmqTransport::msg_t msg_t; - - Node3(const std::string& name, const std::string& address, zmqTransport* const transp): - name(name),address(address),transp(transp) {} - - int svc_init() { - ff_dnode::init(name,address, 1, transp, true, 0, callback); - printf("Node3 start\n"); - return 0; - } - void* svc(void *task) { return task;} - - void prepare(svector& v, void* ptr, const int sender=-1) { - // just send the data not the message - struct iovec iov={((zmq1_1::TransportImpl::msg_t*)ptr)->getData(),sizeof(void*)}; - v.push_back(iov); - setCallbackArg(ptr); // set the pointer for the callback - } - -private: - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -int main(int argc, char * argv[]) { - if (argc != 4) { - std::cerr << "use: " << argv[0] << " 1|0 producer-host:port1 consumer-host:port2\n"; - return -1; - } - - char * P = argv[1]; // 1 producer 0 consumer - char * address1 = argv[2]; // no check, this is the address of the producer - char * address2 = argv[3]; // no check, this is the address of the consumer - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - ff_pipeline pipe; - Node0* n0 = new Node0("B", address2, &transport); - Node1* n1 = new Node1("A", address1, &transport); - n0->skipfirstpop(true); - pipe.add_stage(n0); - pipe.add_stage(n1); - pipe.run_and_wait_end(); - delete n0; - delete n1; - } else { - ff_pipeline pipe; - Node2 * n2 = new Node2("A", address1, &transport); - Node3 * n3 = new Node3("B", address2, &transport); - pipe.add_stage(n2); - pipe.add_stage(n3); - pipe.run_and_wait_end(); - delete n2; - delete n3; - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/lat11_v2.cpp b/tests/d/lat11_v2.cpp deleted file mode 100644 index 41eea6c9..00000000 --- a/tests/d/lat11_v2.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * - * host0 host1 - * -------------------- ------------------- - * | | | | - * | | COMM1 | | - * --|-> Node0 --> Node1 -|---------- |-> Node2 --> Node3 |-- - * | | | | | | - * | | (ff_pipeline) | | (ff_pipeline) | | - * | -------------------- ------------------- | - * | COMM | - * ---------------------------------------------------------- - * - * - * This simple test measures the latency of sending one long integer - * from host0 to host1. The Node0 sends ROUNDTRIP_COUNT integers out - * and wait to receive them back from Node3. - * When it receives the last one in input, then sends out the EOS. - * The latency is calculated as: - * Node0 computation time / (ROUNDTRIP_COUNT*2) - * - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -const long int ROUNDTRIP_COUNT=5000; - -using namespace ff; - -#define COMM1 zmq1_1 -#define COMM zmq1_1 // zmqOnDemand - -class Node0: public ff_dnode { -public: - Node0(const unsigned size, const std::string& name, const std::string& address, zmqTransport* const transp): cnt(0), - size(size),name(name), address(address), transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address, 1,transp,false, 0); - printf("Node0 start\n"); - ff::ffTime(START_TIME); - return 0; - } - - void * svc(void* task) { - if (task==NULL) { - char* data=new char[size]; -#if defined(MAKE_VALGRIND_HAPPY) - for(unsigned i=0;i= ROUNDTRIP_COUNT) { - //printf("EXIT, sending EOS\n"); - ff_send_out((void*)FF_EOS); - return GO_ON; - } - COMM::TransportImpl::msg_t* msg=(COMM::TransportImpl::msg_t*)task; - assert(size == msg->size()); - delete msg; - char* data=new char[size]; -#if defined(MAKE_VALGRIND_HAPPY) - for(unsigned i=0;i* const v[], const int vlen, void *& task) { - task = v[0]->operator[](0); - delete v[0]; - } - -private: - long cnt; - const unsigned size; - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -class Node1: public ff_dnode { -protected: - static void callback(void * e,void*) { - delete [] ((char*)e); - } -public: - Node1(const unsigned size, const std::string& name, const std::string& address, zmqTransport* const transp): - size(size),name(name), address(address), transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address, 1,transp,true, 0, callback); - printf("Node1 start\n"); - return 0; - } - void * svc(void * task) { return task;} - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={ptr,size*sizeof(char)}; - v.push_back(iov); - setCallbackArg(NULL); - } -private: - const unsigned size; - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -class Node2: public ff_dnode { -public: - typedef zmqTransport::msg_t msg_t; - - Node2(const unsigned size, const std::string& name, const std::string& address, zmqTransport* const transp): - size(size),name(name),address(address),transp(transp) {} - - int svc_init() { - ff_dnode::init(name,address, 1, transp, false, 0); - printf("Node2 start\n"); - return 0; - } - void * svc(void *task) { - COMM1::TransportImpl::msg_t* msg=(COMM1::TransportImpl::msg_t*)task; - assert(size == msg->size()); - return task; - } - - void unmarshalling(svector* const v[], const int vlen, void *& task) { - // returns the msg - // the message (allocated by the run-time) will be deleted in Node3 - task = v[0]->operator[](0); - delete v[0]; - } -private: - const unsigned size; - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -class Node3: public ff_dnode { -protected: - static void callback(void * e,void* arg) { - delete (COMM::TransportImpl::msg_t*)arg; - } -public: - typedef zmqTransport::msg_t msg_t; - - Node3(const unsigned size, const std::string& name, const std::string& address, zmqTransport* const transp): - size(size), name(name),address(address),transp(transp) {} - - int svc_init() { - ff_dnode::init(name,address, 1, transp, true, 0, callback); - printf("Node3 start\n"); - return 0; - } - void* svc(void *task) { - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - // just send the data not the message - struct iovec iov={((COMM::TransportImpl::msg_t*)ptr)->getData(),size}; - v.push_back(iov); - setCallbackArg(ptr); // set the pointer for the callback - } - -private: - const unsigned size; - const std::string name; - const std::string address; - zmqTransport* const transp; -}; - - -int main(int argc, char * argv[]) { - if (argc != 5) { - std::cerr << "use: " << argv[0] << " 1|0 producer-host:port1 consumer-host:port2 size-in-bytes\n"; - return -1; - } - - char * P = argv[1]; // 1 producer 0 consumer - char * address1 = argv[2]; // no check, this is the address of the producer - char * address2 = argv[3]; // no check, this is the address of the consumer - unsigned size = atoi(argv[4]); - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - ff_pipeline pipe; - Node0* n0 = new Node0(size, "B", address2, &transport); - Node1* n1 = new Node1(size, "A", address1, &transport); - n0->skipfirstpop(true); - pipe.add_stage(n0); - pipe.add_stage(n1); - pipe.run_and_wait_end(); - delete n0; - delete n1; - } else { - ff_pipeline pipe; - Node2 * n2 = new Node2(size, "A", address1, &transport); - Node3 * n3 = new Node3(size, "B", address2, &transport); - pipe.add_stage(n2); - pipe.add_stage(n3); - pipe.run_and_wait_end(); - delete n2; - delete n3; - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/ondemand.cpp b/tests/d/ondemand.cpp deleted file mode 100644 index 78f0b187..00000000 --- a/tests/d/ondemand.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -const unsigned MAX_SLEEP=500000; - -using namespace ff; - -typedef zmqTransportMsg_t msg_t; - -int main(int argc, char * argv[]) { - if (argc != 5) { - std::cerr << "use: " << argv[0] << " name 1|0 nhosts master-host:port\n"; - std::cerr << " 1|0 : 1 for the master 0 for other hosts\n"; - std::cerr << " nhosts: is the number of hosts for the master and the hostID for the others\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - int n = atoi(argv[3]); - char * address = argv[4]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(n); - if (transport.initTransport()<0) abort(); - - ONDEMAND OnDemand(new ONDEMAND_DESC(name,n,&transport,atoi(P))); - if (!OnDemand.init(address)) abort(); - - srandom(::getpid()+(getusec()%4999)); - - msg_t msg; - if (atoi(P)) { - for(int i=0;i<10;++i) { - msg.init(new int(i),sizeof(int)); - OnDemand.put(msg); - } - // sending EOS - //msg.init(new int(-1),sizeof(int)); - //OnDemand.put(msg,-1); - for(int i=0;i(msg.getData()); - if (*d==-1) { printf("RECEIVED EOS\n"); break; } - long sleeptime= (random() % MAX_SLEEP) + n*1000000; - printf("received %d sleeping for %ld us\n", *static_cast(msg.getData()), sleeptime); - usleep(sleeptime); - ++ntasks; - } while(1); - printf("got %d tasks\n", ntasks); - } - - OnDemand.close(); - delete OnDemand.getDescriptor(); - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/pipe_farm.cpp b/tests/d/pipe_farm.cpp deleted file mode 100644 index 9a43f531..00000000 --- a/tests/d/pipe_farm.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * - * host0 host1 - * ------------------ ------------------- - * | | | | - * | | UNICAST | | - * | farm(f) ----|---------- |----> farm(g) | - * | | | | - * ------------------ ------------------- - * pipeline(farm(f),farm(g)) - * - * host0 - * ------------------ - * | | - * | | - * | farm(f;g) | - * | | - * ------------------ - * - * If we define SINGLE at compile-time, then the code executed is on - * a single host as farm(f;g). - * - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; -#define COMM zmq1_1 - -// gloabal just to save some coding -unsigned taskSize=0; - -class Emitter: public ff_node { -public: - Emitter(unsigned nTasks):nTasks(nTasks) {} - void * svc(void*) { - srandom(0); //::getpid()+(getusec()%4999)); - - for(unsigned i=0;i { -protected: - static void callback(void * e,void*) { - delete [] (double*)e; - } -public: - typedef COMM::TransportImpl transport_t; - - Collector(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - -#if !defined(SINGLE) - // initializes dnode - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, 1, transp, SENDER, 0, callback); - return 0; - } -#endif - - void *svc(void *task) { -#if defined(SINGLE) - callback(task,NULL); - return GO_ON; -#endif - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={ptr,taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -class Emitter2: public ff_dnode { - typedef COMM::TransportImpl transport_t; -public: - Emitter2(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { - ff_dnode::init(name, address, 1, transp, RECEIVER); - return 0; - } - - void * svc(void* task) { - printf("Emitter2 received one task\n"); - assert(task); - return task; - } -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -class Worker2: public ff_node { -public: - - int svc_init() { - myt = new double[taskSize*taskSize]; - assert(myt); - c = new double[taskSize*taskSize]; - assert(c); - for(unsigned j=0;j<(taskSize*taskSize);++j) - c[j] = j*1.0; - - return 0; - } - void * svc(void * task) { - double* t = (double*)task; - bzero(myt,taskSize*taskSize*sizeof(double)); - for(unsigned i=0;i farm; - Emitter E(numTasks); - farm.add_emitter(&E); - Collector C(name, address, &transport); - farm.add_collector(&C); - std::vector w; - for(unsigned i=0;i farm2; - Emitter2 E2(name, address, &transport); - farm2.add_emitter(&E2); - Collector2 C2; - farm2.add_collector(&C2); - std::vector w2; - for(unsigned i=0;i Farm ------|--- Last - * -------------------------- | | | | --------------------------- - * | | | | (ff_farm) | | | | - * | | | A ------------------ | B | | C - * --|-> StartStop --> Emitter--|--- ------------| --> Collector --> Sender--|--- - * | | | | ON-DEMAND (COMM1) | | | | - * | | (ff_pipeline) | | | | | | - * | -------------------------- | Farm1 | | (ff_pipeline) | | - * | | ------------------ | --------------------------- | - * | | | | | | - * | | | | | | - * | ------|------> Farm -----|--- | - * | | | FROM ANY (COMM2) | - * | | (ff_farm) | | - * | ------------------ | - * | UNICAST (COMM3) | - * ----------------------------------------------------------------------------------------------------------- - * - * NOTE: - Each Farm has the same number of workers (nw) - * - * - * - * COMM1, the server address is master:port1 (the address1 parameter) - * COMM2, the server address is master:port2 (the address2 parameter) - * COMM3, the server address is master:port3 (the address3 parameter) - * - * - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#if defined(USE_PROC_AFFINITY) -#include -#endif -#include -#include -#include - -using namespace ff; - -#define COMM1 zmqOnDemand -#define COMM2 zmqFromAny -#define COMM3 zmq1_1 - -// gloabals just to save some coding -unsigned taskSize=0; - -#if defined(USE_PROC_AFFINITY) -//WARNING: the following mapping targets dual-eight core Intel Sandy-Bridge -const int worker_mapping[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; -const int emitter_mapping = 31; -const int PHYCORES = 16; -#endif - - -struct ff_task_t { - double* getData() { return (double*)(msg->getData()); } - zmqTransport::msg_t* msg; - ff_task_t* self; -}; - - -/* -------------- First ------------------- */ - -class StartStop: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -public: - StartStop(unsigned nTasks, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { - struct timeval start,stop; - gettimeofday(&start,NULL); - printf("StartStop init Id= %d\n", transp->getProcId()); - ff_dnode::init(name, address, 1, transp, RECEIVER,transp->getProcId()); - gettimeofday(&stop,NULL); - - printf("StartStop init time %f ms\n", diffmsec(stop,start)); - - cnt=0; - return 0; - } - - void *svc(void *task) { - if (task == NULL) { - srandom(0); //::getpid()+(getusec()%4999)); - for(unsigned i=0;imsg); - delete (t->self); - - ++cnt; - return GO_ON; - } - - void svc_end() { - printf("StartStop: computed %d tasks\n", cnt); - } - - void prepare(svector*& v, size_t len, const int sender=-1) { - svector * v2 = new svector(len); - assert(v2); - for(size_t i=0;ipush_back(m); - } - v = v2; - } - - virtual void unmarshalling(svector* const v[], const int vlen, void *& task) { - assert(vlen==1 && v[0]->size()==1); - ff_task_t* t = new ff_task_t; - t->msg = v[0]->operator[](0); - t->self= t; - task = t; - delete v[0]; - } - -private: - unsigned nTasks; -protected: - const std::string name; - const std::string address; - transport_t * transp; - unsigned cnt; -}; - - -class Emitter: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -protected: - static void callback(void* e, void* arg) { - delete [] (double*)e; - } -public: - Emitter(unsigned nTasks, unsigned nHosts, const std::string& name, const std::string& address, transport_t* const transp): - nTasks(nTasks),nHosts(nHosts),name(name),address(address),transp(transp) { - } - - int svc_init() { - struct timeval start,stop; - gettimeofday(&start,NULL); - printf("Emitter init Id= %d\n", transp->getProcId()); - ff_dnode::init(name, address, nHosts, transp, SENDER, transp->getProcId(), callback); - - gettimeofday(&stop,NULL); - printf("Emitter init time %f ms\n", diffmsec(stop,start)); - return 0; - } - - void * svc(void* task) { - if (--nTasks == 0) { - ff_send_out(task); - return NULL; // generates EOS - } - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={ptr,taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - setCallbackArg(NULL); - } -private: - unsigned nTasks; - unsigned nHosts; -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -/* -------------- Farm ------------------- */ - -class Emitter2: public ff_dnode { - typedef COMM1::TransportImpl transport_t; -public: - Emitter2(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - printf("Emitter2 init Id= %d\n", transp->getProcId()); - ff_dnode::init(name, address, 1, transp, RECEIVER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - return task; - } - - void prepare(svector*& v, size_t len, const int sender=-1) { - svector * v2 = new svector(len); - assert(v2); - for(size_t i=0;ipush_back(m); - } - v = v2; - } - - virtual void unmarshalling(svector* const v[], const int vlen, void *& task) { - assert(vlen==1 && v[0]->size()==1); - ff_task_t* t = new ff_task_t; - t->msg = v[0]->operator[](0); - t->self= t; - task = t; - delete v[0]; - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Worker: public ff_node { -public: - int svc_init() { - myt = new double[taskSize*taskSize]; - assert(myt); - cnt=0; - return 0; - } - - void * svc(void * task) { - double* t = ((ff_task_t*)task)->getData(); - bzero(myt,taskSize*taskSize*sizeof(double)); - - for(unsigned i=0;i { - typedef COMM2::TransportImpl transport_t; -protected: - static void callback(void *e, void* arg) { - ff_task_t* t = (ff_task_t*)arg; - assert(t); - delete (t->msg); - delete (t->self); - } -public: - Collector2(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - printf("Collector2 init Id= %d\n", transp->getProcId()); - ff_dnode::init(name, address, 1, transp, SENDER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={((ff_task_t*)ptr)->getData(),taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - setCallbackArg(ptr); - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -/* -------------- Last ------------------- */ - -class Collector: public ff_dnode { - typedef COMM2::TransportImpl transport_t; -public: - - Collector(const std::string& name, unsigned nHosts, const std::string& address, transport_t* const transp): - name(name),nHosts(nHosts), address(address),transp(transp) { - } - - int svc_init() { - printf("Collector init Id= %d\n", transp->getProcId()); - ff_dnode::init(name, address, nHosts, transp, RECEIVER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - return task; - } - - void prepare(svector*& v, size_t len, const int sender=-1) { - svector * v2 = new svector(len); - assert(v2); - for(size_t i=0;ipush_back(m); - } - v = v2; - } - - virtual void unmarshalling(svector* const v[], const int vlen, void *& task) { - assert(vlen==1 && v[0]->size()==1); - ff_task_t* t = new ff_task_t; - t->msg = v[0]->operator[](0); - t->self= t; - task = t; - delete v[0]; - } - -protected: - const std::string name; - const unsigned nHosts; - const std::string address; - transport_t * transp; -}; - - -class Sender: public ff_dnode { - typedef COMM3::TransportImpl transport_t; -protected: - static void callback(void *e, void* arg) { - ff_task_t* t = (ff_task_t*)arg; - assert(t); - delete (t->msg); - delete (t->self); - } -public: - Sender(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - printf("Sender init Id= %d\n", transp->getProcId()); - ff_dnode::init(name, address, 1, transp, SENDER, transp->getProcId()); - return 0; - } - - void * svc(void * task) { - return task; - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - struct iovec iov={((ff_task_t*)ptr)->getData(),taskSize*taskSize*sizeof(double)}; - v.push_back(iov); - setCallbackArg(ptr); - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - - -/* --------------------------------- */ - -int main(int argc, char * argv[]) { - - if (argc == 2 && (atoi(argv[1])<0)) { - printf( - " Farm0 \n" - " ------------------ \n" - " | | \n" - " | | \n" - " First ------|-----> Farm ------|--- Last \n" - " -------------------------- | | | | --------------------------- \n" - " | | | | (ff_farm) | | | | \n" - " | | | A ------------------ | B | | C \n" - " --|-> StartStop --> Emitter--|--- ------------| --> Collector --> Sender--|--- \n" - " | | | | ON-DEMAND (COMM1) | | | | \n" - " | | (ff_pipeline) | | | | | | \n" - " | -------------------------- | Farm1 | | (ff_pipeline) | | \n" - " | | ------------------ | --------------------------- | \n" - " | | | | | | \n" - " | | | | | | \n" - " | ------|------> Farm -----|--- | \n" - " | | | FROM ANY (COMM2) | \n" - " | | (ff_farm) | | \n" - " | ------------------ | \n" - " | UNICAST (COMM3) | \n" - " ----------------------------------------------------------------------------------------------------------- \n" - " \n" - " COMM1, the server address is master:port1 (the address1 parameter) \n" - " COMM2, the server address is master:port2 (the address2 parameter) \n" - " COMM3, the server address is master:port3 (the address3 parameter) \n"); - return 0; - } - - - if (argc < 9) { - std::cerr << "\n"; - std::cerr << "use: " << argv[0] << " tasksize stream-length hostId nfarms nw host:port1 host:port2 host:port3\n"; - std::cerr << " hostId: the host identifier, for the farms the hostId(s) are in the range [0..N[\n"; - std::cerr << " -1 is the id of the first stage (First in the picture)\n"; - std::cerr << " -2 is the id of the last stage (Last in the picture)\n"; - std::cerr << " nfarms: number of farm hosts\n"; - std::cerr << " nw: number of farm's worker\n\n"; - std::cerr << " To print the application picture use " << argv[0] << " -1\n\n\n"; - return -1; - } - - // SPMD style code - - taskSize = atoi(argv[1]); - - unsigned numTasks=atoi(argv[2]); - int hostId = atoi(argv[3]); - unsigned nfarms = atoi(argv[4]); - unsigned nw = atoi(argv[5]); - char* address1 = argv[6]; // no check - char* address2 = argv[7]; // no check - char* address3 = argv[8]; // no check - - - // creates the network using 0mq as transport layer - zmqTransport transport(hostId); - if (transport.initTransport()<0) abort(); - - if (hostId == -1) { // First - ff_pipeline pipe; - StartStop C(numTasks,"C", address3, &transport); - Emitter E(numTasks, nfarms, "A", address1, &transport); - - C.skipfirstpop(true); - - pipe.add_stage(&C); - pipe.add_stage(&E); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - printf("wTime= %f\n", pipe.ffwTime()); - printf("Time = %f\n", pipe.ffTime()); - } else if (hostId == -2) { // Last - ff_pipeline pipe; - Collector C("B", nfarms, address2, &transport); - Sender E("C", address3, &transport); - - pipe.add_stage(&C); - pipe.add_stage(&E); - - if (pipe.run_and_wait_end()<0) { - error("running pipeline\n"); - return -1; - } - } else { // Farm(s) - ff_farm<> farm; - Emitter2 E2("A", address1, &transport); - farm.add_emitter(&E2); - std::vector w; - for(unsigned i=0;i -#include -#include -#include - -#include -#include -#include - -using namespace ff; - -typedef zmqTransportMsg_t msg_t; - -int main(int argc, char * argv[]) { - if (argc != 5) { - std::cerr << "use: " << argv[0] << " name 1|0 nhosts master-host:port\n"; - std::cerr << " 1|0 : 1 for the master 0 for other hosts\n"; - std::cerr << " nhosts: is the number of hosts for the master and the hostID for the others\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - int n = atoi(argv[3]); // num peers for the producer, node id for the consumers - char * address = argv[4]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(n); - if (transport.initTransport()<0) abort(); - - SCATTER Scatter(new SCATTER_DESC(name,n,&transport,atoi(P))); - if (!Scatter.init(address)) abort(); - - msg_t msg; - if (atoi(P)) { - for(int i=0;i<100;++i) { - SCATTER::tosend_t msg; - msg.resize(n); - int *M = new int[n]; - for(int j=0;j(msg.getData())); - } - } - - Scatter.close(); - delete Scatter.getDescriptor(); - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/test11_pipe.cpp b/tests/d/test11_pipe.cpp deleted file mode 100644 index 2aa2ded9..00000000 --- a/tests/d/test11_pipe.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * host0 host1 - * ------------------ ------------------- - * | | | | - * | | UNICAST | | - * | Node0 --> Node1 -|---------- |-> Node2 --> Node3 | - * | | | | - * | (ff_pipeline) | | (ff_pipeline) | - * ------------------ ------------------- - * - * - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; -/* Other options are: zmqOnDemand and zmqBcast */ -#define COMM zmq1_1 - -class Node0: public ff_node { -public: - void * svc(void*) { - printf("Node0 starting\n"); - for(long i=1;i<=10000;++i) - ff_send_out((void*)i); - printf("Node0 exiting\n"); - return NULL; - } -}; - -class Node1: public ff_dnode { -protected: - static void callback(void * e,void* ) { - delete ((long*)e); - } -public: - typedef COMM::TransportImpl transport_t; - - Node1(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address, 1, transp, SENDER, 0, callback); - - printf("Node1 starting\n"); - return 0; - } - - void * svc(void *task) { - printf("Node1 received %ld\n", (long)task); - return (new long((long)task)); // the callback deallocates the data - } - - void svc_end() { - printf("Node1 svn_end\n"); - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Node2: public ff_dnode { -public: - typedef zmqTransport::msg_t msg_t; - typedef COMM::TransportImpl transport_t; - - Node2(const std::string& name, const std::string& address, zmqTransport* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - // initializes dnode - ff_dnode::init(name, address, 1, transp, RECEIVER); - printf("Node2 starting\n"); - return 0; - } - - void * svc(void *task) { - //printf("Node2 received %ld\n", *(long*)task); - return (new long(*(long*)task)); - } - - // overriding the default prepare method - void prepare(svector*& v, size_t len,const int=-1) { - msgv.clear(); - msgv.reserve(len); - msgv.push_back(&msg); - v=&msgv; - } - - void unmarshalling(svector* const v[], const int vlen, void *& ptr) { - // potentially, I can receive multiple messages depending on - // the fact that the sender has serialized the output data - // in multiple parts (peraphs because the message is not - // contiguous in memory) - // - assert(vlen==1 && v[0]->size()==1); // in this example we have just 1 msg - ptr = v[0]->operator[](0)->getData(); - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -private: - svector msgv; - msg_t msg; - double t; -}; - -class Node3: public ff_node { -public: - void *svc(void *task) { - printf("Node3 received %ld\n", *(long*)task); - delete ((long*)task); - return GO_ON; - } -}; - - - -int main(int argc, char * argv[]) { - if (argc != 4) { - std::cerr << "use: " << argv[0] << " name 1|0 host:port\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - char * address = argv[3]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(0); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - Node0 * n0 = new Node0; - Node1 * n1 = new Node1(name, address, &transport); - - ff_pipeline pipe; - pipe.add_stage(n0); - pipe.add_stage(n1); - pipe.run_and_wait_end(); - - delete n0; - delete n1; - } else { - Node2 * n2 = new Node2(name, address, &transport); - Node3 * n3 = new Node3; - - ff_pipeline pipe; - pipe.add_stage(n2); - pipe.add_stage(n3); - pipe.run_and_wait_end(); - delete n2; - delete n3; - } - // TODO: shutdown protocol - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/test11_torus.cpp b/tests/d/test11_torus.cpp deleted file mode 100644 index eb144ba3..00000000 --- a/tests/d/test11_torus.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * - * host0 host1 - * -------------------- UNICAST ------------------- - * | | or | | - * | | ONDEMAND | | - * --|-> Node0 --> Node1 -|---------- |-> Node2 --> Node3 |-- - * | | | | | | - * | | (ff_pipeline) | | (ff_pipeline) | | - * | -------------------- ------------------- | - * | UNICAST | - * ---------------------------------------------------------- - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; - -#define NTASKS 10000 -#define COMM zmq1_1 - -class Node0: public ff_dnode { -public: - - Node0(const std::string& name, const std::string& address, zmq1_1::TransportImpl* const transp): - ntasks(NTASKS), name(name),address(address),transp(transp) { } - - int svc_init() { - ff_dnode::init(name, address,1, transp,RECEIVER); - ff::ffTime(START_TIME); - return 0; - } - - void * svc(void* task) { - if (task==NULL) { - for(unsigned long i=1;i<=ntasks;++i) - ff_send_out((void*)i); - return GO_ON; - } - printf("Node0 received %ld\n", *(long*)task); - - if (--ntasks == 0) return NULL; - return GO_ON; - } - - void svc_end() { - printf("Time= %f ms\n", ff::ffTime(STOP_TIME)); - } - - - // overriding the default prepare and unmarshall methods - void prepare(svector*& v, size_t len,const int=-1) { - msgv.clear(); - msgv.reserve(len); - msgv.push_back(&msg); - v=&msgv; - } - void unmarshalling(svector* const v[], const int vlen, void *& ptr) { - assert(vlen==1 && v[0]->size()==1); // in this example we have just 1 msg - ptr = v[0]->operator[](0)->getData(); - } - - -private: - svector msgv; - msg_t msg; - -private: - unsigned long ntasks; - const std::string name; - const std::string address; - zmq1_1::TransportImpl* const transp; -}; - -class Node1: public ff_dnode { -protected: - static void callback(void * e,void* ) { - delete ((long*)e); - } -public: - - Node1(const std::string& name, const std::string& address, zmq1_1::TransportImpl* const transp): - name(name), address(address), transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address,1, transp,SENDER, 0, callback); - return 0; - } - - void * svc(void *task) { - return (new long((long)task)); // the callback deallocates the data - } - -private: - const std::string name; - const std::string address; - COMM::TransportImpl* const transp; -}; - -class Node2: public ff_dnode { -public: - typedef zmqTransport::msg_t msg_t; - - Node2(const std::string& name, const std::string& address,zmq1_1::TransportImpl* const transp): - name(name),address(address),transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address,1, transp, RECEIVER, 0); - return 0; - } - - void * svc(void *task) { - return (new long(*(long*)task)); - } - - // overriding the default prepare and unmarshall methods - void prepare(svector*& v, size_t len,const int=-1) { - msgv.clear(); - msgv.reserve(len); - msgv.push_back(&msg); - v=&msgv; - } - void unmarshalling(svector* const v[], const int vlen, void *& ptr) { - assert(vlen==1 && v[0]->size()==1); // in this example we have just 1 msg - ptr = v[0]->operator[](0)->getData(); - } - -private: - svector msgv; - msg_t msg; - const std::string name; - const std::string address; - COMM::TransportImpl* const transp; -}; - -class Node3: public ff_dnode { -protected: - static void callback(void * e,void*) { - delete ((long*)e); - } -public: - Node3(const std::string& name, const std::string& address, zmq1_1::TransportImpl* const transp): - name(name),address(address),transp(transp) {} - - int svc_init() { - ff_dnode::init(name, address,1, transp, SENDER, 0, callback); - return 0; - } - - void *svc(void *task) { - return task; - } -private: - const std::string name; - const std::string address; - zmq1_1::TransportImpl* const transp; -}; - - - -int main(int argc, char * argv[]) { - if (argc != 6) { - std::cerr << "use: " << argv[0] << " name1 name2 1|0 host1:port1 host2:port2\n"; - return -1; - } - - char * name1 = argv[1]; - char * name2 = argv[2]; - char * P = argv[3]; // 1 producer 0 consumer - char * address1 = argv[4]; // no check - char * address2 = argv[5]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(0); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - Node0 * n0 = new Node0(name2, address2, &transport); - Node1 * n1 = new Node1(name1, address1, &transport); - n0->skipfirstpop(true); - - ff_pipeline pipe(false, NTASKS); - pipe.add_stage(n0); - pipe.add_stage(n1); - pipe.run_and_wait_end(); - - delete n0; - delete n1; - } else { - Node2 * n2 = new Node2(name1, address1, &transport); - Node3 * n3 = new Node3(name2, address2, &transport); - - ff_pipeline pipe(false, NTASKS); - pipe.add_stage(n2); - pipe.add_stage(n3); - pipe.run_and_wait_end(); - delete n2; - delete n3; - } - // TODO: shutdown protocol - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/test_dinout.cpp b/tests/d/test_dinout.cpp deleted file mode 100644 index 1ae2aeaf..00000000 --- a/tests/d/test_dinout.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * - * host0 host1 - * ---------------- ---------------- - * | | | | - * | | UNICAST | | - * --|---> InOut0 ---|---------- |---> InOut1 --- |-- - * | | | | | | - * | | | | | | - * | ---------------- ---------------- | - * | UNICAST | - * --------------------------------------------------- - * - * - */ - -#include -#include -#include -#include -#include -#include - -using namespace ff; - -#define COMM1 zmq1_1 -#define COMM2 zmq1_1 - -#define NUMTASKS 1000 - -class InOut0: public ff_dinout { -protected: - static void callback(void * e,void* ) { - delete ((long*)e); - } -public: - typedef COMM1::TransportImpl transport_t; - - InOut0(const std::string& name1, const std::string& address1, - const std::string& name2, const std::string& address2, - transport_t* const transp): - name1(name1),address1(address1), - name2(name2),address2(address2), - transp(transp) { - } - - // initializes dnode - int svc_init() { - ff_dinout::initOut(name2, address2, 1, transp,0,callback); - ff_dinout::initIn(name1, address1, 1, transp); - return 0; - } - - void * svc(void *task) { - if (task==NULL) { - for(long i=1;i<=NUMTASKS;++i) - ff_send_out((void*)new int(i)); - ff_send_out((void*)FF_EOS); - return GO_ON; - } - printf("InOut0 received %ld\n", *(long*)task); - return GO_ON; - } - - void svc_end() { - printf("InOut0 ending\n"); - } - -protected: - const std::string name1; - const std::string address1; - const std::string name2; - const std::string address2; - transport_t * transp; -}; - - -class InOut1: public ff_dinout { -protected: - static void callback(void * e,void* ) { - delete ((long*)e); - } -public: - typedef COMM1::TransportImpl transport_t; - - InOut1(const std::string& name1, const std::string& address1, - const std::string& name2, const std::string& address2, - transport_t* const transp): - name1(name1),address1(address1), - name2(name2),address2(address2), - transp(transp) { - } - - // initializes dnode - int svc_init() { - ff_dinout::initIn(name2, address2, 1, transp); - ff_dinout::initOut(name1, address1, 1, transp, 0, callback); - return 0; - } - - void * svc(void *task) { - printf("InOut1 received %ld\n", *(long*)task); - return (new long(*(long*)task)); - } - - void svc_end() { - printf("InOut1 ending\n"); - } - - virtual FFBUFFER * get_out_buffer() const { return (FFBUFFER*)1;} - -protected: - const std::string name1; - const std::string address1; - const std::string name2; - const std::string address2; - transport_t * transp; -}; - - -int main(int argc, char * argv[]) { - if (argc != 6) { - std::cerr << "use: " << argv[0] << " name1 name2 1|0 host0:port host1:port\n"; - return -1; - } - - char * name1 = argv[1]; - char * name2 = argv[2]; - char * P = argv[3]; // 1 producer 0 consumer - char * address1 = argv[4]; // no check - char * address2 = argv[5]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(0); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - InOut0 n0(name1,address1,name2,address2,&transport); - n0.skipfirstpop(true); - n0.run(); - n0.wait(); - } else { - InOut1 n1(name1,address1,name2,address2,&transport); - n1.run(); - n1.wait(); - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/test_gw.cpp b/tests/d/test_gw.cpp deleted file mode 100644 index 7d1b6bab..00000000 --- a/tests/d/test_gw.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * - * - * --------- ----------- ----------- - * | | | | | | - * | | OnDemand | | FromAny | | - * | U ---|---------->| gw |---------->| P | - * | | | | | | - * --------- ----------- ----------- - * - * - */ - -#include -#include -#include -#include -#include - -using namespace ff; - -#define COMM1 zmqOnDemand -#define COMM2 zmqFromAny - -#define NUMTASKS 1000 - -class U: public ff_dnode { - - -}; - - - -class GW: public ff_dinout { -protected: - static void callback(void * e,void* ) { - delete ((long*)e); - } -public: - typedef COMM1::TransportImpl transport_t; - - GW(const std::string& name1, const std::string& address1, - const std::string& name2, const std::string& address2, - transport_t* const transp): - name1(name1),address1(address1), - name2(name2),address2(address2), - transp(transp) {} - - // initializes dnode - int svc_init() { - ff_dinout::initOut(name2, address2, 1, transp,0,callback); - ff_dinout::initIn(name1, address1, 1, transp); - return 0; - } - // TODO: increase/decrease granularity at run-time - void * svc(void *task) { return task); - -protected: - const std::string name1; - const std::string address1; - const std::string name2; - const std::string address2; - transport_t * transp; -}; - - -class InOut1: public ff_dinout { -protected: - static void callback(void * e,void* ) { - delete ((long*)e); - } -public: - typedef COMM1::TransportImpl transport_t; - - InOut1(const std::string& name1, const std::string& address1, - const std::string& name2, const std::string& address2, - transport_t* const transp): - name1(name1),address1(address1), - name2(name2),address2(address2), - transp(transp) { - } - - // initializes dnode - int svc_init() { - ff_dinout::initIn(name2, address2, 1, transp); - ff_dinout::initOut(name1, address1, 1, transp, 0, callback); - return 0; - } - - void * svc(void *task) { - printf("InOut1 received %ld\n", *(long*)task); - return (new long(*(long*)task)); - } - - void svc_end() { - printf("InOut1 ending\n"); - } - - virtual FFBUFFER * const get_out_buffer() const { return (FFBUFFER*)1;} - -protected: - const std::string name1; - const std::string address1; - const std::string name2; - const std::string address2; - transport_t * transp; -}; - - -int main(int argc, char * argv[]) { - if (argc != 6) { - std::cerr << "use: " << argv[0] << " name1 name2 1|0 host0:port host1:port\n"; - return -1; - } - - char * name1 = argv[1]; - char * name2 = argv[2]; - char * P = argv[3]; // 1 producer 0 consumer - char * address1 = argv[4]; // no check - char * address2 = argv[5]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(0); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - InOut0 n0(name1,address1,name2,address2,&transport); - n0.skipfirstpop(true); - n0.run(); - n0.wait(); - } else { - InOut1 n1(name1,address1,name2,address2,&transport); - n1.run(); - n1.wait(); - } - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/test_marshal.cpp b/tests/d/test_marshal.cpp deleted file mode 100644 index edf7d8a8..00000000 --- a/tests/d/test_marshal.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace ff; - -#define COMM zmqBcast - -// my string type -struct mystring_t { - mystring_t(int l, char* str):length(l),str(str) {} - unsigned length; - char * str; -}; - -class Node0: public ff_node { -public: - void * svc(void*) { - printf("Node0 starting\n"); - ff_send_out((void*)GO_ON); - printf("Node0 exiting\n"); - return NULL; - } -}; - -class Node1: public ff_dnode { -protected: - static void callback(void * e,void*) { - static int flag=0; - if (flag==0) { - mystring_t* s = (mystring_t*)e; - delete s; - flag=1; - return; - } - char* s = (char*)e; - delete [] s; - flag=0; - } - -public: - typedef COMM::TransportImpl transport_t; - - Node1(const std::string& name, const std::string& address, transport_t* const transp): - name(name),address(address),transp(transp) { - } - - // initializes dnode - int svc_init() { - // the callback will be called as soon as the output message is no - // longer in use by the transport layer - ff_dnode::init(name, address,1, transp, SENDER, 0, callback); - - printf("Node1 starting\n"); - return 0; - } - - void * svc(void *task) { - printf("Node1 started\n"); - - char * s1 = new char[12+1]; - strncpy(s1, "Hello World!", 12+1); - mystring_t* s = new mystring_t(12,s1); - ff_send_out(s); - - char * s2 = new char[32+1]; - strncpy(s2, "This is just a very simple test.", 32+1); - mystring_t* k = new mystring_t(32,s2); - ff_send_out(k); - - return NULL; - } - - void svc_end() { - printf("Node1 ending\n"); - } - - void prepare(svector& v, void* ptr, const int sender=-1) { - mystring_t* p = static_cast(ptr); - struct iovec iov={ptr,sizeof(mystring_t)}; - v.push_back(iov); - iov.iov_base = p->str; - iov.iov_len = p->length+1; - v.push_back(iov); - } - - -protected: - const std::string name; - const std::string address; - transport_t * transp; -}; - -class Node2: public ff_dnode { -public: - typedef zmqTransport::msg_t msg_t; - typedef COMM::TransportImpl transport_t; - - Node2(const std::string& name, const std::string& address, zmqTransport* const transp): - name(name),address(address),transp(transp) { - } - - int svc_init() { - // initializes dnode - return ff_dnode::init(name, address, 1, transp, RECEIVER); - } - - void * svc(void *task) { - mystring_t* s = (mystring_t*)task; - printf("Node2 received %s\n", s->str); - return task; - } - - // overriding the default prepare method - void prepare(svector*& v, size_t len,const int=-1) { - assert(len==2); - msgv.clear(); - msgv.reserve(len); - msgv.push_back(&msg1); - msgv.push_back(&msg2); - v=&msgv; - } -#if 0 - void unmarshalling(svector* const v[],const int vlen, - void *& task) { - assert(vlen==1 && v[0]->size()==2); - mystring_t* p =static_cast(v[0]->operator[](0)->getData()); - p->str = static_cast(v[0]->operator[](1)->getData()); - assert(strlen(p->str)== p->length); - task=p; - } -#endif - - void unmarshalling(svector* const v[],const int vlen, - void *& task) { - assert(vlen==1 && v[0]->size()==2); - mystring_t* p =static_cast(v[0]->operator[](0)->getData()); - p->str = static_cast(v[0]->operator[](1)->getData()); - assert(strlen(p->str)== p->length); - task=p; - } - -protected: - const std::string name; - const std::string address; - transport_t * transp; -private: - svector msgv; - msg_t msg1; - msg_t msg2; -}; - -class Node3: public ff_node { -public: - void *svc(void *task) { - return GO_ON; - } -}; - - - -int main(int argc, char * argv[]) { - if (argc != 4) { - std::cerr << "use: " << argv[0] << " name 1|0 host:port\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - char * address = argv[3]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)); - if (transport.initTransport()<0) abort(); - - if (atoi(P)) { - Node0 * n0 = new Node0; - Node1 * n1 = new Node1(name, address, &transport); - - ff_pipeline pipe; - pipe.add_stage(n0); - pipe.add_stage(n1); - pipe.run_and_wait_end(); - - delete n0; - delete n1; - } else { - Node2 * n2 = new Node2(name, address, &transport); - Node3 * n3 = new Node3; - - ff_pipeline pipe; - pipe.add_stage(n2); - pipe.add_stage(n3); - pipe.run_and_wait_end(); - delete n2; - delete n3; - } - // TODO: shutdown protocol - - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} diff --git a/tests/d/unicast.cpp b/tests/d/unicast.cpp deleted file mode 100644 index 40b625fd..00000000 --- a/tests/d/unicast.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -using namespace ff; - -typedef zmqTransportMsg_t msg_t; - -int main(int argc, char * argv[]) { - if (argc != 4) { - std::cerr << "use: " << argv[0] << " name 1|0 host:port\n"; - return -1; - } - - char * name = argv[1]; - char * P = argv[2]; // 1 producer 0 consumer - char * address = argv[3]; // no check - - // creates the network using 0mq as transport layer - zmqTransport transport(atoi(P)); - if (transport.initTransport()<0) abort(); - - UNICAST Unicast(new UNICAST_DESC(name,&transport,atoi(P))); - if (!Unicast.init(address)) abort(); - - msg_t msg; - if (atoi(P)) { - for(int i=0;i<100;++i) { - msg.init(new int(i),sizeof(int)); - Unicast.put(msg); - } - } else { - for(int i=0;i<100;++i) { - Unicast.get(msg); - printf("received %d\n", *static_cast(msg.getData())); - } - } - - Unicast.close(); - delete Unicast.getDescriptor(); - transport.closeTransport(); - std::cout << "done\n"; - return 0; -} From 2af877c1d4780f34d073367370c93ea4da2595af Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 12 Feb 2022 12:22:22 +0100 Subject: [PATCH 111/202] more cleaning of the tests/distributed directory --- tests/distributed/test_a2aOnDemand.cpp | 25 ++++++---- tests/distributed/test_a2aOnDemand2.cpp | 40 ++++++++++++---- .../{test_complete6.cpp => test_group13.cpp} | 23 ++++----- ...{test_complete6.json => test_group13.json} | 0 .../{test_complete7.cpp => test_group14.cpp} | 47 ++++++++++--------- ...{test_complete7.json => test_group14.json} | 0 .../{test_complete.cpp => test_group15.cpp} | 44 ++++++++--------- .../{test_complete.json => test_group15.json} | 0 .../{test_complete8.cpp => test_group16.cpp} | 11 +++-- ...{test_complete8.json => test_group16.json} | 0 .../{test_complete9.cpp => test_group17.cpp} | 32 +++++++++++-- ...{test_complete9.json => test_group17.json} | 0 tests/distributed/test_parametricPerf.cpp | 10 ++-- 13 files changed, 139 insertions(+), 93 deletions(-) rename tests/distributed/{test_complete6.cpp => test_group13.cpp} (81%) rename tests/distributed/{test_complete6.json => test_group13.json} (100%) rename tests/distributed/{test_complete7.cpp => test_group14.cpp} (72%) rename tests/distributed/{test_complete7.json => test_group14.json} (100%) rename tests/distributed/{test_complete.cpp => test_group15.cpp} (82%) rename tests/distributed/{test_complete.json => test_group15.json} (100%) rename tests/distributed/{test_complete8.cpp => test_group16.cpp} (93%) rename tests/distributed/{test_complete8.json => test_group16.json} (100%) rename tests/distributed/{test_complete9.cpp => test_group17.cpp} (70%) rename tests/distributed/{test_complete9.json => test_group17.json} (100%) diff --git a/tests/distributed/test_a2aOnDemand.cpp b/tests/distributed/test_a2aOnDemand.cpp index f5a8ef72..9aed3886 100644 --- a/tests/distributed/test_a2aOnDemand.cpp +++ b/tests/distributed/test_a2aOnDemand.cpp @@ -7,8 +7,14 @@ * |-> MoNode-->| * |--> MiNode * - * /<- pipe ->//<-------- a2a -------->/ + * /<-------- a2a -------->/ * /<----------- pipeMain ------------->/ + * + * distributed version: + * + * G1: MoNode + * G2: a2a + * */ @@ -123,21 +129,20 @@ int main(int argc, char*argv[]){ a2a.add_firstset(sxWorkers, 1); // enabling on-demand distribution policy a2a.add_secondset(dxWorkers); + //----- defining the distributed groups ------ - auto g0 = pipe.createGroup("G0"); + auto g0 = generator.createGroup("G0"); auto g1 = a2a.createGroup("G1"); auto g2 = a2a.createGroup("G2"); - g0.out << &generator; + for(int i = 0; i < numWorkerSx; i++) + g1 << sxWorkers[i]; + for(int i = 0; i < numWorkerDx; i++) + g2 << dxWorkers[i]; - for(int i = 0; i < numWorkerSx; i++) { - g1.in << sxWorkers[i]; - g1.out << sxWorkers[i]; - } - for(int i = 0; i < numWorkerDx; i++) { - g2.in << dxWorkers[i]; - } + // ------------------------------------------- + if (mainPipe.run_and_wait_end()<0) { error("running mainPipe\n"); return -1; diff --git a/tests/distributed/test_a2aOnDemand2.cpp b/tests/distributed/test_a2aOnDemand2.cpp index 3d7bbbd2..298cf0e3 100644 --- a/tests/distributed/test_a2aOnDemand2.cpp +++ b/tests/distributed/test_a2aOnDemand2.cpp @@ -1,10 +1,31 @@ +/* + * FastFlow concurrent network: + * + * |--> Sink1 + * Source1-->| + * |--> Sink2 + * Source2-->| + * |--> Sink3 + * + * + * distributed version: + * + * G1: all Source(s) + * G2: all Sink(s) + * + */ + + + #include #include #include #include -std::mutex mtx; +using namespace ff; +// ------------------------------------------------------ +std::mutex mtx; // used only for pretty printing static inline float active_delay(int msecs) { // read current time float x = 1.25f; @@ -19,6 +40,7 @@ static inline float active_delay(int msecs) { } return x; } +// ----------------------------------------------------- struct myTask_t { std::string str; @@ -31,7 +53,6 @@ struct myTask_t { void serialize(Archive & archive) { archive(str, S.t, S.f); } - }; struct Sink: ff::ff_minode_t{ @@ -87,7 +108,6 @@ int main(int argc, char*argv[]){ return -1; } - ff::ff_pipeline mainPipe; ff::ff_a2a a2a; auto g1 = a2a.createGroup("G1"); @@ -98,19 +118,19 @@ int main(int argc, char*argv[]){ for(int i = 0; i < numWorkerSx; i++) { sx.push_back(new Source(items)); - g1.out << sx[i]; + g1 << sx[i]; } - a2a.add_firstset(sx, asyncdegree); // enabling on-demand distribution policy with #asyncdegree buffer slots - for(int i = 0; i < numWorkerDx; i++){ dx.push_back(new Sink((long)100*(i+1))); - g2.in << dx[i]; + g2 << dx[i]; } + + // enabling on-demand distribution policy with #asyncdegree buffer slots + a2a.add_firstset(sx, asyncdegree); a2a.add_secondset(dx); - mainPipe.add_stage(&a2a); - if (mainPipe.run_and_wait_end()<0) { - error("running pipe"); + if (a2a.run_and_wait_end()<0) { + error("running a2a"); return -1; } diff --git a/tests/distributed/test_complete6.cpp b/tests/distributed/test_group13.cpp similarity index 81% rename from tests/distributed/test_complete6.cpp rename to tests/distributed/test_group13.cpp index 4e1064e1..eafbbb38 100644 --- a/tests/distributed/test_complete6.cpp +++ b/tests/distributed/test_group13.cpp @@ -5,8 +5,8 @@ * |-> Node22 -->| | * |-> Node33 -->| * - * /<-pipe0->//<--------- a2a0 ---------->//<- pipe1->/ - * /<-------------..------ pipe --------------------->/ + * /<--------- a2a0 ---------->/ + * /<--------------------- pipe --------------------->/ * * G1: pipe0 * G2: a2a0 @@ -91,29 +91,26 @@ int main(int argc, char*argv[]){ ntasks = std::stol(argv[1]); } - ff_pipeline pipe, pipe0, pipe1; + ff_pipeline pipe; Node1 n1(ntasks); Node2 n21, n22; Node3 n31, n32, n33; Node4 n4(ntasks); - pipe0.add_stage(&n1); - pipe1.add_stage(&n4); ff_a2a a2a; a2a.add_firstset({&n21, &n22}); a2a.add_secondset({&n31, &n32, &n33}); - pipe.add_stage(&pipe0); + pipe.add_stage(&n1); pipe.add_stage(&a2a); - pipe.add_stage(&pipe1); + pipe.add_stage(&n4); + + //----- defining the distributed groups ------ - auto G1 = pipe0.createGroup("G1"); + auto G1 = n1.createGroup("G1"); auto G2 = a2a.createGroup("G2"); - auto G3 = pipe1.createGroup("G3"); - + auto G3 = n4.createGroup("G3"); - /* ----------------- */ G1.out << &n1; - G2.in << &n21 << &n22; G2.out << &n31 << &n32 << &n33; - G3.in << &n4; /* -------------------------- */ + // ------------------------------------------- if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); diff --git a/tests/distributed/test_complete6.json b/tests/distributed/test_group13.json similarity index 100% rename from tests/distributed/test_complete6.json rename to tests/distributed/test_group13.json diff --git a/tests/distributed/test_complete7.cpp b/tests/distributed/test_group14.cpp similarity index 72% rename from tests/distributed/test_complete7.cpp rename to tests/distributed/test_group14.cpp index 93eb7c53..5b3205be 100644 --- a/tests/distributed/test_complete7.cpp +++ b/tests/distributed/test_group14.cpp @@ -1,14 +1,14 @@ /* * - * | -> Node3 -->| - * Node1--> -->| -> Node3 -->| -> Node4 - * | -> Node3 -->| - * /<-- pipe0-->/ /<--------- a2a -------->/ - * G1 G2 + * | -> Node2 -->| + * Node1--> -->| -> Node2 -->| -> Node3 + * | -> Node2 -->| + * /<--------- a2a -------->/ * /<----------------- pipe ---------------->/ * - * G1: pipe0 - * G2: a2a + * G1: Node1 + * G2: all Node2 + * G3: Node3 */ @@ -47,7 +47,7 @@ struct Node1: ff_monode_t{ const long ntasks; }; -struct Node3: ff_node_t{ +struct Node2: ff_node_t{ myTask_t* svc(myTask_t* t){ t->S.t += get_my_id(); t->S.f += get_my_id()*1.0; @@ -60,8 +60,8 @@ struct Node3: ff_node_t{ }; -struct Node4: ff_minode_t{ - Node4(long ntasks):ntasks(ntasks) {} +struct Node3: ff_minode_t{ + Node3(long ntasks):ntasks(ntasks) {} myTask_t* svc(myTask_t* t){ //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; ++processed; @@ -96,24 +96,25 @@ int main(int argc, char*argv[]){ ff_pipeline pipe; Node1 n1(ntasks); - Node3 n31, n32, n33; - Node4 n4(ntasks); - ff_pipeline pipe0; - pipe0.add_stage(&n1); + Node2 n21, n22, n23; + Node3 n3(ntasks); ff_a2a a2a; - a2a.add_firstset({&n31, &n32, &n33}); - a2a.add_secondset({&n4}); - pipe.add_stage(&pipe0); + a2a.add_firstset({&n21, &n22, &n23}); + a2a.add_secondset({&n3}); + pipe.add_stage(&n1); pipe.add_stage(&a2a); - - auto G1 = pipe0.createGroup("G1"); + + //----- defining the distributed groups ------ + + auto G1 = n1.createGroup("G1"); auto G2 = a2a.createGroup("G2"); auto G3 = a2a.createGroup("G3"); - G1.out << &n1; - G2.in << &n31 << &n32 << &n33; G2.out << &n31 << &n32 << &n33; - G3.in << &n4; - + G2 << &n21 << &n22 << &n23; + G3 << &n3; + + // ------------------------------------------- + if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); return -1; diff --git a/tests/distributed/test_complete7.json b/tests/distributed/test_group14.json similarity index 100% rename from tests/distributed/test_complete7.json rename to tests/distributed/test_group14.json diff --git a/tests/distributed/test_complete.cpp b/tests/distributed/test_group15.cpp similarity index 82% rename from tests/distributed/test_complete.cpp rename to tests/distributed/test_group15.cpp index 6b66d401..7b88e43b 100644 --- a/tests/distributed/test_complete.cpp +++ b/tests/distributed/test_group15.cpp @@ -8,14 +8,14 @@ * |--> MiNode -->| * * /<--------- a2a -------->/ - * /<------------ pipe1 -------------->/ /<- pipe2 ->/ + * /<------------- pipe -------------->/ * /<-------------------- pipeMain ------------------>/ * * * distributed version: * * -------- -------- - * | pipe1 | ----> | pipe2 | + * | pipe | ----> | Sink | * | | | | * -------- -------- * G1 G2 @@ -91,33 +91,29 @@ int main(int argc, char*argv[]){ } // defining the concurrent network - ff_pipeline mainPipe; - ff_a2a a2a; + ff_pipeline pipe; Source s; - ff_pipeline sp; - sp.add_stage(&s); - sp.add_stage(&a2a); - ff_pipeline sinkp; - Sink sink; - sinkp.add_stage(&sink); - mainPipe.add_stage(&sp); - mainPipe.add_stage(&sinkp); - + ff_a2a a2a; MoNode sx1, sx2, sx3; MiNode dx1, dx2, dx3; - a2a.add_firstset({&sx1, &sx2, &sx3}); a2a.add_secondset({&dx1, &dx2, &dx3}); - // ----------------------- - - // defining the distributed groups - dGroup g1 = sp.createGroup("G1"); - dGroup g3 = sinkp.createGroup("G2"); - - g1.out << &dx1 << &dx2 << &dx3; - g3.in << &sink; - // ---------------------- - + pipe.add_stage(&s); + pipe.add_stage(&a2a); + + Sink sink; + ff_pipeline mainPipe; + + mainPipe.add_stage(&pipe); + mainPipe.add_stage(&sink); + + //----- defining the distributed groups ------ + + auto g1 = pipe.createGroup("G1"); + auto g2 = sink.createGroup("G2"); + + // ------------------------------------------- + // running the distributed groups if (mainPipe.run_and_wait_end()<0) { error("running mainPipe\n"); diff --git a/tests/distributed/test_complete.json b/tests/distributed/test_group15.json similarity index 100% rename from tests/distributed/test_complete.json rename to tests/distributed/test_group15.json diff --git a/tests/distributed/test_complete8.cpp b/tests/distributed/test_group16.cpp similarity index 93% rename from tests/distributed/test_complete8.cpp rename to tests/distributed/test_group16.cpp index a92d904e..c7a7017e 100644 --- a/tests/distributed/test_complete8.cpp +++ b/tests/distributed/test_group16.cpp @@ -1,6 +1,6 @@ /* * - * Node1-->Node2 --> Node3 --> Node4 + * Node1--->Node2 ---> Node3 ---> Node4 * * /<-- pipe0-->//<-- pipe1 -->//<-pipe2->/ * /<----------- pipe ------------>/ @@ -107,14 +107,15 @@ int main(int argc, char*argv[]){ pipe.add_stage(&pipe0); pipe.add_stage(&pipe1); pipe.add_stage(&pipe2); + + //----- defining the distributed groups ------ auto G1 = pipe0.createGroup("G1"); auto G2 = pipe1.createGroup("G2"); auto G3 = pipe2.createGroup("G3"); - - G1.out << &n2; - G2.in << &n3; G2.out << &n3; - G3.in << &n4; + + // ------------------------------------------- + if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); diff --git a/tests/distributed/test_complete8.json b/tests/distributed/test_group16.json similarity index 100% rename from tests/distributed/test_complete8.json rename to tests/distributed/test_group16.json diff --git a/tests/distributed/test_complete9.cpp b/tests/distributed/test_group17.cpp similarity index 70% rename from tests/distributed/test_complete9.cpp rename to tests/distributed/test_group17.cpp index 45140456..e6b69b44 100644 --- a/tests/distributed/test_complete9.cpp +++ b/tests/distributed/test_group17.cpp @@ -1,3 +1,23 @@ +/* + * FastFlow concurrent network: + * + * + * ----------------------------- + * | |--> Sink1 --> | + * | Source1-->| | + * | |--> Sink2 --> | + * | Source2-->| | + * | |--> Sink3 --> | + * ----------------------------- + * + * distributed version: + * + * G1: all Source(s) + * G2: all Sink(s) + * + */ + + #include #include #include @@ -45,18 +65,22 @@ int main(int argc, char*argv[]){ for(int i = 0 ; i < 3; i++){ auto s = new Source(4, i); firstSet.push_back(s); - g1.out << s; + g1 << s; } for(int i = 0; i < 4; i++){ auto s = new Sink(i); secondSet.push_back(s); - g2.in << s; + g2 << s; } a2a.add_firstset(firstSet); a2a.add_secondset(secondSet); ff_Pipe p(&a2a); - p.run_and_wait_end(); -} \ No newline at end of file + if (p.run_and_wait_end()<0) { + error("running pipe"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_complete9.json b/tests/distributed/test_group17.json similarity index 100% rename from tests/distributed/test_complete9.json rename to tests/distributed/test_group17.json diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 89b3530f..94c63cfd 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -8,7 +8,10 @@ * |--> MiNode * * /<------- a2a ------>/ - * /<---- pipeMain ---->/ + * + * S: all left-hand side nodes + * D: all righ-hand side nodes + * */ @@ -175,7 +178,6 @@ int main(int argc, char*argv[]){ if ((p=getenv("CHECK_DATA"))!=nullptr) check=true; printf("chackdata = %s\n", p); - ff_pipeline mainPipe; ff::ff_a2a a2a; mainPipe.add_stage(&a2a); @@ -195,14 +197,14 @@ int main(int argc, char*argv[]){ for(int i = 0; i < numProcSx; i++){ auto& g = a2a.createGroup(std::string("S")+std::to_string(i)); for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ - g.out << sxWorkers[j]; + g << sxWorkers[j]; } } for(int i = 0; i < numProcDx; i++){ auto& g = a2a.createGroup(std::string("D")+std::to_string(i)); for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ - g.in << dxWorkers[j]; + g << dxWorkers[j]; } } From 03ea66b05d44dc383844b9aa0f672a1f9d6d4d34 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Mon, 21 Feb 2022 12:50:49 +0100 Subject: [PATCH 112/202] Non working commit for debugging purpose. --- ff/distributed/ff_dgroup2.hpp | 179 ++++++++++++++++++++++++++++ ff/distributed/ff_dgroups.hpp | 29 ++++- ff/distributed/ff_dintermediate.hpp | 6 +- ff/distributed/ff_dreceiver.hpp | 9 +- ff/distributed/ff_dsender.hpp | 24 ++-- ff/node.hpp | 7 +- tests/distributed/test_group17.cpp | 8 ++ 7 files changed, 239 insertions(+), 23 deletions(-) create mode 100644 ff/distributed/ff_dgroup2.hpp diff --git a/ff/distributed/ff_dgroup2.hpp b/ff/distributed/ff_dgroup2.hpp new file mode 100644 index 00000000..8a6c4255 --- /dev/null +++ b/ff/distributed/ff_dgroup2.hpp @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include + +#ifdef DFF_MPI +#include +#include +#endif + + +template +T getBackAndPop(std::vector v){ + T b = v.back(); + v.pop_back(); + return b; +} + +namespace ff{ +class dGroup2 : public ff::ff_farm { + + static inline std::unordered_map vector2UMap(const std::vector v){ + std::unordered_map output; + for(int i = 0; i < v.size(); i++) output[v[i]] = i; + return output; + } + + static inline std::map vector2Map(const std::vector v){ + std::map output; + for(int i = 0; i < v.size(); i++) output[v[i]] = i; + return output; + } + + struct ForwarderNode : ff_node { + ForwarderNode(std::function f){ + this->serializeF = f; + } + ForwarderNode(std::function f){ + this->deserializeF = f; + } + void* svc(void* input){return input;} + }; + +public: + dGroup2(ff_IR& ir){ + + if (ir.isVertical()){ + std::vector reverseOutputIndexes((ir.hasLeftChildren() ? ir.outputL : ir.outputR).rbegin(), (ir.hasLeftChildren() ? ir.outputL : ir.outputR).rend()); + for(ff_node* child: (ir.hasLeftChildren() ? ir.L : ir.R)){ + if (isSeq(child)){ + if (ir.hasReceiver && ir.hasSender) + workers.push_back(new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes))); + else if (ir.hasReceiver) + workers.push_back(new WrapperIN(child)); + else workers.push_back(new WrapperOUT(child, getBackAndPop(reverseOutputIndexes))); + + } else { + if (ir.hasReceiver){ + ff::svector inputs; child->get_in_nodes(inputs); + for(ff_node* input : inputs){ + ff_node* inputParent = getBB(child, input); + if (inputParent) inputParent->change_node(input, new WrapperIN(input), true); //cleanup?? removefromcleanuplist?? + } + } + + if (ir.hasSender){ + ff::svector outputs; child->get_out_nodes(outputs); + for(ff_node* output : outputs){ + ff_node* outputParent = getBB(child, output); + if (outputParent) outputParent->change_node(output, new WrapperOUT(output, getBackAndPop(reverseOutputIndexes)), true); // cleanup?? removefromcleanuplist?? + } + } + + workers.push_back(child); + } + } + + if (!ir.hasReceiver) + this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL))); + + if (!ir.hasSender) + this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName), true); + } + else { // the group is horizontal! + ff_a2a* innerA2A = new ff_a2a(); + + std::vector reverseLeftOutputIndexes(ir.outputL.rbegin(), ir.outputL.rend()); + + std::unordered_map localRightWorkers = vector2UMap(ir.inputR); + std::vector firstSet; + for(ff_node* child : ir.L){ + if (isSeq(child)) + if (ir.isSource){ + ff_node* wrapped = new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers); + wrapped->skipallpop(true); + firstSet.push_back(wrapped); + } else { + firstSet.push_back(new ff_comb(new WrapperIN(new ForwarderNode(child->getDeserializationFunction()), 1, true), new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers), true, true)); + } + else { + + ff::svector inputs; child->get_in_nodes(inputs); + for(ff_node* input : inputs){ + if (ir.isSource) + input->skipallpop(true); + else { + ff_node* inputParent = getBB(child, input); + if (inputParent) inputParent->change_node(input, new WrapperIN(input, 1), true); // cleanup??? remove_fromcleanuplist?? + } + } + + ff::svector outputs; child->get_out_nodes(outputs); + for(ff_node* output : outputs){ + ff_node* outputParent = getBB(child, output); + if (outputParent) outputParent->change_node(output, new EmitterAdapter(output, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers) , true); // cleanup??? remove_fromcleanuplist?? + } + firstSet.push_back(child); //ondemand?? cleanup?? + } + } + // add the Square Box Left, just if we have a receiver! + if (ir.hasReceiver) + firstSet.push_back(new SquareBoxLeft(vector2UMap(ir.inputR))); // ondemand?? + + innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? + + + std::vector reverseRightOutputIndexes(ir.outputR.rbegin(), ir.outputR.rend()); + std::vector secondSet; + for(ff_node* child : ir.R){ + if (isSeq(child)) + secondSet.push_back( + (ir.isSink) ? (ff_node*)new CollectorAdapter(child, ir.outputL) + : (ff_node*)new ff_comb(new CollectorAdapter(child, ir.outputL), new WrapperOUT(new ForwarderNode(child->getSerializationFunction()), getBackAndPop(reverseRightOutputIndexes), 1, true), true, true) + ); + else { + ff::svector inputs; child->get_in_nodes(inputs); + for(ff_node* input : inputs){ + ff_node* inputParent = getBB(child, input); + if (inputParent) inputParent->change_node(input, new CollectorAdapter(input, ir.outputL), true); //cleanup?? remove_fromcleanuplist?? + } + + if (!ir.isSink){ + ff::svector outputs; child->get_out_nodes(outputs); + for(ff_node* output : outputs){ + ff_node* outputParent = getBB(child, output); + if (outputParent) outputParent->change_node(output, new WrapperOUT(output, getBackAndPop(reverseRightOutputIndexes)), true); //cleanup?? removefromcleanuplist? + } + } + + secondSet.push_back(child); + } + } + + // add the SQuareBox Right, iif there is a sender! + if (ir.hasSender) + secondSet.push_back(new SquareBoxRight); + + innerA2A->add_secondset(secondSet); // cleanup?? + workers.push_back(innerA2A); + + + if (ir.hasReceiver) + this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); + + if (ir.hasSender) + this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB) , true); + } + + if (this->getNWorkers() == 0){ + std::cerr << "The farm implementing the distributed group is empty! There might be an error! :(\n"; + abort(); + } + } + + +}; +} \ No newline at end of file diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index c789a640..e252c548 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -97,8 +98,6 @@ class dGroups { const std::string& getRunningGroup() const { return runningGroup; } void forceProtocol(Proto p){this->usedProtocol = p;} - - bool isBuildByMyBuildingBlock(const std::string gName); int run_and_wait_end(ff_pipeline* parent){ if (annotatedGroups.find(runningGroup) == annotatedGroups.end()){ @@ -110,10 +109,21 @@ class dGroups { // qui dovrei creare la rappresentazione intermedia di tutti this->prepareIR(parent); + this->annotatedGroups[this->runningGroup].print(); + // buildare il farm dalla rappresentazione intermedia del gruppo che devo rannare - + dGroup2 _grp(this->annotatedGroups[this->runningGroup]); // rannere il farm come sotto! + if (_grp.run() < 0){ + std::cerr << "Error running the group!" << std::endl; + return -1; + } + if (_grp.wait() < 0){ + std::cerr << "Error waiting the group!" << std::endl; + return -1; + } + //ff_node* runningGroup = this->groups[this->runningGroup]; //if (runningGroup->run(parent) < 0) return -1; @@ -271,9 +281,16 @@ class dGroups { std::cerr << "Some building block has not been annotated and no coverage found! You missed something. Aborting now" << std::endl; abort(); } - } else // compute the coverage anyway - for(const std::string& gName : pair.second) annotatedGroups[gName].computeCoverage(); + } else + for(const std::string& gName : pair.second) { + annotatedGroups[gName].computeCoverage(); + // compute the coverage anyway + } + // set the isSrc and isSink fields + runningGroup_IR.isSink = isSnk; runningGroup_IR.isSource = isSrc; + // populate the set with the names of other groups created from this 1st level BB + runningGroup_IR.otherGroupsFromSameParentBB = pair.second; } //############# compute the number of excpected input connections @@ -313,7 +330,7 @@ class dGroups { runningGroup_IR.buildIndexes(); - runningGroup_IR.print(); + //runningGroup_IR.print(); } std::set outputGroups(std::set groupNames){ diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index f269d848..29738681 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -76,11 +76,15 @@ class ff_IR { ff_endpoint listenEndpoint; std::vector destinationEndpoints; - + std::set otherGroupsFromSameParentBB; size_t expectedEOS = 0; // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table std::vector inputL, outputL, inputR, outputR; + + // TODO: implmentare l'assegnamento di questi campi + int leftTotalOuputs; + int rightTotalInputs; bool isVertical(){return (L.empty() + R.empty()) == 1;} diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index e9d7d243..d2d612f1 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -173,7 +173,7 @@ class ff_dreceiver: public ff_monode_t { error("Error listening\n"); return -1; } - + return 0; } @@ -269,6 +269,7 @@ class ff_dreceiverH : public ff_dreceiver { std::vector internalDestinations; std::map isInternalConnection; + std::set internalGroupsNames; int internalNEos = 0; void registerEOS(int sck){ @@ -295,7 +296,7 @@ class ff_dreceiverH : public ff_dreceiver { error("Error reading from socket groupName\n"); return -1; } - bool internalGroup = dGroups::Instance()->isBuildByMyBuildingBlock(std::string(groupName,size)); + bool internalGroup = internalGroupsNames.contains(std::string(groupName,size)); isInternalConnection[sck] = internalGroup; // save somewhere the fact that this sck represent an internal connection @@ -312,8 +313,8 @@ class ff_dreceiverH : public ff_dreceiver { } public: - ff_dreceiverH(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {{0,0}}, std::vector internalRoutingTable = {0}, int coreid=-1) - : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid), internalDestinations(internalRoutingTable) { + ff_dreceiverH(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {{0,0}}, std::vector internalRoutingTable = {0}, std::set internalGroups = {}, int coreid=-1) + : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid), internalDestinations(internalRoutingTable), internalGroupsNames(internalGroups) { } diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 217eb54b..5ae423d8 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -31,6 +31,7 @@ class ff_dsender: public ff_minode_t { //std::unordered_map> type2sck; std::vector sockets; //int internalGateways; + std::string gName; int coreid; int receiveReachableDestinations(int sck, std::map& m){ @@ -74,7 +75,6 @@ class ff_dsender: public ff_minode_t { } int sendGroupName(const int sck){ - std::string gName = dGroups::Instance()->getRunningGroup(); size_t sz = htobe64(gName.size()); struct iovec iov[2]; iov[0].iov_base = &sz; @@ -193,11 +193,11 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(ff_endpoint dest_endpoint, int coreid=-1): coreid(coreid) { + ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int coreid=-1): gName(gName), coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector dest_endpoints_, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), coreid(coreid) {} + ff_dsender( std::vector dest_endpoints_, std::string gName = "", int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), coreid(coreid) {} @@ -227,7 +227,7 @@ class ff_dsender: public ff_minode_t { next_rr_destination = (next_rr_destination + 1) % dest2Socket.size(); } - + std::cout << "Sender sending out a task!\n"; sendToSck(dest2Socket[task->chid], task); delete task; return this->GO_ON; @@ -248,11 +248,12 @@ class ff_dsenderH : public ff_dsender { std::map internalDest2Socket; std::map::const_iterator rr_iterator; std::vector internalSockets; + std::set internalGroupNames; public: - ff_dsenderH(ff_endpoint e, int coreid=-1) : ff_dsender(e, coreid) {} - ff_dsenderH(std::vector dest_endpoints_, int coreid=-1) : ff_dsender(dest_endpoints_, coreid) {} + ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int coreid=-1) : ff_dsender(e, gName, coreid), internalGroupNames(internalGroups) {} + ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int coreid=-1) : ff_dsender(dest_endpoints_, gName, coreid), internalGroupNames(internalGroups) {} int handshakeHandler(const int sck, bool isInternal){ if (sendGroupName(sck) < 0) return -1; @@ -267,13 +268,14 @@ class ff_dsenderH : public ff_dsender { int sck = tryConnect(endpoint); if (sck <= 0) return -1; - bool isInternal = dGroups::Instance()->isBuildByMyBuildingBlock(endpoint.groupName); + bool isInternal = internalGroupNames.contains(endpoint.groupName); if (isInternal) internalSockets.push_back(sck); else sockets.push_back(sck); handshakeHandler(sck, isInternal); } rr_iterator = internalDest2Socket.cbegin(); + std::cout << "SenderH started correctly!\n"; return 0; } @@ -324,11 +326,11 @@ class ff_dsenderOD: public ff_dsender { public: - ff_dsenderOD(ff_endpoint dest_endpoint, int queueDim = 1, int coreid=-1) - : ff_dsender(dest_endpoint, coreid), queueDim(queueDim) {} + ff_dsenderOD(ff_endpoint dest_endpoint, int queueDim = 1, std::string gName = "", int coreid=-1) + : ff_dsender(dest_endpoint, gName, coreid), queueDim(queueDim) {} - ff_dsenderOD(std::vector dest_endpoints_, int queueDim = 1, int coreid=-1) - : ff_dsender(dest_endpoints_, coreid), queueDim(queueDim) {} + ff_dsenderOD(std::vector dest_endpoints_, int queueDim = 1, std::string gName = "", int coreid=-1) + : ff_dsender(dest_endpoints_, gName, coreid), queueDim(queueDim) {} int svc_init() { if (coreid!=-1) diff --git a/ff/node.hpp b/ff/node.hpp index 0df0570f..4e66b88f 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -509,8 +509,12 @@ class ff_node { friend class ff_comb; friend struct internal_mo_transformer; friend struct internal_mi_transformer; + +#ifdef DFF_ENABLED friend class dGroups; - + friend class dGroup2; +#endif + private: FFBUFFER * in; ///< Input buffer, built upon SWSR lock-free (wait-free) ///< (un)bounded FIFO queue @@ -1015,6 +1019,7 @@ class ff_node { */ inline size_t getOSThreadId() const { if (thread) return thread->getOSThreadId(); return 0; } + virtual bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) {} #if defined(FF_TASK_CALLBACK) virtual void callbackIn(void * =NULL) { } diff --git a/tests/distributed/test_group17.cpp b/tests/distributed/test_group17.cpp index e6b69b44..d3c73cdb 100644 --- a/tests/distributed/test_group17.cpp +++ b/tests/distributed/test_group17.cpp @@ -29,8 +29,15 @@ std::mutex mtx; struct Source : ff_monode_t{ int numWorker, generatorID; Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + int svc_init(){ + std::cout << "Source init called!\n"; + return 0; + } + std::string* svc(std::string* in){ + std::cout << "Entering the loop of Source\n"; for(int i = 0; i < numWorker; i++) ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); return EOS; @@ -41,6 +48,7 @@ struct Source : ff_monode_t{ struct Sink : ff_minode_t{ int sinkID; Sink(int id): sinkID(id) {} + std::string* svc(std::string* in){ const std::lock_guard lock(mtx); ff::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << ff::endl; From e59b3ecd5a8d6c86732481207eb4e15c30042adc Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Mon, 21 Feb 2022 17:32:53 +0100 Subject: [PATCH 113/202] Fixed some bugs. Added the possibility to print the intermediate representation if and only if the program is compiled with the macro PRINT_IR defined --- ff/distributed/ff_dgroup2.hpp | 19 ++++++++++++------- ff/distributed/ff_dgroups.hpp | 4 +++- ff/distributed/ff_dsender.hpp | 1 - ff/distributed/ff_wrappers.hpp | 4 ++-- tests/distributed/Makefile | 3 +++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ff/distributed/ff_dgroup2.hpp b/ff/distributed/ff_dgroup2.hpp index 8a6c4255..082eb2be 100644 --- a/ff/distributed/ff_dgroup2.hpp +++ b/ff/distributed/ff_dgroup2.hpp @@ -12,7 +12,7 @@ template -T getBackAndPop(std::vector v){ +T getBackAndPop(std::vector& v){ T b = v.back(); v.pop_back(); return b; @@ -47,8 +47,15 @@ class dGroup2 : public ff::ff_farm { dGroup2(ff_IR& ir){ if (ir.isVertical()){ - std::vector reverseOutputIndexes((ir.hasLeftChildren() ? ir.outputL : ir.outputR).rbegin(), (ir.hasLeftChildren() ? ir.outputL : ir.outputR).rend()); + std::vector reverseOutputIndexes(ir.hasLeftChildren() ? ir.outputL.rbegin() : ir.outputR.rbegin(), ir.hasLeftChildren() ? ir.outputL.rend() : ir.outputR.rend()); for(ff_node* child: (ir.hasLeftChildren() ? ir.L : ir.R)){ + ff::svector inputs; child->get_in_nodes(inputs); + ff::svector outputs; child->get_out_nodes(outputs); + + // handle the case we have a pipe (or more nested) with just one sequential stage (not a combine) + if (inputs.size() == 1 && outputs.size() == 1 && inputs[0] == outputs[0]) + child = inputs[0]; + if (isSeq(child)){ if (ir.hasReceiver && ir.hasSender) workers.push_back(new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes))); @@ -58,7 +65,6 @@ class dGroup2 : public ff::ff_farm { } else { if (ir.hasReceiver){ - ff::svector inputs; child->get_in_nodes(inputs); for(ff_node* input : inputs){ ff_node* inputParent = getBB(child, input); if (inputParent) inputParent->change_node(input, new WrapperIN(input), true); //cleanup?? removefromcleanuplist?? @@ -66,7 +72,6 @@ class dGroup2 : public ff::ff_farm { } if (ir.hasSender){ - ff::svector outputs; child->get_out_nodes(outputs); for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); if (outputParent) outputParent->change_node(output, new WrapperOUT(output, getBackAndPop(reverseOutputIndexes)), true); // cleanup?? removefromcleanuplist?? @@ -77,10 +82,10 @@ class dGroup2 : public ff::ff_farm { } } - if (!ir.hasReceiver) - this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL))); + if (ir.hasReceiver) + this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); - if (!ir.hasSender) + if (ir.hasSender) this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName), true); } else { // the group is horizontal! diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index e252c548..4f00ec05 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -109,8 +109,10 @@ class dGroups { // qui dovrei creare la rappresentazione intermedia di tutti this->prepareIR(parent); +#ifdef PRINT_IR this->annotatedGroups[this->runningGroup].print(); - +#endif + // buildare il farm dalla rappresentazione intermedia del gruppo che devo rannare dGroup2 _grp(this->annotatedGroups[this->runningGroup]); // rannere il farm come sotto! diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 5ae423d8..51d5e9b7 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -227,7 +227,6 @@ class ff_dsender: public ff_minode_t { next_rr_destination = (next_rr_destination + 1) % dest2Socket.size(); } - std::cout << "Sender sending out a task!\n"; sendToSck(dest2Socket[task->chid], task); delete task; return this->GO_ON; diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index d1ac0711..417773dd 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -80,10 +80,10 @@ class WrapperOUT: public internal_mo_transformer { bool serialize(void* in, int id) { if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); - + message_t* msg = new message_t; - + this->n->serializeF(in, msg->data); msg->sender = myID; // da cambiare con qualcosa di reale! msg->chid = id; diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index b443ecc3..485976f8 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -32,6 +32,9 @@ endif ifdef EXCLUDE_BLOCKING CXXFLAGS += -DDFF_EXCLUDE_BLOCKING endif +ifdef PRINT_IR + CXXFLAGS += -DPRINT_IR +endif ifdef EXCLUDE_MPI CXXFLAGS += -DDFF_EXCLUDE_MPI else From bf7e469d86aa016bb933342a1f9225184b84da19 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Tue, 22 Feb 2022 18:43:36 +0100 Subject: [PATCH 114/202] Bug fixes and minor changes --- ff/distributed/ff_dgroup2.hpp | 2 +- ff/distributed/ff_dgroups.hpp | 18 +++++++++++++++++- ff/distributed/ff_dintermediate.hpp | 2 +- ff/distributed/ff_dreceiver.hpp | 19 +++++++++++++------ ff/distributed/ff_dsender.hpp | 12 +++++++----- tests/distributed/test_group6.cpp | 2 +- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/ff/distributed/ff_dgroup2.hpp b/ff/distributed/ff_dgroup2.hpp index 082eb2be..821e6394 100644 --- a/ff/distributed/ff_dgroup2.hpp +++ b/ff/distributed/ff_dgroup2.hpp @@ -126,7 +126,7 @@ class dGroup2 : public ff::ff_farm { } // add the Square Box Left, just if we have a receiver! if (ir.hasReceiver) - firstSet.push_back(new SquareBoxLeft(vector2UMap(ir.inputR))); // ondemand?? + firstSet.push_back(new SquareBoxLeft(localRightWorkers)); // ondemand?? innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 4f00ec05..a9194037 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -293,6 +293,22 @@ class dGroups { runningGroup_IR.isSink = isSnk; runningGroup_IR.isSource = isSrc; // populate the set with the names of other groups created from this 1st level BB runningGroup_IR.otherGroupsFromSameParentBB = pair.second; + + } + + // + if (!runningGroup_IR.isVertical()){ + ff_a2a* parentA2A = reinterpret_cast(runningGroup_IR.parentBB); + { + ff::svector inputs; + for(ff_node* child : parentA2A->getSecondSet()) child->get_in_nodes(inputs); + runningGroup_IR.rightTotalInputs = inputs.size(); + } + { + ff::svector outputs; + for(ff_node* child : parentA2A->getFirstSet()) child->get_out_nodes(outputs); + runningGroup_IR.leftTotalOuputs = outputs.size(); + } } //############# compute the number of excpected input connections @@ -309,7 +325,7 @@ class dGroups { if (runningGroup_IR.expectedEOS > 0) runningGroup_IR.hasReceiver = true; //############ compute the name of the outgoing connection groups - if (runningGroup_IR.parentBB->isAll2All() && runningGroup_IR.isVertical() && runningGroup_IR.hasLeftChildren()){ + if (runningGroup_IR.parentBB->isAll2All() && runningGroup_IR.isVertical() && runningGroup_IR.hasLeftChildren() && !runningGroup_IR.wholeParent){ // inserisci tutte i gruppi di questo bb a destra for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) if (!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasRightChildren()) diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index 29738681..a609a2c3 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -11,9 +11,9 @@ namespace ff { class ff_IR { friend class dGroups; +protected: // set to true if the group contains the whole parent building block bool wholeParent = false; -protected: void computeCoverage(){ if (!parentBB->isAll2All()) return; ff_a2a* a2a = reinterpret_cast(parentBB); diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index d2d612f1..a00086d0 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -63,7 +63,7 @@ class ff_dreceiver: public ff_monode_t { return this->sendRoutingTable(sck, reachableDestinations); } - void registerEOS(int sck){ + virtual void registerEOS(int sck){ /*switch(connectionsTypes[sck]){ case ConnectionType::EXTERNAL: if (++Eneos == Einput_channels) @@ -270,15 +270,22 @@ class ff_dreceiverH : public ff_dreceiver { std::vector internalDestinations; std::map isInternalConnection; std::set internalGroupsNames; - int internalNEos = 0; + int internalNEos = 0, externalNEos = 0; void registerEOS(int sck){ - if (isInternalConnection[sck] && ++internalNEos == std::count_if(std::begin(isInternalConnection), - std::end (isInternalConnection), - [](std::pair const &p) {return p.second;})) + neos++; + int internalConn = std::count_if(std::begin(isInternalConnection), + std::end (isInternalConnection), + [](std::pair const &p) {return p.second;}); + + if (!isInternalConnection[sck]){ + if (++externalNEos == (isInternalConnection.size()-internalConn)) + for(int i = 0; i < 1; i++) ff_send_out_to(this->EOS, i); + } else + if (++internalNEos == internalConn) ff_send_out_to(this->EOS, this->get_num_outchannels()-1); - neos++; + } virtual int handshakeHandler(const int sck){ diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 51d5e9b7..7d8d56b3 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -49,7 +49,7 @@ class ff_dsender: public ff_minode_t { sz = be64toh(sz); - std::cout << "Receiving routing table (" << sz << " bytes)" << std::endl; + char* buff = new char [sz]; assert(buff); @@ -71,6 +71,7 @@ class ff_dsender: public ff_minode_t { /*for (const auto& p : m) std::cout << p.first << " - " << p.second << std::endl; */ + ff::cout << "Receiving routing table (" << sz << " bytes)" << ff::endl; return 0; } @@ -261,12 +262,14 @@ class ff_dsenderH : public ff_dsender { } int svc_init() { - + sockets.resize(this->dest_endpoints.size()); for(const auto& endpoint : this->dest_endpoints){ int sck = tryConnect(endpoint); - if (sck <= 0) return -1; - + if (sck <= 0) { + error("Error on connecting!\n"); + return -1; + } bool isInternal = internalGroupNames.contains(endpoint.groupName); if (isInternal) internalSockets.push_back(sck); else sockets.push_back(sck); @@ -274,7 +277,6 @@ class ff_dsenderH : public ff_dsender { } rr_iterator = internalDest2Socket.cbegin(); - std::cout << "SenderH started correctly!\n"; return 0; } diff --git a/tests/distributed/test_group6.cpp b/tests/distributed/test_group6.cpp index 8eb8af1a..ca0733d8 100644 --- a/tests/distributed/test_group6.cpp +++ b/tests/distributed/test_group6.cpp @@ -127,7 +127,7 @@ int main(int argc, char*argv[]){ //----- defining the distributed groups ------ auto g1 = a2a1.createGroup("G1"); - auto g2 = a2a1.createGroup("G2"); + auto g2 = a2a2.createGroup("G2"); // ------------------------------------------- From 67159a59216543994edbcf4eae15915778544f50 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 23 Feb 2022 18:03:13 +0100 Subject: [PATCH 115/202] Bug fixes --- ff/distributed/ff_dgroup2.hpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/ff/distributed/ff_dgroup2.hpp b/ff/distributed/ff_dgroup2.hpp index 821e6394..85d4e452 100644 --- a/ff/distributed/ff_dgroup2.hpp +++ b/ff/distributed/ff_dgroup2.hpp @@ -43,6 +43,16 @@ class dGroup2 : public ff::ff_farm { void* svc(void* input){return input;} }; + static ff_node* buildWrapperIN(ff_node* n){ + if (n->isMultiOutput()) return new ff_comb(new WrapperIN(new ForwarderNode(n->deserializeF)), n, true, false); + return new WrapperIN(n); + } + + static ff_node* buildWrapperOUT(ff_node* n, int id){ + if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF), id, 1, true), false, true); + return new WrapperOUT(n, id); + } + public: dGroup2(ff_IR& ir){ @@ -60,21 +70,21 @@ class dGroup2 : public ff::ff_farm { if (ir.hasReceiver && ir.hasSender) workers.push_back(new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes))); else if (ir.hasReceiver) - workers.push_back(new WrapperIN(child)); - else workers.push_back(new WrapperOUT(child, getBackAndPop(reverseOutputIndexes))); + workers.push_back(buildWrapperIN(child)); + else workers.push_back(buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes))); } else { if (ir.hasReceiver){ for(ff_node* input : inputs){ ff_node* inputParent = getBB(child, input); - if (inputParent) inputParent->change_node(input, new WrapperIN(input), true); //cleanup?? removefromcleanuplist?? + if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); //cleanup?? removefromcleanuplist?? } } if (ir.hasSender){ for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, new WrapperOUT(output, getBackAndPop(reverseOutputIndexes)), true); // cleanup?? removefromcleanuplist?? + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes)), true); // cleanup?? removefromcleanuplist?? } } @@ -112,7 +122,7 @@ class dGroup2 : public ff::ff_farm { input->skipallpop(true); else { ff_node* inputParent = getBB(child, input); - if (inputParent) inputParent->change_node(input, new WrapperIN(input, 1), true); // cleanup??? remove_fromcleanuplist?? + if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); // cleanup??? remove_fromcleanuplist?? } } @@ -128,6 +138,10 @@ class dGroup2 : public ff::ff_farm { if (ir.hasReceiver) firstSet.push_back(new SquareBoxLeft(localRightWorkers)); // ondemand?? + std::transform(firstSet.begin(), firstSet.end(), firstSet.begin(), [](ff_node* n) -> ff_node* { + if (!n->isPipe()) return new ff_Pipe(n); return n; + }); + innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? @@ -150,7 +164,7 @@ class dGroup2 : public ff::ff_farm { ff::svector outputs; child->get_out_nodes(outputs); for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, new WrapperOUT(output, getBackAndPop(reverseRightOutputIndexes)), true); //cleanup?? removefromcleanuplist? + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseRightOutputIndexes)), true); //cleanup?? removefromcleanuplist? } } @@ -162,6 +176,10 @@ class dGroup2 : public ff::ff_farm { if (ir.hasSender) secondSet.push_back(new SquareBoxRight); + std::transform(secondSet.begin(), secondSet.end(), secondSet.begin(), [](ff_node* n) -> ff_node* { + if (!n->isPipe()) return new ff_Pipe(n); return n; + }); + innerA2A->add_secondset(secondSet); // cleanup?? workers.push_back(innerA2A); From e07f9d6b2d74c7d880740c72f1ec950dc1745067 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 26 Feb 2022 19:45:41 +0100 Subject: [PATCH 116/202] fixed a problem occurring when an all2all or a farm without collector are used as workers of a farm; added two basic tests --- ff/all2all.hpp | 38 +++++--- tests/Makefile | 2 +- tests/test_farm+A2A3.cpp | 180 +++++++++++++++++++++++++++++++++++ tests/test_farm+A2A4.cpp | 201 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 408 insertions(+), 13 deletions(-) create mode 100644 tests/test_farm+A2A3.cpp create mode 100644 tests/test_farm+A2A4.cpp diff --git a/ff/all2all.hpp b/ff/all2all.hpp index bd930b6a..13d5e3a4 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -205,18 +205,30 @@ class ff_a2a: public ff_node { } } if (outputNodes.size()) { - if (outputNodes.size() != workers2.size()) { - error("A2A, prepare, invalid state\n"); + + svector w; + for(size_t i=0;iget_out_nodes(w); + + if (outputNodes.size() != w.size()) { + error("A2A, prepare, invalid state detected\n"); return -1; } + + for(size_t i=0;iisMultiOutput()) { + error("A2A, prepare, invalid state, unexpected multi-output node\n"); + return -1; + } + + assert(outputNodes[i]->get_in_buffer() != nullptr); + if (w[i]->set_output_buffer(outputNodes[i]->get_in_buffer()) < 0) { + error("A2A, prepare, invalid state, setting output buffer\n"); + return -1; + } + } } - for(size_t i=0;iisMultiOutput()); - assert(outputNodes[i]->get_in_buffer() != nullptr); - assert(workers2[i]->get_out_buffer() == nullptr); - if (workers2[i]->set_output_buffer(outputNodes[i]->get_in_buffer()) <0) - return -1; - } + // blocking stuff -------------------------------------------- @@ -499,13 +511,11 @@ class ff_a2a: public ff_node { int numThreads() const { return cardinality(); } int set_output(const svector & w) { - if (outputNodes.size()+w.size() > workers2.size()) return -1; outputNodes +=w; return 0; } int set_output(ff_node *node) { - if (outputNodes.size()+1 > workers2.size()) return -1; outputNodes.push_back(node); return 0; } @@ -725,7 +735,11 @@ class ff_a2a: public ff_node { ff_node* t = new ff_buffernode(nentries,fixedsize); t->set_id(j); internalSupportNodes.push_back(t); - if (workers2[i]->set_output(t)<0) return -1; + if (w[j]->isMultiOutput()) { + if (w[j]->set_output(t)<0) return -1; + } else { + if (workers2[i]->set_output(t)<0) return -1; + } } } else if (workers2[i]->create_output_buffer(nentries,fixedsize)==-1) return -1; diff --git a/tests/Makefile b/tests/Makefile index b9351dad..67a997b1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_farm+A2A3 test_farm+A2A4 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode #test_taskf2 test_taskf3 diff --git a/tests/test_farm+A2A3.cpp b/tests/test_farm+A2A3.cpp new file mode 100644 index 00000000..9cee192b --- /dev/null +++ b/tests/test_farm+A2A3.cpp @@ -0,0 +1,180 @@ +/* + * FastFlow concurrent network: + * --------------------------------------- + * | pipeA2A1 | + * | -------------- | + * | pipe1 | T1 ->| T3 | | + * | ------------- | | | | + * | | W1 --> W2 | --> | | | | ----- + * ---- ----- | ------------- ^ | T2 ->| T4 | | | | + * | S1 |-> | S2 |-->| | -------------- |-->| S3 | + * ---- | | | ------------- v -------------- | | | + * ----- | | W1 --> W2 | --> | T1 ->| T5 | | ----- + * emitter | ------------- | | | | collector + * | pipe2 | | | | + * | | T2 ->| T6 | | + * | -------------- | + * | pipeA2A2 | + * --------------------------------------- + * /<----------------a2a ----------------->/ + * /<------------------------ farm --------------------------->/ + * /<-------------------------------- pipeMain ------------------------->/ + * + */ + +#include +#include +#include + + +using namespace ff; + +std::mutex mtx; // for printing purposes + +struct S1 : ff_node_t { + S1(long N):N(N) {} + std::string* svc(std::string*) { + for(long i = 0; i < N; i++) + ff_send_out(new std::string("[Task generated from S1 for Worker")); + + return EOS; + } + long N; +}; + +struct S2 : ff_monode_t{ + + std::string* svc(std::string* in){ + long idx = next % 2; + std::string* out = new std::string(*in + std::to_string(idx) + "]"); + + ff_send_out_to(out, idx); + delete in; + ++next; + return GO_ON; + + } + long next=0; +}; + +struct W1: ff_node_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[W1-" << get_my_id() << " received " << *in << " from S2]\n"; + + return in; + } +}; +struct W2: ff_monode_t { + + int svc_init() { + next = get_my_id(); + return 0; + } + + std::string* svc(std::string* in){ + long outchannels = get_num_outchannels(); + long idx = next % outchannels; + + ff_send_out_to(new std::string("[Task generated from W2-" + std::to_string(get_my_id()) + " to T" + std::to_string(idx) + "]"), idx); + + ++next; + return GO_ON; + } + long next; +}; + +struct T_left: ff_minode_t { + std::string* svc(std::string* in){ + return in; + } +}; +struct T_right: ff_monode_t { + std::string* svc(std::string* in){ + return in; + } +}; +struct T_right2: ff_node_t { + std::string* svc(std::string* in){ + return in; + } +}; + + +struct S3 : ff_minode_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + + std::cout << "[S3 received " << *in << " from T" << (3+get_channel_id()) << "]" << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + int N=1000; + if (argc==2) N=std::stol(argv[1]); + + + using T1 = ff_comb; + using T2 = ff_comb; + using T3 = ff_comb; + using T4 = ff_comb; + using T5 = ff_comb; + using T6 = ff_comb; + + + ff_pipeline pipe1; + pipe1.add_stage(new W1, true); + pipe1.add_stage(new W2, true); + + ff_pipeline pipe2; + pipe2.add_stage(new W1, true); + pipe2.add_stage(new W2, true); + + ff_pipeline pipeA2A1; + ff_a2a a2a1; + a2a1.add_firstset( {new T1(new T_left, new T_right, true, true), + new T2(new T_left, new T_right, true, true)}, 0, true); + + + a2a1.add_secondset({new T3(new T_left, new T_right, true, true), + new T4(new T_left, new T_right, true, true)}, true); + + pipeA2A1.add_stage(&a2a1); + + ff_pipeline pipeA2A2; + ff_a2a a2a2; + a2a2.add_firstset( {new T1(new T_left, new T_right, true, true), + new T2(new T_left, new T_right, true, true)}, 0, true); + + + a2a2.add_secondset({new T5(new T_left, new T_right, true, true), + new T6(new T_left, new T_right, true, true)}, true); + + pipeA2A2.add_stage(&a2a2); + + ff_a2a a2a; + a2a.add_firstset( {&pipe1, &pipe2}); + a2a.add_secondset({&pipeA2A1, &pipeA2A2}); + + S1 s1(N); + S2 s2; + S3 s3; + + ff_pipeline pipeMain; + ff_farm farm; + farm.add_emitter(&s2); + farm.add_workers({&a2a}); + farm.add_collector(&s3); + + pipeMain.add_stage(&s1); + pipeMain.add_stage(&farm); + + if (pipeMain.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + return 0; +} diff --git a/tests/test_farm+A2A4.cpp b/tests/test_farm+A2A4.cpp new file mode 100644 index 00000000..2ea444b0 --- /dev/null +++ b/tests/test_farm+A2A4.cpp @@ -0,0 +1,201 @@ +/* + * FastFlow concurrent network: + * --------------------------------------------- + * | pipeA2A1 | + * | pipe1 -------------------- | + * | ----------------- | /<-farm3-->/|| + * || | W2 | | T1 ->| | T4 || + * || W1-> DefE->| W2 | -->| |-->T3-->| T4 || + * ---- ----- || | W2 | ^ | T2 ->| | T4 || ---- + * | S1 |-> | S2 |->|| /<-farm1-->/ | | -------------------- | | S3 | + * ---- | | | ---------------- | a2a1 |->| | + * ---- | | | ---- + * emitter | | | collector + * | ----------------- | a2a2 | + * || /<-farm2-->/| | -------------------- | + * || | W2 | v | T1 ->| | T4 || + * || W1-> DefE->| W2 | -->| |-->T3-->| T4 || + * || | W2 | | T2 ->| | T4 || + * | ----------------- | /<-farm4-->/|| + * | pipe2 -------------------- | + * | pipeA2A2 | + * --------------------------------------------- + * /<--------------------a2a ------------------->/ + * /<------------------------- farm ---------------------------->/ + * /<-------------------------------- pipeMain --------------------------->/ + * + */ + +#include +#include +#include + + +using namespace ff; + +std::mutex mtx; // for printing purposes + +struct S1 : ff_node_t { + S1(long N):N(N) {} + std::string* svc(std::string*) { + for(long i = 0; i < N; i++) + ff_send_out(new std::string("[Task generated from S1 for Worker")); + + return EOS; + } + long N; +}; + +struct S2 : ff_monode_t{ + + std::string* svc(std::string* in){ + long idx = next % 2; + std::string* out = new std::string(*in + std::to_string(idx) + "]"); + + ff_send_out_to(out, idx); + delete in; + ++next; + return GO_ON; + + } + long next=0; +}; + +struct W1: ff_node_t { + + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + std::cout << "[W1-" << get_my_id() << " received " << *in << " from S2]\n"; + + return in; + } +}; +struct W2: ff_monode_t { + + int svc_init() { + next = get_my_id(); + return 0; + } + + std::string* svc(std::string* in){ + long outchannels = get_num_outchannels(); + long idx = next % outchannels; + + ff_send_out_to(new std::string("[Task generated from W2-" + std::to_string(get_my_id()) + " to T" + std::to_string(idx) + "]"), idx); + + ++next; + return GO_ON; + } + long next; +}; + +struct T_left: ff_minode_t { + std::string* svc(std::string* in){ + return in; + } +}; +struct T_right: ff_monode_t { + std::string* svc(std::string* in){ + return in; + } +}; +struct T_right2: ff_node_t { + std::string* svc(std::string* in){ + return in; + } +}; + + +struct S3 : ff_minode_t{ + std::string* svc(std::string* in){ + const std::lock_guard lock(mtx); + + std::cout << "[S3 received " << *in << " from T" << (3+get_channel_id()) << "]" << std::endl; + delete in; + return this->GO_ON; + } +}; + +int main(int argc, char*argv[]){ + int N=1000; + if (argc==2) N=std::stol(argv[1]); + + + using T1 = ff_comb; + using T2 = ff_comb; + using T3 = ff_comb; + using T4 = T_right2; // T_right2 + + + ff_pipeline pipe1; + pipe1.add_stage(new W1, true); + ff_farm farm1; + farm1.add_workers({new W2, new W2, new W2}); + farm1.cleanup_workers(); + pipe1.add_stage(&farm1); + + ff_pipeline pipe2; + pipe2.add_stage(new W1, true); + ff_farm farm2; + farm2.add_workers({new W2, new W2, new W2}); + farm2.cleanup_workers(); + pipe2.add_stage(&farm2); + + ff_pipeline pipeA2A1; + ff_a2a a2a1; + a2a1.add_firstset( {new T1(new T_left, new T_right, true, true), + new T2(new T_left, new T_right, true, true)}, 0, true); + + + ff_farm farm3; + farm3.add_emitter(new T3(new T_left, new T_right, true, true)); + farm3.add_workers({ new T4, new T4, new T4 }); + + // to be added in the second set of a2a1, the farm must be inside a pipeline + ff_pipeline pipefarm1; + pipefarm1.add_stage(&farm3); + + a2a1.add_secondset({&pipefarm1}); + + pipeA2A1.add_stage(&a2a1); + + ff_pipeline pipeA2A2; + ff_a2a a2a2; + a2a2.add_firstset( {new T1(new T_left, new T_right, true, true), + new T2(new T_left, new T_right, true, true)}, 0, true); + + ff_farm farm4; + farm4.add_emitter(new T3(new T_left, new T_right, true, true)); + farm4.add_workers({ new T4, new T4, new T4 }); + // to be added in the second set of a2a2, the farm must be inside a pipeline + ff_pipeline pipefarm2; + pipefarm2.add_stage(&farm4); + + a2a2.add_secondset({&pipefarm2}); + + + pipeA2A2.add_stage(&a2a2); + + ff_a2a a2a; + a2a.add_firstset( {&pipe1, &pipe2}); + a2a.add_secondset({&pipeA2A1, &pipeA2A2}); + + S1 s1(N); + S2 s2; + S3 s3; + + ff_pipeline pipeMain; + ff_farm farm; + farm.add_emitter(&s2); + farm.add_workers({&a2a}); + farm.add_collector(&s3); + + pipeMain.add_stage(&s1); + pipeMain.add_stage(&farm); + + if (pipeMain.run_and_wait_end()<0) { + error("running a2a\n"); + return -1; + } + return 0; +} From 7305ce2ddaf2cbb74f85bed9b83ffe557fcd7637 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 28 Feb 2022 10:52:05 +0100 Subject: [PATCH 117/202] merged bug fix in the a2a from the master branch, minor other modifications --- ff/all2all.hpp | 38 ++++--- ff/distributed/ff_dadapters.hpp | 3 +- ff/node.hpp | 4 +- tests/distributed/test_group16.cpp | 8 +- tests/distributed/test_group18.cpp | 149 ++++++++++++++++++++++++++++ tests/distributed/test_group18.json | 18 ++++ tests/distributed/test_group7.cpp | 23 +++-- 7 files changed, 217 insertions(+), 26 deletions(-) create mode 100644 tests/distributed/test_group18.cpp create mode 100644 tests/distributed/test_group18.json diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 9997ee3f..110dac84 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -200,18 +200,30 @@ class ff_a2a: public ff_node { } } if (outputNodes.size()) { - if (outputNodes.size() != workers2.size()) { - error("A2A, prepare, invalid state\n"); + + svector w; + for(size_t i=0;iget_out_nodes(w); + + if (outputNodes.size() != w.size()) { + error("A2A, prepare, invalid state detected\n"); return -1; } + + for(size_t i=0;iisMultiOutput()) { + error("A2A, prepare, invalid state, unexpected multi-output node\n"); + return -1; + } + + assert(outputNodes[i]->get_in_buffer() != nullptr); + if (w[i]->set_output_buffer(outputNodes[i]->get_in_buffer()) < 0) { + error("A2A, prepare, invalid state, setting output buffer\n"); + return -1; + } + } } - for(size_t i=0;iisMultiOutput()); - assert(outputNodes[i]->get_in_buffer() != nullptr); - assert(workers2[i]->get_out_buffer() == nullptr); - if (workers2[i]->set_output_buffer(outputNodes[i]->get_in_buffer()) <0) - return -1; - } + // blocking stuff -------------------------------------------- @@ -498,13 +510,11 @@ class ff_a2a: public ff_node { int numThreads() const { return cardinality(); } int set_output(const svector & w) { - if (outputNodes.size()+w.size() > workers2.size()) return -1; outputNodes +=w; return 0; } int set_output(ff_node *node) { - if (outputNodes.size()+1 > workers2.size()) return -1; outputNodes.push_back(node); return 0; } @@ -735,7 +745,11 @@ class ff_a2a: public ff_node { ff_node* t = new ff_buffernode(nentries,fixedsize); t->set_id(id++); internalSupportNodes.push_back(t); - if (workers2[i]->set_output(t)<0) return -1; + if (w[j]->isMultiOutput()) { + if (w[j]->set_output(t)<0) return -1; + } else { + if (workers2[i]->set_output(t)<0) return -1; + } } } else{ if (workers2[i]->create_output_buffer(nentries,fixedsize)==-1) return -1; diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index e0db7b9c..bd9cdb79 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -84,7 +84,8 @@ class EmitterAdapter: public internal_mo_transformer { int svc_init() { if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); - mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers + //mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers + mo->set_running(totalWorkers); } return n->svc_init(); } diff --git a/ff/node.hpp b/ff/node.hpp index 4e66b88f..852437ee 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1019,7 +1019,7 @@ class ff_node { */ inline size_t getOSThreadId() const { if (thread) return thread->getOSThreadId(); return 0; } - virtual bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) {} + virtual bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) { return false;} #if defined(FF_TASK_CALLBACK) virtual void callbackIn(void * =NULL) { } @@ -1710,4 +1710,4 @@ struct ff_buffernode: ff_node { } // namespace ff -#endif /* FF_NODE_HPP */ \ No newline at end of file +#endif /* FF_NODE_HPP */ diff --git a/tests/distributed/test_group16.cpp b/tests/distributed/test_group16.cpp index c7a7017e..bb373682 100644 --- a/tests/distributed/test_group16.cpp +++ b/tests/distributed/test_group16.cpp @@ -49,7 +49,7 @@ struct Node1: ff_node_t{ struct Node2: ff_node_t{ myTask_t* svc(myTask_t* t){ t->str += std::string(" World"); - //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; return t; } @@ -100,18 +100,16 @@ int main(int argc, char*argv[]){ ff_pipeline pipe0; pipe0.add_stage(&n1); pipe0.add_stage(&n2); - ff_pipeline pipe1; - pipe1.add_stage(&n3); ff_pipeline pipe2; pipe2.add_stage(&n4); pipe.add_stage(&pipe0); - pipe.add_stage(&pipe1); + pipe.add_stage(&n3); pipe.add_stage(&pipe2); //----- defining the distributed groups ------ auto G1 = pipe0.createGroup("G1"); - auto G2 = pipe1.createGroup("G2"); + auto G2 = n3.createGroup("G2"); auto G3 = pipe2.createGroup("G3"); // ------------------------------------------- diff --git a/tests/distributed/test_group18.cpp b/tests/distributed/test_group18.cpp new file mode 100644 index 00000000..47d71d9c --- /dev/null +++ b/tests/distributed/test_group18.cpp @@ -0,0 +1,149 @@ +/* + * FastFlow concurrent network: + * + * |---> Node2 ---> | -> Node3 -->| + * | | | --> Node4 ->| + * Node1-->|---> Node2 ---> | -> Node3 -->| |-> Node5 + * (emitter) | | | --> Node4 ->| + * |---> Node2 ---> | -> Node3 -->| + * + * + * /<-- farm (no collector)-->//<------- a2a ----------->/ + * /<---------------------- pipe ----------------------->/ + * + * NOTE: Node2 is just a standard node, not a multi-output node, + * Node3 is just a ff_monode, not also a multi-input node, + * therefore, there are direct connections between Node2 + * and Node3 nodes and not a shuffle connection among them. + * + * + * distributed version: + * + * ------------------------- ------------------------ + * | |--> Node2 ---> | | -> Node3-->| | + * | | | | | --> Node4 | ------- + * | Node1-->|--> Node2 ---> | -->| -> Node3-->| |-->| Node5 | + * | | | | | --> Node4 | ------- + * | |--> Node2 ---> | | -> Node3-->| | + * ------------------------- ------------------------ + * G1 G2 G3 + * + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World") + std::to_string(get_my_id()); + return t; + } +}; + +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + return t; + } +}; +struct Node5: ff_minode_t{ + Node5(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node5: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n" << std::flush; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + //ff:: + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_farm farm; + farm.add_emitter(new Node1(ntasks)); + farm.add_workers({new Node2, new Node2, new Node2}); + farm.cleanup_emitter(); + farm.cleanup_workers(); + + ff_a2a a2a; + a2a.add_firstset({new Node3, new Node3, new Node3}, 0, true); + a2a.add_secondset({new Node4, new Node4}, true); + + Node5 n5(ntasks); + + ff_pipeline pipe; + pipe.add_stage(&farm); + pipe.add_stage(&a2a); + pipe.add_stage(&n5); + + //----- defining the distributed groups ------ + + auto G1 = farm.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + auto G3 = n5.createGroup("G3"); + + // ------------------------------------------- + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group18.json b/tests/distributed/test_group18.json new file mode 100644 index 00000000..6efb3eea --- /dev/null +++ b/tests/distributed/test_group18.json @@ -0,0 +1,18 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:9004", + "name" : "G1", + "OConn" : ["G2"] + }, + { + "name" : "G2", + "endpoint": "localhost:9005", + "OConn" : ["G3"] + }, + { + "name" : "G3", + "endpoint": "localhost:9006" + } + ] +} diff --git a/tests/distributed/test_group7.cpp b/tests/distributed/test_group7.cpp index 388d8878..79718d30 100644 --- a/tests/distributed/test_group7.cpp +++ b/tests/distributed/test_group7.cpp @@ -42,7 +42,7 @@ * G3 */ -#include +#include #include #include @@ -139,11 +139,12 @@ struct S3 : ff_minode_t{ }; int main(int argc, char*argv[]){ +#if 0 if (DFF_Init(argc, argv) != 0) { error("DFF_Init\n"); return -1; } - +#endif int N=10; if (argc==2) N=std::stol(argv[1]); @@ -171,14 +172,24 @@ int main(int argc, char*argv[]){ ff_a2a a2a1; a2a1.add_firstset( {new T1(new T_left, new T_right, true, true), new T2(new T_left, new T_right, true, true)}, 0, true); - a2a1.add_secondset({new T3, new T4}, true); + + + a2a1.add_secondset({new ff_comb(new T3, new T_right, true, true), new ff_comb(new T4, new T_right, true, true)}, true); + //a2a1.add_secondset({new T3, new T4}, true); + + pipeA2A1.add_stage(&a2a1); ff_pipeline pipeA2A2; ff_a2a a2a2; a2a2.add_firstset( {new T1(new T_left, new T_right, true, true), new T2(new T_left, new T_right, true, true)}, 0, true); - a2a2.add_secondset({new T5, new T6}, true); + + + a2a2.add_secondset({new ff_comb(new T5, new T_right, true, true), new ff_comb(new T6, new T_right, true, true)}, true); + //a2a2.add_secondset({new T5, new T6}, true); + + pipeA2A2.add_stage(&a2a2); ff_a2a a2a; @@ -191,7 +202,7 @@ int main(int argc, char*argv[]){ pipeMain.add_stage(&pipe0); pipeMain.add_stage(&a2a); pipeMain.add_stage(&s3); - +#if 0 //----- defining the distributed groups ------ auto G1 = pipe0.createGroup("G1"); auto G2 = a2a.createGroup("G2"); @@ -202,7 +213,7 @@ int main(int argc, char*argv[]){ G3 << &pipe2 << &pipeA2A2; // ------------------------------------------- - +#endif // running the distributed groups if (pipeMain.run_and_wait_end()<0) { error("running a2a\n"); From 35400b1f86c030f285b7e3725b0d91634471a633 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 28 Feb 2022 12:19:02 +0100 Subject: [PATCH 118/202] removed warnings, termination issue solved --- ff/distributed/ff_dadapters.hpp | 4 ++-- ff/distributed/ff_dgroup2.hpp | 14 +++++++++----- ff/distributed/ff_dreceiver.hpp | 10 +++++----- ff/distributed/ff_dsender.hpp | 4 ++-- tests/distributed/test_group14.cpp | 5 ----- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index bd9cdb79..a46c0c4f 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -20,8 +20,8 @@ class SquareBoxRight : public ff_minode { void* svc(void* in) {return in;} void eosnotify(ssize_t id) { - if (id == this->get_num_inchannels() - 1) return; // EOS coming from the SquareLeft, we must ignore it - if (++neos == (this->get_num_inchannels() - 1)) + if (id == (ssize_t)(this->get_num_inchannels() - 1)) return; // EOS coming from the SquareLeft, we must ignore it + if (++neos == (ssize_t)(this->get_num_inchannels() - 1)) this->ff_send_out(this->EOS); } }; diff --git a/ff/distributed/ff_dgroup2.hpp b/ff/distributed/ff_dgroup2.hpp index 85d4e452..4daa6e2e 100644 --- a/ff/distributed/ff_dgroup2.hpp +++ b/ff/distributed/ff_dgroup2.hpp @@ -23,13 +23,13 @@ class dGroup2 : public ff::ff_farm { static inline std::unordered_map vector2UMap(const std::vector v){ std::unordered_map output; - for(int i = 0; i < v.size(); i++) output[v[i]] = i; + for(size_t i = 0; i < v.size(); i++) output[v[i]] = i; return output; } static inline std::map vector2Map(const std::vector v){ std::map output; - for(int i = 0; i < v.size(); i++) output[v[i]] = i; + for(size_t i = 0; i < v.size(); i++) output[v[i]] = i; return output; } @@ -139,7 +139,9 @@ class dGroup2 : public ff::ff_farm { firstSet.push_back(new SquareBoxLeft(localRightWorkers)); // ondemand?? std::transform(firstSet.begin(), firstSet.end(), firstSet.begin(), [](ff_node* n) -> ff_node* { - if (!n->isPipe()) return new ff_Pipe(n); return n; + if (!n->isPipe()) + return new ff_Pipe(n); + return n; }); innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? @@ -177,7 +179,9 @@ class dGroup2 : public ff::ff_farm { secondSet.push_back(new SquareBoxRight); std::transform(secondSet.begin(), secondSet.end(), secondSet.begin(), [](ff_node* n) -> ff_node* { - if (!n->isPipe()) return new ff_Pipe(n); return n; + if (!n->isPipe()) + return new ff_Pipe(n); + return n; }); innerA2A->add_secondset(secondSet); // cleanup?? @@ -199,4 +203,4 @@ class dGroup2 : public ff::ff_farm { }; -} \ No newline at end of file +} diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index a00086d0..e236d119 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -270,20 +270,20 @@ class ff_dreceiverH : public ff_dreceiver { std::vector internalDestinations; std::map isInternalConnection; std::set internalGroupsNames; - int internalNEos = 0, externalNEos = 0; + size_t internalNEos = 0, externalNEos = 0; void registerEOS(int sck){ neos++; - int internalConn = std::count_if(std::begin(isInternalConnection), + size_t internalConn = std::count_if(std::begin(isInternalConnection), std::end (isInternalConnection), [](std::pair const &p) {return p.second;}); if (!isInternalConnection[sck]){ if (++externalNEos == (isInternalConnection.size()-internalConn)) - for(int i = 0; i < 1; i++) ff_send_out_to(this->EOS, i); + for(size_t i = 0; i < this->get_num_outchannels()-1; i++) ff_send_out_to(this->EOS, i); } else - if (++internalNEos == internalConn) - ff_send_out_to(this->EOS, this->get_num_outchannels()-1); + if (++internalNEos == internalConn) + ff_send_out_to(this->EOS, this->get_num_outchannels()-1); } diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 7d8d56b3..d7ffd107 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -281,7 +281,7 @@ class ff_dsenderH : public ff_dsender { } message_t *svc(message_t* task) { - if (this->get_channel_id() == (this->get_num_inchannels() - 1)){ + if (this->get_channel_id() == (ssize_t)(this->get_num_inchannels() - 1)){ // pick destination from the list of internal connections! if (task->chid == -1){ // roundrobin over the destinations task->chid = rr_iterator->first; @@ -297,7 +297,7 @@ class ff_dsenderH : public ff_dsender { } void eosnotify(ssize_t id) { - if (id == (this->get_num_inchannels() - 1)){ + if (id == (ssize_t)(this->get_num_inchannels() - 1)){ // send the EOS to all the internal connections message_t E_O_S(0,0); for(const auto& sck : internalSockets) sendToSck(sck, &E_O_S); diff --git a/tests/distributed/test_group14.cpp b/tests/distributed/test_group14.cpp index 5b3205be..4fdb8fea 100644 --- a/tests/distributed/test_group14.cpp +++ b/tests/distributed/test_group14.cpp @@ -68,11 +68,6 @@ struct Node3: ff_minode_t{ delete t; return GO_ON; } - void eosnotify(ssize_t id) { - printf("Node4 EOS RECEIVED from %ld\n", id); - fflush(NULL); - } - void svc_end() { if (processed != ntasks) { abort(); From cfc86969e06701306b364800bba1a09cd10bfabc Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 2 Mar 2022 07:36:57 +0100 Subject: [PATCH 119/202] added change_node in the farm building block, fixed a problem with multi-output workers in a farm having farm workers --- ff/farm.hpp | 49 +++++++++++++++++- ff/graph_utils.hpp | 2 +- tests/Makefile | 2 +- tests/test_changenode.cpp | 16 +++++- tests/test_farm+A2A4.cpp | 4 +- tests/test_farm+farm2.cpp | 102 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 tests/test_farm+farm2.cpp diff --git a/ff/farm.hpp b/ff/farm.hpp index 53839b99..c07b1d2f 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -434,7 +434,11 @@ class ff_farm: public ff_node { ff_node* t = new ff_buffernode(out_buffer_entries,fixedsizeOUT, idx++); assert(t); internalSupportNodes.push_back(t); - workers[i]->set_output(t); + if (w[j]->isMultiOutput()) { + if (w[j]->set_output(t)<0) return -1; + } else { + if (workers[i]->set_output(t)<0) return -1; + } gt->register_worker(t); } } else { // single node multi-output @@ -1009,6 +1013,48 @@ class ff_farm: public ff_node { return this->add_emitter(e); } + bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) { + assert(old!=nullptr); + assert(n!=nullptr); + if (prepared) { + error("FARM, change_node cannot be called because the FARM has already been prepared\n"); + return false; + } + + if (emitter == old) return (change_emitter(n, cleanup)==0); + + if (collector && !collector_removed && collector == old) { + if (collector_cleanup) { + delete collector; + collector_cleanup=false; + } + gt->reset_filter(); + collector=nullptr; + collector_cleanup=cleanup; + return (this->add_collector(n) == 0); + } + + for(size_t i=0; i=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos); + } + + if (worker_cleanup) + internalSupportNodes.push_back(workers[i]); + + workers[i] = n; + if (cleanup && !worker_cleanup) internalSupportNodes.push_back(n); + return true; + } + } + + return false; + } + /** * * \brief Set scheduling with on demand polity @@ -1090,6 +1136,7 @@ class ff_farm: public ff_node { } /** * replace the workers node. Note, that no cleanup of previous workers will be done. + * For more fine-grained control you should use change_node */ int change_workers(const std::vector& w) { workers.clear(); diff --git a/ff/graph_utils.hpp b/ff/graph_utils.hpp index b4177912..3bd5eb5f 100644 --- a/ff/graph_utils.hpp +++ b/ff/graph_utils.hpp @@ -127,7 +127,7 @@ static inline ff_node* getBB(ff_node* startnode, ff_node* n) { } return nullptr; } - // TODO: ofarm + if (startnode->isOFarm()) abort(); // TODO: ofarm if (startnode->isFarm()) { ff_farm* farm = reinterpret_cast(startnode); if (farm->getEmitter() == n) return farm; diff --git a/tests/Makefile b/tests/Makefile index 67a997b1..ad1dcfa2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+A2A test_farm+A2A2 test_farm+A2A3 test_farm+A2A4 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+farm2 test_farm+A2A test_farm+A2A2 test_farm+A2A3 test_farm+A2A4 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode #test_taskf2 test_taskf3 diff --git a/tests/test_changenode.cpp b/tests/test_changenode.cpp index 7be68287..76f699ee 100644 --- a/tests/test_changenode.cpp +++ b/tests/test_changenode.cpp @@ -26,7 +26,7 @@ */ /* - * Testing the change_node method for the pipeline, all-to-all and combine. + * Testing the change_node method for the pipeline, farm all-to-all and combine. * */ @@ -94,12 +94,19 @@ int main() { ff_comb *c3 = new ff_comb(sette, otto, true, true); W2.push_back(c3); a2a.add_secondset(W2,true); - + + ff_farm farm; + moNode* nove = new moNode; + moNode* dieci = new moNode; + farm.add_workers({nove, dieci}); + farm.cleanup_workers(); + ff_pipeline pipeMain; pipeMain.add_stage(&source); pipeMain.add_stage(&pipe0); pipeMain.add_stage(&a2a); + pipeMain.add_stage(&farm); pipeMain.add_stage(&sink); // changing some nodes @@ -119,6 +126,11 @@ int main() { assert(t3 == &a2a); (reinterpret_cast(t3))->change_node(c2, new ff_comb(new moNode, new moNode, true, true), true, true); delete c2; + + ff_node* t4 = getBB(&pipeMain, nove); + assert(t4 == &farm); + (reinterpret_cast(t4))->change_node(nove, new ff_comb(new moNode, new moNode, true, true), true, true); + (reinterpret_cast(t4))->change_node(dieci, new ff_comb(new moNode, new moNode, true, true), true, true); pipeMain.change_node(&source, new Source(100), true); diff --git a/tests/test_farm+A2A4.cpp b/tests/test_farm+A2A4.cpp index 2ea444b0..b873c6af 100644 --- a/tests/test_farm+A2A4.cpp +++ b/tests/test_farm+A2A4.cpp @@ -81,7 +81,7 @@ struct W2: ff_monode_t { long outchannels = get_num_outchannels(); long idx = next % outchannels; - ff_send_out_to(new std::string("[Task generated from W2-" + std::to_string(get_my_id()) + " to T" + std::to_string(idx) + "]"), idx); + ff_send_out_to(new std::string("[Task generated from W2-" + std::to_string(get_my_id()) + " to T" + ((idx==0 || idx==1)?std::to_string(idx):std::to_string(idx%3)) + "-" + std::to_string(idx) + "]"), idx); ++next; return GO_ON; @@ -110,7 +110,7 @@ struct S3 : ff_minode_t{ std::string* svc(std::string* in){ const std::lock_guard lock(mtx); - std::cout << "[S3 received " << *in << " from T" << (3+get_channel_id()) << "]" << std::endl; + std::cout << "[S3 received " << *in << " from T4-" << get_channel_id() << "]" << std::endl; delete in; return this->GO_ON; } diff --git a/tests/test_farm+farm2.cpp b/tests/test_farm+farm2.cpp new file mode 100644 index 00000000..803008c1 --- /dev/null +++ b/tests/test_farm+farm2.cpp @@ -0,0 +1,102 @@ +/* + * FastFlow concurrent network: + * + * -------------------------- + * | | + * | | -> Node2 | + * | | | + * ------ | ------- | -> Node2 | ------- ------- + * | DefE | --->| | Node1 | -->| | -->| Node3 | ---> | Node4 | + * ------ | ------- | -> Node2 | ------- ------- + * (emitter) | (emitter) | | (collector) + * | | -> Node2 | + * | | + * -------------------------- + * /<------- farm ----------->/ + * /<------------------------ farmExt ------------------->/ + * /<---------------------------- pipeMain ---------------------------->/ + * + */ + + +#include +#include + +using namespace ff; + + +using myTask_t=long; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + ff_send_out(new myTask_t(i)); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + std::cout << *t << " (" << get_my_id() << ")\n"; + return t; + } +}; + +struct Node3: ff_minode_t { + myTask_t* svc(myTask_t* t) { + std::cout << "Node3 " << *t << " from " << get_channel_id() << "\n"; + return t; + } +}; + +struct Node4: ff_node_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + std::cout << "ERROR: processed = " << processed << " ntasks= " << ntasks << "\n"; + exit(-1); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + + ff_farm farm; + farm.add_emitter(new Node1(ntasks)); + farm.add_workers({new Node2, new Node2, new Node2}); + farm.cleanup_emitter(); + farm.cleanup_workers(); + + ff_farm farmExt; + farmExt.add_workers({&farm}); + farmExt.add_collector(new Node3); + + Node4 n4(ntasks); + + ff_pipeline pipeMain; + pipeMain.add_stage(&farmExt); + pipeMain.add_stage(&n4); + + if (pipeMain.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} From bb7d11f4c31c981c2801b8b8290a45f70eb03bfb Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 2 Mar 2022 07:42:25 +0100 Subject: [PATCH 120/202] merged modifications from the master branch in the ff_farm --- ff/distributed/ff_dgroups.hpp | 1 + ff/farm.hpp | 63 +++++++++++++++++++++++++++++- tests/distributed/test_group18.cpp | 8 +++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index a9194037..ff67beda 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -298,6 +298,7 @@ class dGroups { // if (!runningGroup_IR.isVertical()){ + assert(runningGroup_IR.parentBB->isAll2All()); ff_a2a* parentA2A = reinterpret_cast(runningGroup_IR.parentBB); { ff::svector inputs; diff --git a/ff/farm.hpp b/ff/farm.hpp index 540c4df9..3b3979da 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -432,7 +432,11 @@ class ff_farm: public ff_node { ff_node* t = new ff_buffernode(out_buffer_entries,fixedsizeOUT, idx++); assert(t); internalSupportNodes.push_back(t); - workers[i]->set_output(t); + if (w[j]->isMultiOutput()) { + if (w[j]->set_output(t)<0) return -1; + } else { + if (workers[i]->set_output(t)<0) return -1; + } gt->register_worker(t); } } else { // single node multi-output @@ -1007,6 +1011,48 @@ class ff_farm: public ff_node { return this->add_emitter(e); } + bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) { + assert(old!=nullptr); + assert(n!=nullptr); + if (prepared) { + error("FARM, change_node cannot be called because the FARM has already been prepared\n"); + return false; + } + + if (emitter == old) return (change_emitter(n, cleanup)==0); + + if (collector && !collector_removed && collector == old) { + if (collector_cleanup) { + delete collector; + collector_cleanup=false; + } + gt->reset_filter(); + collector=nullptr; + collector_cleanup=cleanup; + return (this->add_collector(n) == 0); + } + + for(size_t i=0; i=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos); + } + + if (worker_cleanup) + internalSupportNodes.push_back(workers[i]); + + workers[i] = n; + if (cleanup && !worker_cleanup) internalSupportNodes.push_back(n); + return true; + } + } + + return false; + } + /** * * \brief Set scheduling with on demand polity @@ -1088,6 +1134,7 @@ class ff_farm: public ff_node { } /** * replace the workers node. Note, that no cleanup of previous workers will be done. + * For more fine-grained control you should use change_node */ int change_workers(const std::vector& w) { workers.clear(); @@ -1810,7 +1857,21 @@ class ff_farm: public ff_node { return diffmsec(getwstoptime(),lb->getwstartime()); } +#ifdef DFF_ENABLED + virtual bool isSerializable(){ + svector outputs; this->get_out_nodes(outputs); + for(ff_node* output: outputs) if (!output->isSerializable()) return false; + return true; + } + virtual bool isDeserializable(){ + svector inputs; this->get_in_nodes(inputs); + for(ff_node* input: inputs) if(!input->isDeserializable()) return false; + return true; + } +#endif + + #if defined(TRACE_FASTFLOW) void ffStats(std::ostream & out) { out << "--- farm:\n"; diff --git a/tests/distributed/test_group18.cpp b/tests/distributed/test_group18.cpp index 47d71d9c..32e82c6e 100644 --- a/tests/distributed/test_group18.cpp +++ b/tests/distributed/test_group18.cpp @@ -28,6 +28,10 @@ * ------------------------- ------------------------ * G1 G2 G3 * + * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + * REMEMBER: CHECK THAT Node2_i CAN SEND DATA ONLY TO Node3_i (same index)!!!! + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * */ @@ -115,13 +119,13 @@ int main(int argc, char*argv[]){ if (argc>1) { ntasks = std::stol(argv[1]); } - + ff_farm farm; farm.add_emitter(new Node1(ntasks)); farm.add_workers({new Node2, new Node2, new Node2}); farm.cleanup_emitter(); farm.cleanup_workers(); - + ff_a2a a2a; a2a.add_firstset({new Node3, new Node3, new Node3}, 0, true); a2a.add_secondset({new Node4, new Node4}, true); From cb338d004952434545fb4c4644c022dfb1bb58b3 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 2 Mar 2022 17:55:08 +0100 Subject: [PATCH 121/202] Added feature for handling get_num_outchannels() call in the distributed rts --- ff/distributed/ff_dgroup2.hpp | 21 +++++++++---- ff/distributed/ff_dgroups.hpp | 46 ++++++++++++++--------------- ff/distributed/ff_dintermediate.hpp | 9 +++++- ff/distributed/ff_wrappers.hpp | 3 +- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/ff/distributed/ff_dgroup2.hpp b/ff/distributed/ff_dgroup2.hpp index 85d4e452..793f0752 100644 --- a/ff/distributed/ff_dgroup2.hpp +++ b/ff/distributed/ff_dgroup2.hpp @@ -5,6 +5,8 @@ #include #include +#include + #ifdef DFF_MPI #include #include @@ -48,15 +50,22 @@ class dGroup2 : public ff::ff_farm { return new WrapperIN(n); } - static ff_node* buildWrapperOUT(ff_node* n, int id){ + static ff_node* buildWrapperOUT(ff_node* n, int id, int outputChannels){ if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF), id, 1, true), false, true); - return new WrapperOUT(n, id); + return new WrapperOUT(n, id, outputChannels); } public: dGroup2(ff_IR& ir){ - if (ir.isVertical()){ + int outputChannels = 0; + if (ir.hasSender){ + ff::cout << "Size of routintable " << ir.routingTable.size() << std::endl; + outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+f.second.first.size();}); + ff::cout << "Outputchannels: " << outputChannels << std::endl; + } + if (ir.isVertical()){ + std::vector reverseOutputIndexes(ir.hasLeftChildren() ? ir.outputL.rbegin() : ir.outputR.rbegin(), ir.hasLeftChildren() ? ir.outputL.rend() : ir.outputR.rend()); for(ff_node* child: (ir.hasLeftChildren() ? ir.L : ir.R)){ ff::svector inputs; child->get_in_nodes(inputs); @@ -71,7 +80,7 @@ class dGroup2 : public ff::ff_farm { workers.push_back(new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes))); else if (ir.hasReceiver) workers.push_back(buildWrapperIN(child)); - else workers.push_back(buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes))); + else workers.push_back(buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels)); } else { if (ir.hasReceiver){ @@ -84,7 +93,7 @@ class dGroup2 : public ff::ff_farm { if (ir.hasSender){ for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes)), true); // cleanup?? removefromcleanuplist?? + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes), outputChannels), true); // cleanup?? removefromcleanuplist?? } } @@ -164,7 +173,7 @@ class dGroup2 : public ff::ff_farm { ff::svector outputs; child->get_out_nodes(outputs); for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseRightOutputIndexes)), true); //cleanup?? removefromcleanuplist? + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseRightOutputIndexes), outputChannels), true); //cleanup?? removefromcleanuplist? } } diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index a9194037..c2eb9375 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -203,22 +203,6 @@ class dGroups { // annotate the listen endpoint for the specified group annotatedGroups[g.name].listenEndpoint = endpoint; } - - - /*auto g = std::find_if(parsedGroups.begin(), parsedGroups.end(), [this](const G& g){return g.name == getRunningGroup();}); - for(std::string& conn : g->Oconn) - // build the list of outgoing connections for each group - if (annotatedGroups.find(conn) != annotatedGroups.end()) - runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[conn].listenEndpoint); - else throw FF_Exception("A specified destination has a wrong name! :("); - */ - - // compute how many expected EOS the future running must wait before exiting - /*size_t expectedEOS = 0; - for (const G& g : parsedGroups) - if (g.name != this->runningGroup) - for (const std::string& conn : g.Oconn) - if (conn == this->runningGroup) expectedEOS++;*/ // build the map parentBB -> std::map> parentBB2GroupsName; @@ -289,14 +273,21 @@ class dGroups { // compute the coverage anyway } - // set the isSrc and isSink fields - runningGroup_IR.isSink = isSnk; runningGroup_IR.isSource = isSrc; - // populate the set with the names of other groups created from this 1st level BB - runningGroup_IR.otherGroupsFromSameParentBB = pair.second; + for(const std::string& _gName : pair.second){ + auto& _ir = annotatedGroups[_gName]; + // set the isSrc and isSink fields + _ir.isSink = isSnk; _ir.isSource = isSrc; + // populate the set with the names of other groups created from this 1st level BB + _ir.otherGroupsFromSameParentBB = pair.second; + } + + //runningGroup_IR.isSink = isSnk; runningGroup_IR.isSource = isSrc; + + //runningGroup_IR.otherGroupsFromSameParentBB = pair.second; } - // + // this is meaningful only if the group is horizontal and made of an a2a if (!runningGroup_IR.isVertical()){ ff_a2a* parentA2A = reinterpret_cast(runningGroup_IR.parentBB); { @@ -342,11 +333,20 @@ class dGroups { for(const auto& gName : inputGroups(parentBB2GroupsName[nextStage])) runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); } + + + runningGroup_IR.buildIndexes(); if (!runningGroup_IR.destinationEndpoints.empty()) runningGroup_IR.hasSender = true; - - runningGroup_IR.buildIndexes(); + // experimental building the expected routing table for the running group offline (i.e., statically) + if (runningGroup_IR.hasSender) + for(auto& ep : runningGroup_IR.destinationEndpoints){ + auto& destIR = annotatedGroups[ep.groupName]; + destIR.buildIndexes(); + bool internalConnection = runningGroup_IR.parentBB == destIR.parentBB; + runningGroup_IR.routingTable[ep.groupName] = std::make_pair(destIR.getInputIndexes(internalConnection), internalConnection); + } //runningGroup_IR.print(); } diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index a609a2c3..b424c02d 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -70,7 +70,7 @@ class ff_IR { std::set L, R; bool coverageL = false, coverageR = false; bool isSource = false, isSink = false; - bool hasReceiver = false, hasSender = false; // todo settare qui. + bool hasReceiver = false, hasSender = false; ff_node* parentBB; @@ -81,6 +81,8 @@ class ff_IR { // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table std::vector inputL, outputL, inputR, outputR; + + std::map, bool>> routingTable; // TODO: implmentare l'assegnamento di questi campi int leftTotalOuputs; @@ -99,6 +101,11 @@ class ff_IR { } } + std::vector getInputIndexes(bool internal){ + if (internal && !R.empty()) return inputR; + return inputL; + } + void print(){ std::cout << "###### BEGIN GROUP ######\n"; std::cout << "Group Orientation: " << (isVertical() ? "vertical" : "horizontal") << std::endl; diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 417773dd..e5bb0500 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -128,7 +128,6 @@ class WrapperINOUT: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have - int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; int myID; public: @@ -157,7 +156,7 @@ class WrapperINOUT: public internal_mi_transformer { } int svc_init() { - if (this->n->isMultiOutput()) { // ??? what?? + if (this->n->isMultiInput()) { // ??? what?? ff_minode* mi = reinterpret_cast(this->n); // what????? mi->set_running(inchannels); } From 9fb573c57dade0e10a93fe56df13bddfe258cf9a Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 2 Mar 2022 18:07:52 +0100 Subject: [PATCH 122/202] Replaced the original dGroup implementation with the latest (previosuly included in dGroup2) --- ff/distributed/ff_dgroup.hpp | 547 ++++++++++------------------------ ff/distributed/ff_dgroup2.hpp | 215 ------------- ff/distributed/ff_dgroups.hpp | 4 +- ff/node.hpp | 2 +- 4 files changed, 157 insertions(+), 611 deletions(-) delete mode 100644 ff/distributed/ff_dgroup2.hpp diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index f821b016..0693fc0a 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -1,53 +1,39 @@ -#ifndef FF_DGROUP_H -#define FF_DGROUP_H - #include -#include -#include -#include -#include -#include -#include #include #include #include #include +#include + +#include #ifdef DFF_MPI #include #include #endif -#include - -#include // used for operators constexpr evaulations - +template +T getBackAndPop(std::vector& v){ + T b = v.back(); + v.pop_back(); + return b; +} namespace ff{ +class dGroup : public ff::ff_farm { -class dGroup; - -enum IOTypes { IN, OUT }; - -template -class MySet { -private: - dGroup* group; -public: - MySet(dGroup* group): group(group){ } - - MySet& operator<<(ff_node*); - - bool check_inout(ff_node* node); -}; - -class dGroups; - -class dGroup : public ff_farm { + static inline std::unordered_map vector2UMap(const std::vector v){ + std::unordered_map output; + for(size_t i = 0; i < v.size(); i++) output[v[i]] = i; + return output; + } - friend class MySet; - friend class MySet; + static inline std::map vector2Map(const std::vector v){ + std::map output; + for(size_t i = 0; i < v.size(); i++) output[v[i]] = i; + return output; + } struct ForwarderNode : ff_node { ForwarderNode(std::function f){ @@ -58,397 +44,172 @@ class dGroup : public ff_farm { } void* svc(void* input){return input;} }; -private: - ff_node * parentStructure; - ff_node * level1BB; - - ff_endpoint endpoint; - std::vector destinations; - int expectedInputConnections; - std::set in_, out_, inout_; - - bool isSource(){return in_.empty() && inout_.empty();} - bool isSink(){return out_.empty() && inout_.empty();} - - static bool isIncludedIn(const ff::svector& firstSet, std::set& secondSet){ - for (const ff_node* n : firstSet) - if (std::find(secondSet.begin(), secondSet.end(), n) == secondSet.end()) - return false; - return true; + static ff_node* buildWrapperIN(ff_node* n){ + if (n->isMultiOutput()) return new ff_comb(new WrapperIN(new ForwarderNode(n->deserializeF)), n, true, false); + return new WrapperIN(n); } - ff_node* buildWrapper(ff_node* n, IOTypes t){ - if (t == IOTypes::IN){ - if (n->isMultiOutput()) - return new ff_comb(new WrapperIN(new ForwarderNode(n->deserializeF), true), n, true, false); - return new WrapperIN(n, 1, false); - } - - if (t == IOTypes::OUT){ - int id = getIndexOfNode(level1BB, n, nullptr, IOTypes::OUT); - if (n->isMultiInput()) - return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF), id, 1, true), false, true); - return new WrapperOUT(n, id, 1, false); - } - - return nullptr; + static ff_node* buildWrapperOUT(ff_node* n, int id, int outputChannels){ + if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF), id, 1, true), false, true); + return new WrapperOUT(n, id, outputChannels); } - bool replaceWrapper(const ff::svector& list, IOTypes t){ - for (ff_node* node : list){ - ff_node* parentBB = getBB(this->parentStructure, node); - if (parentBB != nullptr){ - - ff_node* wrapped = buildWrapper(node, t); - - if (parentBB->isPipe()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); - continue; - } - - if (parentBB->isAll2All()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); - continue; - } - - if (parentBB->isComp()){ - reinterpret_cast(parentBB)->change_node(node, wrapped, true, true); - continue; - } +public: + dGroup(ff_IR& ir){ - return false; - } - return false; + int outputChannels = 0; + if (ir.hasSender){ + ff::cout << "Size of routintable " << ir.routingTable.size() << std::endl; + outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+f.second.first.size();}); + ff::cout << "Outputchannels: " << outputChannels << std::endl; } - return true; - } + if (ir.isVertical()){ - static inline bool isSeq(ff_node* n){return (!n->isAll2All() && !n->isComp() && !n->isFarm() && !n->isOFarm() && !n->isPipe());} + std::vector reverseOutputIndexes(ir.hasLeftChildren() ? ir.outputL.rbegin() : ir.outputR.rbegin(), ir.hasLeftChildren() ? ir.outputL.rend() : ir.outputR.rend()); + for(ff_node* child: (ir.hasLeftChildren() ? ir.L : ir.R)){ + ff::svector inputs; child->get_in_nodes(inputs); + ff::svector outputs; child->get_out_nodes(outputs); + + // handle the case we have a pipe (or more nested) with just one sequential stage (not a combine) + if (inputs.size() == 1 && outputs.size() == 1 && inputs[0] == outputs[0]) + child = inputs[0]; + + if (isSeq(child)){ + if (ir.hasReceiver && ir.hasSender) + workers.push_back(new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes))); + else if (ir.hasReceiver) + workers.push_back(buildWrapperIN(child)); + else workers.push_back(buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels)); + + } else { + if (ir.hasReceiver){ + for(ff_node* input : inputs){ + ff_node* inputParent = getBB(child, input); + if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); //cleanup?? removefromcleanuplist?? + } + } - bool processBB(ff_node* bb, std::set in_C, std::set out_C){ - if (isSeq(bb)){ - ff::svector svectBB(1); svectBB.push_back(bb); - if (isSource() && this->out_.find(bb) != this->out_.end() /*&& replaceWrapper(svectBB, this->out_)*/){ - this->add_workers({buildWrapper(bb, IOTypes::OUT)}); - return true; - } + if (ir.hasSender){ + for(ff_node* output : outputs){ + ff_node* outputParent = getBB(child, output); + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes), outputChannels), true); // cleanup?? removefromcleanuplist?? + } + } - if (isSink() && this->in_.find(bb) != this->in_.end() /*&& replaceWrapper(svectBB, this->in_)*/){ - this->add_workers({buildWrapper(bb, IOTypes::IN)}); - return true; + workers.push_back(child); + } } - return false; - } - - ff::svector in_nodes, out_nodes; - bb->get_in_nodes(in_nodes); - - if (!isSource() && !isIncludedIn(in_nodes, in_C)) - return false; + if (ir.hasReceiver) + this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); - bb->get_out_nodes(out_nodes); - - if (!isSink() && !isIncludedIn(out_nodes, out_C)) - return false; - - if ((isSource() || replaceWrapper(in_nodes, IOTypes::IN)) && (isSink() || replaceWrapper(out_nodes, IOTypes::OUT))){ - this->add_workers({bb}); // here the bb is already modified with the wrapper - return true; + if (ir.hasSender) + this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName), true); } - - return false; - } - - static bool isStageOf(ff_node* n, ff_pipeline* p){ - for (const ff_node* s : p->getStages()) - if (s == n) return true; - - return false; - } - - static ff::svector getIONodes(ff_node* n, IOTypes t){ - ff::svector sv; - switch (t) { - case IOTypes::IN: n->get_in_nodes(sv); break; - case IOTypes::OUT: n->get_out_nodes(sv); break; - } - return sv; - } - - /** - * Retrieve the index of the node wrapper (or the alternative) in the original shared memory graph. - * Depending on the type t (IN - OUT) we get the index either in input or in output. - */ - static int getIndexOfNode(ff_node* bb, ff_node* wrapper, ff_node* alternative, IOTypes t){ - if (bb->isAll2All()){ - ff_a2a* a2a = (ff_a2a*) bb; - int index = 0; - for (ff_node* n : a2a->getFirstSet()){ - for (const ff_node* io : getIONodes(n, t)){ - if (io == wrapper || io == alternative) - return index; - index++; + else { // the group is horizontal! + ff_a2a* innerA2A = new ff_a2a(); + + std::vector reverseLeftOutputIndexes(ir.outputL.rbegin(), ir.outputL.rend()); + + std::unordered_map localRightWorkers = vector2UMap(ir.inputR); + std::vector firstSet; + for(ff_node* child : ir.L){ + if (isSeq(child)) + if (ir.isSource){ + ff_node* wrapped = new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers); + wrapped->skipallpop(true); + firstSet.push_back(wrapped); + } else { + firstSet.push_back(new ff_comb(new WrapperIN(new ForwarderNode(child->getDeserializationFunction()), 1, true), new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers), true, true)); + } + else { + + ff::svector inputs; child->get_in_nodes(inputs); + for(ff_node* input : inputs){ + if (ir.isSource) + input->skipallpop(true); + else { + ff_node* inputParent = getBB(child, input); + if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); // cleanup??? remove_fromcleanuplist?? + } + } + + ff::svector outputs; child->get_out_nodes(outputs); + for(ff_node* output : outputs){ + ff_node* outputParent = getBB(child, output); + if (outputParent) outputParent->change_node(output, new EmitterAdapter(output, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers) , true); // cleanup??? remove_fromcleanuplist?? + } + firstSet.push_back(child); //ondemand?? cleanup?? } } + // add the Square Box Left, just if we have a receiver! + if (ir.hasReceiver) + firstSet.push_back(new SquareBoxLeft(localRightWorkers)); // ondemand?? + + std::transform(firstSet.begin(), firstSet.end(), firstSet.begin(), [](ff_node* n) -> ff_node* { + if (!n->isPipe()) + return new ff_Pipe(n); + return n; + }); - index = 0; - for (ff_node* n : a2a->getSecondSet()){ - for (ff_node* io : getIONodes(n, t)) - if (io == wrapper || io == alternative) - return index; - else index++; - } - } - - int index = 0; - for (ff_node* io : getIONodes(bb, t)) - if (io == wrapper || io == alternative) - return index; - else index++; - - return 0; - } - - /** - * Given a node pointer w and an hint of the type of Wrapper t, get the poiinter of the original wrapped node. - */ - static ff_node* getOriginal(ff_node* w, IOTypes t){ - // if the wrapper is actually a composition, return the correct address of the orginal node! - if (w->isComp()){ - switch (t){ - case IOTypes::IN: return reinterpret_cast(w)->getRight(); - case IOTypes::OUT: return reinterpret_cast(w)->getLeft(); - default: return nullptr; - } - } - switch (t){ - case IOTypes::IN: return reinterpret_cast(w)->n; - case IOTypes::OUT: return reinterpret_cast(w)->n; - default: return nullptr; - } - } - - std::map buildRoutingTable(ff_node* level1BB){ - std::map routingTable; - int localIndex = 0; - for (ff_node* inputBB : this->getWorkers()){ - ff::svector inputs; inputBB->get_in_nodes(inputs); - for (ff_node* input : inputs) - routingTable[getIndexOfNode(level1BB, input, getOriginal(input, IOTypes::IN), IOTypes::IN)] = localIndex++; - } - return routingTable; - } - - int buildFarm(ff_pipeline* basePipe = nullptr){ - - // find the 1 level builiding block which containes the group (level 1 BB means a BB which is a stage in the main piepline) - //ff_node* level1BB = this->parentStructure; - while(!isStageOf(level1BB, basePipe)){ - level1BB = getBB(basePipe, level1BB); - if (!level1BB || level1BB == basePipe) throw FF_Exception("A group were created from a builiding block not included in the Main Pipe! :("); - } - - int onDemandQueueLength = 0; bool onDemandReceiver = false; bool onDemandSender = false; - - if (this->parentStructure->isPipe()) - processBB(this->parentStructure, in_, out_); + innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? + - if (this->parentStructure->isAll2All()){ - ff_a2a * a2a = (ff_a2a*) this->parentStructure; + std::vector reverseRightOutputIndexes(ir.outputR.rbegin(), ir.outputR.rend()); + std::vector secondSet; + for(ff_node* child : ir.R){ + if (isSeq(child)) + secondSet.push_back( + (ir.isSink) ? (ff_node*)new CollectorAdapter(child, ir.outputL) + : (ff_node*)new ff_comb(new CollectorAdapter(child, ir.outputL), new WrapperOUT(new ForwarderNode(child->getSerializationFunction()), getBackAndPop(reverseRightOutputIndexes), 1, true), true, true) + ); + else { + ff::svector inputs; child->get_in_nodes(inputs); + for(ff_node* input : inputs){ + ff_node* inputParent = getBB(child, input); + if (inputParent) inputParent->change_node(input, new CollectorAdapter(input, ir.outputL), true); //cleanup?? remove_fromcleanuplist?? + } - if (!processBB(a2a, in_, out_)){ // if the user has not wrapped the whole a2a, expand its sets - bool first = false, second = false; - - for(ff_node* bb : a2a->getFirstSet()) - if (processBB(bb, in_, out_)) - first = true; - - for(ff_node* bb : a2a->getSecondSet()) - if (processBB(bb, in_, out_)) - second = true; - - // check on input/output nodes, used for ondemand stuff and for checking collision between nodes - if (!first && !second){ - for (const auto& n : this->inout_){ - if (std::find(a2a->getFirstSet().begin(), a2a->getFirstSet().end(), n) != a2a->getFirstSet().end()) - first = true; - else if (std::find(a2a->getSecondSet().begin(), a2a->getSecondSet().end(), n) != a2a->getSecondSet().end()) - second = true; + if (!ir.isSink){ + ff::svector outputs; child->get_out_nodes(outputs); + for(ff_node* output : outputs){ + ff_node* outputParent = getBB(child, output); + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseRightOutputIndexes), outputChannels), true); //cleanup?? removefromcleanuplist? } } - - // if the ondemand scheduling is set in the a2a, i need to adjust the queues of this farm in order to implement the ondemand policy - if (a2a->ondemand_buffer() > 0){ - onDemandQueueLength = a2a->ondemand_buffer(); - if (first) {this->setOutputQueueLength(1, true); onDemandSender = true;} // always set to 1 the length of the queue between worker and collector (SOURCE side) - if (second) {this->set_scheduling_ondemand(a2a->ondemand_buffer()); onDemandReceiver = true;} // set the right length of the queue between emitter and worker (SINK side) + secondSet.push_back(child); } - - if (first && second) throw FF_Exception("Nodes from first and second of an A2A cannot belong to the same group!!"); } + + // add the SQuareBox Right, iif there is a sender! + if (ir.hasSender) + secondSet.push_back(new SquareBoxRight); - } - - // in/out nodes left to be added to the farm. The next lines does it - for (ff_node* n : this->inout_){ - //std::cout << "Added INOUT node" << std::endl; - this->add_workers({new WrapperINOUT(n, getIndexOfNode(level1BB, n, nullptr, IOTypes::OUT))}); - } - - if (this->getNWorkers() == 0) - return -1; - - // create receiver - Proto currentProto = dGroups::Instance()->usedProtocol; - if (!isSource()){ - if (currentProto == Proto::TCP){ - if (onDemandReceiver) - this->add_emitter(new ff_dreceiverOD(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! - else - this->add_emitter(new ff_dreceiver(this->endpoint, this->expectedInputConnections, buildRoutingTable(level1BB))); - } + std::transform(secondSet.begin(), secondSet.end(), secondSet.begin(), [](ff_node* n) -> ff_node* { + if (!n->isPipe()) + return new ff_Pipe(n); + return n; + }); - #ifdef DFF_MPI - if (currentProto == Proto::MPI){ - if (onDemandReceiver) - this->add_emitter(new ff_dreceiverMPIOD(this->expectedInputConnections, buildRoutingTable(level1BB))); // set right parameters HERE!! - else - this->add_emitter(new ff_dreceiverMPI(this->expectedInputConnections, buildRoutingTable(level1BB))); - } - #endif + innerA2A->add_secondset(secondSet); // cleanup?? + workers.push_back(innerA2A); - this->cleanup_emitter(); - } - // create sender - if (!isSink()){ - if (currentProto == Proto::TCP){ - if (onDemandSender) - this->add_collector(new ff_dsenderOD(this->destinations, onDemandQueueLength), true); - else - this->add_collector(new ff_dsender(this->destinations), true); - } + if (ir.hasReceiver) + this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); - #ifdef DFF_MPI - if (currentProto == Proto::MPI){ - if (onDemandSender) - this->add_collector(new ff_dsenderMPIOD(this->destinations, onDemandQueueLength), true); - else - this->add_collector(new ff_dsenderMPI(this->destinations), true); - } - #endif + if (ir.hasSender) + this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB) , true); + } + if (this->getNWorkers() == 0){ + std::cerr << "The farm implementing the distributed group is empty! There might be an error! :(\n"; + abort(); } - - return 0; - } - -public: - dGroup(ff_node* parent, std::string label): parentStructure(parent), level1BB(parent), endpoint(), destinations(), expectedInputConnections(0), in(this), out(this){ - dGroups::Instance()->addGroup(label, this); - } - - ~dGroup() { - // TO DO: check the cleanup with the new way of generate wrapping! - /*if (!prepared) { - for(auto s: in_) - delete s.second; - for(auto s: out_) - delete s.second; - for(auto s: inout_) - delete s.second; - } - */ - } - - int run(bool skip_init=false) override {return 0;} - - int run(ff_node* baseBB, bool skip_init=false) override { - buildFarm(reinterpret_cast(baseBB)); - return ff_farm::run(skip_init); } + - - void setEndpoint(ff_endpoint e){ - this->endpoint = e; - } - - ff_endpoint getEndpoint(){return this->endpoint;} - - ff_node* getParent() const { return parentStructure; } - - void setDestination(ff_endpoint e){ this->destinations.push_back(std::move(e));} - - void setExpectedInputConnections(int connections){this->expectedInputConnections = connections;} - - MySet in; - MySet out; }; - -template<> -MySet& MySet::operator<<(ff_node* node){ - if (!node->isDeserializable()){ - error("The annotated node is not able to deserialize the type!\n"); - abort(); - } - - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - if (!this->group->out_.extract(node).empty()) // if the node was also annotedted in output just create the WrapperINOUT - this->group->inout_.insert(node); - else - this->group->in_.insert(node); - - return *this; -} - -template<> -MySet& MySet::operator<<(ff_node* node){ - if (!node->isSerializable()){ - error("The annotated node is not able to serialize the type!\n"); - abort(); - } - - if (check_inout(node)) return *this; // the node is already processed in input and output, just skip it! - - if (!this->group->in_.extract(node).empty()) // if the node was also annotedted in output just create the WrapperINOUT - this->group->inout_.insert(node); - else - this->group->out_.insert(node); - - return *this; -} - -template -bool MySet::check_inout(ff_node* node){ - return this->group->inout_.find(node) != this->group->inout_.end(); - } - -void dGroups::consolidateGroups(){ - -} - -bool dGroups::isBuildByMyBuildingBlock(const std::string gName) { - auto g1 = reinterpret_cast(groups[gName]); - auto g2 = reinterpret_cast(groups[runningGroup]); - - return g1->getParent() == g2->getParent(); } - - -// redefinition of createGroup methods for ff_a2a and ff_pipeline -ff::dGroup& ff_a2a::createGroup(std::string name){ - dGroup * g = new dGroup(this, std::move(name)); - return *g; -} - -ff::dGroup& ff_pipeline::createGroup(std::string name){ - dGroup * g = new dGroup(this, std::move(name)); - return *g; -} - - -#endif diff --git a/ff/distributed/ff_dgroup2.hpp b/ff/distributed/ff_dgroup2.hpp deleted file mode 100644 index d4a0fbd3..00000000 --- a/ff/distributed/ff_dgroup2.hpp +++ /dev/null @@ -1,215 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#ifdef DFF_MPI -#include -#include -#endif - - -template -T getBackAndPop(std::vector& v){ - T b = v.back(); - v.pop_back(); - return b; -} - -namespace ff{ -class dGroup2 : public ff::ff_farm { - - static inline std::unordered_map vector2UMap(const std::vector v){ - std::unordered_map output; - for(size_t i = 0; i < v.size(); i++) output[v[i]] = i; - return output; - } - - static inline std::map vector2Map(const std::vector v){ - std::map output; - for(size_t i = 0; i < v.size(); i++) output[v[i]] = i; - return output; - } - - struct ForwarderNode : ff_node { - ForwarderNode(std::function f){ - this->serializeF = f; - } - ForwarderNode(std::function f){ - this->deserializeF = f; - } - void* svc(void* input){return input;} - }; - - static ff_node* buildWrapperIN(ff_node* n){ - if (n->isMultiOutput()) return new ff_comb(new WrapperIN(new ForwarderNode(n->deserializeF)), n, true, false); - return new WrapperIN(n); - } - - static ff_node* buildWrapperOUT(ff_node* n, int id, int outputChannels){ - if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF), id, 1, true), false, true); - return new WrapperOUT(n, id, outputChannels); - } - -public: - dGroup2(ff_IR& ir){ - - int outputChannels = 0; - if (ir.hasSender){ - ff::cout << "Size of routintable " << ir.routingTable.size() << std::endl; - outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+f.second.first.size();}); - ff::cout << "Outputchannels: " << outputChannels << std::endl; - } - if (ir.isVertical()){ - - std::vector reverseOutputIndexes(ir.hasLeftChildren() ? ir.outputL.rbegin() : ir.outputR.rbegin(), ir.hasLeftChildren() ? ir.outputL.rend() : ir.outputR.rend()); - for(ff_node* child: (ir.hasLeftChildren() ? ir.L : ir.R)){ - ff::svector inputs; child->get_in_nodes(inputs); - ff::svector outputs; child->get_out_nodes(outputs); - - // handle the case we have a pipe (or more nested) with just one sequential stage (not a combine) - if (inputs.size() == 1 && outputs.size() == 1 && inputs[0] == outputs[0]) - child = inputs[0]; - - if (isSeq(child)){ - if (ir.hasReceiver && ir.hasSender) - workers.push_back(new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes))); - else if (ir.hasReceiver) - workers.push_back(buildWrapperIN(child)); - else workers.push_back(buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels)); - - } else { - if (ir.hasReceiver){ - for(ff_node* input : inputs){ - ff_node* inputParent = getBB(child, input); - if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); //cleanup?? removefromcleanuplist?? - } - } - - if (ir.hasSender){ - for(ff_node* output : outputs){ - ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes), outputChannels), true); // cleanup?? removefromcleanuplist?? - } - } - - workers.push_back(child); - } - } - - if (ir.hasReceiver) - this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); - - if (ir.hasSender) - this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName), true); - } - else { // the group is horizontal! - ff_a2a* innerA2A = new ff_a2a(); - - std::vector reverseLeftOutputIndexes(ir.outputL.rbegin(), ir.outputL.rend()); - - std::unordered_map localRightWorkers = vector2UMap(ir.inputR); - std::vector firstSet; - for(ff_node* child : ir.L){ - if (isSeq(child)) - if (ir.isSource){ - ff_node* wrapped = new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers); - wrapped->skipallpop(true); - firstSet.push_back(wrapped); - } else { - firstSet.push_back(new ff_comb(new WrapperIN(new ForwarderNode(child->getDeserializationFunction()), 1, true), new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers), true, true)); - } - else { - - ff::svector inputs; child->get_in_nodes(inputs); - for(ff_node* input : inputs){ - if (ir.isSource) - input->skipallpop(true); - else { - ff_node* inputParent = getBB(child, input); - if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); // cleanup??? remove_fromcleanuplist?? - } - } - - ff::svector outputs; child->get_out_nodes(outputs); - for(ff_node* output : outputs){ - ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, new EmitterAdapter(output, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers) , true); // cleanup??? remove_fromcleanuplist?? - } - firstSet.push_back(child); //ondemand?? cleanup?? - } - } - // add the Square Box Left, just if we have a receiver! - if (ir.hasReceiver) - firstSet.push_back(new SquareBoxLeft(localRightWorkers)); // ondemand?? - - std::transform(firstSet.begin(), firstSet.end(), firstSet.begin(), [](ff_node* n) -> ff_node* { - if (!n->isPipe()) - return new ff_Pipe(n); - return n; - }); - - innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? - - - std::vector reverseRightOutputIndexes(ir.outputR.rbegin(), ir.outputR.rend()); - std::vector secondSet; - for(ff_node* child : ir.R){ - if (isSeq(child)) - secondSet.push_back( - (ir.isSink) ? (ff_node*)new CollectorAdapter(child, ir.outputL) - : (ff_node*)new ff_comb(new CollectorAdapter(child, ir.outputL), new WrapperOUT(new ForwarderNode(child->getSerializationFunction()), getBackAndPop(reverseRightOutputIndexes), 1, true), true, true) - ); - else { - ff::svector inputs; child->get_in_nodes(inputs); - for(ff_node* input : inputs){ - ff_node* inputParent = getBB(child, input); - if (inputParent) inputParent->change_node(input, new CollectorAdapter(input, ir.outputL), true); //cleanup?? remove_fromcleanuplist?? - } - - if (!ir.isSink){ - ff::svector outputs; child->get_out_nodes(outputs); - for(ff_node* output : outputs){ - ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseRightOutputIndexes), outputChannels), true); //cleanup?? removefromcleanuplist? - } - } - - secondSet.push_back(child); - } - } - - // add the SQuareBox Right, iif there is a sender! - if (ir.hasSender) - secondSet.push_back(new SquareBoxRight); - - std::transform(secondSet.begin(), secondSet.end(), secondSet.begin(), [](ff_node* n) -> ff_node* { - if (!n->isPipe()) - return new ff_Pipe(n); - return n; - }); - - innerA2A->add_secondset(secondSet); // cleanup?? - workers.push_back(innerA2A); - - - if (ir.hasReceiver) - this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); - - if (ir.hasSender) - this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB) , true); - } - - if (this->getNWorkers() == 0){ - std::cerr << "The farm implementing the distributed group is empty! There might be an error! :(\n"; - abort(); - } - } - - -}; -} diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index eb932121..502df087 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -114,7 +114,7 @@ class dGroups { #endif // buildare il farm dalla rappresentazione intermedia del gruppo che devo rannare - dGroup2 _grp(this->annotatedGroups[this->runningGroup]); + dGroup _grp(this->annotatedGroups[this->runningGroup]); // rannere il farm come sotto! if (_grp.run() < 0){ std::cerr << "Error running the group!" << std::endl; diff --git a/ff/node.hpp b/ff/node.hpp index 852437ee..981d1081 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -512,7 +512,7 @@ class ff_node { #ifdef DFF_ENABLED friend class dGroups; - friend class dGroup2; + friend class dGroup; #endif private: From 81477d2be64c77ccf92e2913b970548343ca142f Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 2 Mar 2022 19:10:04 +0100 Subject: [PATCH 123/202] Fixed endif bug on ff_dGroup.hpp --- ff/distributed/ff_dgroup.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 5856e698..c131054f 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -242,3 +242,5 @@ class dGroup : public ff::ff_farm { }; } +#endif + From 6952d171222008ab5efed060438b35c38309868f Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 2 Mar 2022 19:13:13 +0100 Subject: [PATCH 124/202] Wiped out unused fields in the json parsing code --- ff/distributed/ff_dgroups.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 2ac3bb7a..dae4b603 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -178,7 +178,6 @@ class dGroups { std::string name; std::string address; int port; - std::vector Oconn; template void load( Archive & ar ){ @@ -190,9 +189,6 @@ class dGroups { address = endp[0]; port = std::stoi(endp[1]); } catch (cereal::Exception&) {ar.setNextName(nullptr);} - try { - ar(cereal::make_nvp("OConn", Oconn)); - } catch (cereal::Exception&) {ar.setNextName(nullptr);} } }; From 5d2e73e44ee67351fbd2398c1edaa63132fbdc5f Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 2 Mar 2022 19:32:00 +0100 Subject: [PATCH 125/202] removed 'OConn' from json files, minor compilation fixes in testParametricPerf.cpp --- tests/distributed/test_Farm2.cpp | 92 ---------------------- tests/distributed/test_a2aOnDemand.json | 6 +- tests/distributed/test_a2aOnDemand2.json | 3 +- tests/distributed/test_farm1.cpp | 79 ------------------- tests/distributed/test_group1.json | 9 +-- tests/distributed/test_group10.json | 3 +- tests/distributed/test_group11.json | 3 +- tests/distributed/test_group12.json | 15 ++-- tests/distributed/test_group13.json | 6 +- tests/distributed/test_group14.json | 6 +- tests/distributed/test_group15.json | 3 +- tests/distributed/test_group16.json | 6 +- tests/distributed/test_group17.json | 3 +- tests/distributed/test_group18.json | 6 +- tests/distributed/test_group2.json | 9 +-- tests/distributed/test_group3.json | 3 +- tests/distributed/test_group4.json | 3 +- tests/distributed/test_group5.json | 6 +- tests/distributed/test_group6.json | 3 +- tests/distributed/test_group7.json | 9 +-- tests/distributed/test_group8.json | 6 +- tests/distributed/test_group9.json | 3 +- tests/distributed/test_parametricPerf.cpp | 13 +-- tests/distributed/test_parametricPerf.json | 6 +- 24 files changed, 46 insertions(+), 255 deletions(-) delete mode 100644 tests/distributed/test_Farm2.cpp delete mode 100644 tests/distributed/test_farm1.cpp diff --git a/tests/distributed/test_Farm2.cpp b/tests/distributed/test_Farm2.cpp deleted file mode 100644 index 8e4a6d1d..00000000 --- a/tests/distributed/test_Farm2.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include -#include - - -using namespace ff; - -#define WORKERS 9 - -struct Source : ff_monode_t{ - int numWorker; - Source(int numWorker) : numWorker(numWorker) {} - - std::string* svc(std::string* in){ - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task generated for " + std::to_string(i) + " passed to"), i); - return EOS; - } -}; - -struct Worker : ff_node_t{ - int numWorker; - Worker(int numWorker) : numWorker(numWorker) {} - - std::string* svc(std::string * in){ - std::string * output = new std::string(*in + " " + std::to_string(numWorker)); - delete in; - return output; - } -}; - -struct Sink : ff_minode_t{ - std::string* svc(std::string* in){ - std::cout << *in << " received by Collector from " << get_channel_id() << std::endl; - delete in; - return this->GO_ON; - } -}; - - -int main(int argc, char*argv[]){ - - if (argc != 2){ - std::cerr << "Execute with the index of process!" << std::endl; - return 1; - } - - if (atoi(argv[1]) == 0){ - ff_farm f; - f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, 3, true)); - std::vector workers; - for(int i = 0; i < 3; i++) - workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); - workers.push_back(new WrapperOUT(new SquareBoxEmitter(), 1, true)); - f.add_workers(workers); - f.add_collector(new ff_dsender({ff_endpoint("127.0.0.1", 8001), ff_endpoint("127.0.0.1", 8002)})); - f.run_and_wait_end(); - - } else if (atoi(argv[1]) == 1) { - ff_farm f; - std::map routingTable; - std::vector workers; - int j = 0; - for(int i = 3; i < 6; i++){ - workers.push_back(new WrapperINOUT(new Worker(i), 1, true, FARM_GATEWAY)); - routingTable.emplace(i, j++); - } - f.add_workers(workers); - f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8002), 1, routingTable)); - f.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001))); - f.run_and_wait_end(); - }else { - ff_farm f; - std::map routingTable; - std::vector workers; - int j = 1; - workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); - routingTable.emplace(FARM_GATEWAY, 0); - for(int i = 6; i < WORKERS; i++){ - workers.push_back(new WrapperIN(new Worker(i), 1, true)); - routingTable.emplace(i, j++); - } - - f.add_workers(workers); - f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); - f.add_collector(new CollectorAdapter(new Sink, 6, 3, true), true); - - f.run_and_wait_end(); - } -} \ No newline at end of file diff --git a/tests/distributed/test_a2aOnDemand.json b/tests/distributed/test_a2aOnDemand.json index 240e4334..6061e9b5 100644 --- a/tests/distributed/test_a2aOnDemand.json +++ b/tests/distributed/test_a2aOnDemand.json @@ -2,13 +2,11 @@ "groups" : [ { "endpoint" : "localhost:8003", - "name" : "G0", - "OConn" : ["G1"] + "name" : "G0" }, { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_a2aOnDemand2.json b/tests/distributed/test_a2aOnDemand2.json index fab97f4a..79e1b1af 100644 --- a/tests/distributed/test_a2aOnDemand2.json +++ b/tests/distributed/test_a2aOnDemand2.json @@ -2,8 +2,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_farm1.cpp b/tests/distributed/test_farm1.cpp deleted file mode 100644 index b31ba5ef..00000000 --- a/tests/distributed/test_farm1.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include - - -using namespace ff; - -#define WORKERS 10 - -struct Source : ff_monode_t{ - int numWorker; - Source(int numWorker) : numWorker(numWorker) {} - - std::string* svc(std::string* in){ - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task generated for " + std::to_string(i) + " passed to"), i); - return EOS; - } -}; - -struct Worker : ff_node_t{ - int numWorker; - Worker(int numWorker) : numWorker(numWorker) {} - - std::string* svc(std::string * in){ - std::string * output = new std::string(*in + " " + std::to_string(numWorker)); - delete in; - return output; - } -}; - -struct Sink : ff_minode_t{ - std::string* svc(std::string* in){ - std::cout << *in << " received by Collector from " << get_channel_id() << std::endl; - delete in; - return this->GO_ON; - } -}; - - -int main(int argc, char*argv[]){ - - if (argc != 2){ - std::cerr << "Execute with the index of process!" << std::endl; - return 1; - } - - if (atoi(argv[1]) == 0){ - ff_farm f; - f.add_emitter(new EmitterAdapter(new Source(WORKERS), WORKERS, WORKERS/2, true)); - std::vector workers; - for(int i = 0; i < WORKERS/2; i++) - workers.push_back(new WrapperOUT(new Worker(i), 1, true, FARM_GATEWAY)); - workers.push_back(new WrapperOUT(new SquareBoxEmitter(), 1, true)); - f.add_workers(workers); - f.add_collector(new ff_dsender(ff_endpoint("127.0.0.1", 8001))); - f.run_and_wait_end(); - - } else { - ff_farm f; - std::map routingTable; - std::vector workers; - int j = 1; - workers.push_back(new WrapperIN(new SquareBoxCollector(), 1, true)); - routingTable.emplace(FARM_GATEWAY, 0); - for(int i = WORKERS / 2; i < WORKERS; i++){ - workers.push_back(new WrapperIN(new Worker(i), 1, true)); - routingTable.emplace(i, j++); - } - - f.add_workers(workers); - f.add_emitter(new ff_dreceiver(ff_endpoint("127.0.0.1", 8001), 1, routingTable)); - f.add_collector(new CollectorAdapter(new Sink, 5, 5, true), true); - - f.run_and_wait_end(); - } -} \ No newline at end of file diff --git a/tests/distributed/test_group1.json b/tests/distributed/test_group1.json index 8282e4fa..23b0cd91 100644 --- a/tests/distributed/test_group1.json +++ b/tests/distributed/test_group1.json @@ -3,18 +3,15 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2", "G3"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G3", "G4"] + "endpoint": "localhost:8005" }, { "name" : "G3", - "endpoint": "localhost:8006", - "OConn" : ["G2", "G4"] + "endpoint": "localhost:8006" }, { "name" : "G4", diff --git a/tests/distributed/test_group10.json b/tests/distributed/test_group10.json index fab97f4a..79e1b1af 100644 --- a/tests/distributed/test_group10.json +++ b/tests/distributed/test_group10.json @@ -2,8 +2,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_group11.json b/tests/distributed/test_group11.json index fab97f4a..79e1b1af 100644 --- a/tests/distributed/test_group11.json +++ b/tests/distributed/test_group11.json @@ -2,8 +2,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_group12.json b/tests/distributed/test_group12.json index 30557b8d..d318198e 100644 --- a/tests/distributed/test_group12.json +++ b/tests/distributed/test_group12.json @@ -3,28 +3,23 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2", "G3"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G4", "G5"] + "endpoint": "localhost:8005" }, { "name" : "G3", - "endpoint": "localhost:8006", - "OConn" : ["G4", "G5"] + "endpoint": "localhost:8006" }, { "name" : "G4", - "endpoint": "localhost:8007", - "OConn" : ["G6"] + "endpoint": "localhost:8007" }, { "name" : "G5", - "endpoint": "localhost:8008", - "OConn" : ["G6"] + "endpoint": "localhost:8008" }, { "name" : "G6", diff --git a/tests/distributed/test_group13.json b/tests/distributed/test_group13.json index eb42a9ed..8149c994 100644 --- a/tests/distributed/test_group13.json +++ b/tests/distributed/test_group13.json @@ -2,13 +2,11 @@ "groups" : [ { "endpoint": "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G3"] + "endpoint": "localhost:8005" }, { "name" : "G3", diff --git a/tests/distributed/test_group14.json b/tests/distributed/test_group14.json index beae1ddf..9d3d2456 100644 --- a/tests/distributed/test_group14.json +++ b/tests/distributed/test_group14.json @@ -2,13 +2,11 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G3"] + "endpoint": "localhost:8005" }, { "name" : "G3", diff --git a/tests/distributed/test_group15.json b/tests/distributed/test_group15.json index fab97f4a..79e1b1af 100644 --- a/tests/distributed/test_group15.json +++ b/tests/distributed/test_group15.json @@ -2,8 +2,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_group16.json b/tests/distributed/test_group16.json index 72bcbe71..c3cd2434 100644 --- a/tests/distributed/test_group16.json +++ b/tests/distributed/test_group16.json @@ -2,13 +2,11 @@ "groups" : [ { "endpoint" : "localhost:9004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:9005", - "OConn" : ["G3"] + "endpoint": "localhost:9005" }, { "name" : "G3", diff --git a/tests/distributed/test_group17.json b/tests/distributed/test_group17.json index 36ddce0b..f5873926 100644 --- a/tests/distributed/test_group17.json +++ b/tests/distributed/test_group17.json @@ -2,8 +2,7 @@ "groups" : [ { "endpoint" : "localhost:9004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_group18.json b/tests/distributed/test_group18.json index 6efb3eea..244fbf8b 100644 --- a/tests/distributed/test_group18.json +++ b/tests/distributed/test_group18.json @@ -2,13 +2,11 @@ "groups" : [ { "endpoint" : "localhost:9004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:9005", - "OConn" : ["G3"] + "endpoint": "localhost:9005" }, { "name" : "G3", diff --git a/tests/distributed/test_group2.json b/tests/distributed/test_group2.json index 579293e3..7426a2c7 100644 --- a/tests/distributed/test_group2.json +++ b/tests/distributed/test_group2.json @@ -3,18 +3,15 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2", "G3"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G3", "G4"] + "endpoint": "localhost:8005" }, { "name" : "G3", - "endpoint": "localhost:8006", - "OConn" : ["G2", "G4"] + "endpoint": "localhost:8006" }, { "name" : "G4", diff --git a/tests/distributed/test_group3.json b/tests/distributed/test_group3.json index 2d846cba..7db5d32d 100644 --- a/tests/distributed/test_group3.json +++ b/tests/distributed/test_group3.json @@ -3,8 +3,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_group4.json b/tests/distributed/test_group4.json index 2d846cba..7db5d32d 100644 --- a/tests/distributed/test_group4.json +++ b/tests/distributed/test_group4.json @@ -3,8 +3,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_group5.json b/tests/distributed/test_group5.json index c9eb9a3c..2f8501e4 100644 --- a/tests/distributed/test_group5.json +++ b/tests/distributed/test_group5.json @@ -3,13 +3,11 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2", "G3"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G1", "G3"] + "endpoint": "localhost:8005" }, { "name" : "G3", diff --git a/tests/distributed/test_group6.json b/tests/distributed/test_group6.json index 2d846cba..7db5d32d 100644 --- a/tests/distributed/test_group6.json +++ b/tests/distributed/test_group6.json @@ -3,8 +3,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_group7.json b/tests/distributed/test_group7.json index 8282e4fa..23b0cd91 100644 --- a/tests/distributed/test_group7.json +++ b/tests/distributed/test_group7.json @@ -3,18 +3,15 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2", "G3"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G3", "G4"] + "endpoint": "localhost:8005" }, { "name" : "G3", - "endpoint": "localhost:8006", - "OConn" : ["G2", "G4"] + "endpoint": "localhost:8006" }, { "name" : "G4", diff --git a/tests/distributed/test_group8.json b/tests/distributed/test_group8.json index beae1ddf..9d3d2456 100644 --- a/tests/distributed/test_group8.json +++ b/tests/distributed/test_group8.json @@ -2,13 +2,11 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", - "endpoint": "localhost:8005", - "OConn" : ["G3"] + "endpoint": "localhost:8005" }, { "name" : "G3", diff --git a/tests/distributed/test_group9.json b/tests/distributed/test_group9.json index fab97f4a..79e1b1af 100644 --- a/tests/distributed/test_group9.json +++ b/tests/distributed/test_group9.json @@ -2,8 +2,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 94c63cfd..3f7c474c 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -160,7 +160,10 @@ struct MiNode : ff::ff_minode_t{ int main(int argc, char*argv[]){ - DFF_Init(argc, argv); + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } if (argc < 8){ std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_sx #np_dx #nwXp" << std::endl; @@ -180,8 +183,6 @@ int main(int argc, char*argv[]){ ff::ff_a2a a2a; - mainPipe.add_stage(&a2a); - std::vector sxWorkers; std::vector dxWorkers; @@ -195,20 +196,20 @@ int main(int argc, char*argv[]){ a2a.add_secondset(dxWorkers); for(int i = 0; i < numProcSx; i++){ - auto& g = a2a.createGroup(std::string("S")+std::to_string(i)); + auto g = a2a.createGroup(std::string("S")+std::to_string(i)); for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ g << sxWorkers[j]; } } for(int i = 0; i < numProcDx; i++){ - auto& g = a2a.createGroup(std::string("D")+std::to_string(i)); + auto g = a2a.createGroup(std::string("D")+std::to_string(i)); for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ g << dxWorkers[j]; } } - if (mainPipe.run_and_wait_end()<0) { + if (a2a.run_and_wait_end()<0) { error("running mainPipe\n"); return -1; } diff --git a/tests/distributed/test_parametricPerf.json b/tests/distributed/test_parametricPerf.json index 49a75d52..1671aed5 100644 --- a/tests/distributed/test_parametricPerf.json +++ b/tests/distributed/test_parametricPerf.json @@ -2,14 +2,12 @@ "groups" : [ { "endpoint" : "localhost:8000", - "name" : "S0", - "OConn" : ["D0", "D1"] + "name" : "S0" } , { "endpoint" : "localhost:8001", - "name" : "S1", - "OConn" : ["D0", "D1"] + "name" : "S1" } , { From 107de9af74987cc386a7099474c46d3c9c5f3fe3 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 10 Mar 2022 17:50:03 +0100 Subject: [PATCH 126/202] added the new method change_outputqueuesize() to the ff_node class. test_changesize shows how to use this new method. --- ff/buffer.hpp | 22 +++++- ff/node.hpp | 11 +++ ff/ubuffer.hpp | 42 +++++++++++- tests/Makefile | 2 +- tests/test_changesize.cpp | 138 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 tests/test_changesize.cpp diff --git a/ff/buffer.hpp b/ff/buffer.hpp index cb05a574..513b50ce 100644 --- a/ff/buffer.hpp +++ b/ff/buffer.hpp @@ -115,8 +115,8 @@ class SWSR_Ptr_Buffer { volatile unsigned long pwrite; ALIGN_TO_POST(CACHE_LINE_SIZE) #endif - const unsigned long size; - void ** buf; + size_t size; + void ** buf; #if defined(SWSR_MULTIPUSH) /* massimot: experimental code (see multipush) @@ -207,6 +207,24 @@ class SWSR_Ptr_Buffer { * \return The size of the buffer. */ inline size_t buffersize() const { return size; }; + + /** + * It changes the size of the queue WITHOUT reallocating + * the internal buffer. It should be used mainly for + * reducing the size of the queue or to restore it after + * is has been previously reduced. + * + * WARNING: this is very a dangerous operation if executed + * while the queue is being used; if wrongly used, it + * may lead to data loss or memory corruption! + * + */ + size_t changesize(size_t newsz) { + size_t tmp=size; + size=newsz; + return tmp; + } + /** * Push method: push the input value into the queue. A Write Memory diff --git a/ff/node.hpp b/ff/node.hpp index 83b13123..abcdf55e 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1031,6 +1031,17 @@ class ff_node { virtual bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) { return false;} + /** + * Change the size of the outputchannel. + * WARNING: this method should not be used if the queue is being used!!!! + * + */ + virtual bool change_outputqueuesize(size_t newsz, size_t &oldsz) { + if (!out) { oldsz=0; return false; } + oldsz = out->changesize(newsz); + return true; + } + #if defined(FF_TASK_CALLBACK) virtual void callbackIn(void * =NULL) { } virtual void callbackOut(void * =NULL) { } diff --git a/ff/ubuffer.hpp b/ff/ubuffer.hpp index 303fc044..34baef37 100644 --- a/ff/ubuffer.hpp +++ b/ff/ubuffer.hpp @@ -183,6 +183,25 @@ class BufferPool { while(inuse.pop(&p.b2)) release(p.b1); } + // + // WARNING: using this call in the wrong place is very dangerous!!!! + // + void changesize(size_t newsz) { + // the inuse queue should be empty + union { INTERNAL_BUFFER_T * b1; void * b2;} p; + while (inuse.pop(&p.b2)) + assert(1==0); + dynqueue tmp; + while(bufcache.pop(&p.b2)) { + p.b1->changesize(newsz); + tmp.push(p.b2); + } + while(tmp.pop(&p.b2)) { + bufcache.push(p.b2); + } + } + + private: #if defined(UBUFFER_STATS) unsigned long miss,hit; @@ -449,6 +468,27 @@ class uSWSR_Ptr_Buffer { return buf_w->buffersize(); }; + /** + * It changes the size of the queue WITHOUT reallocating + * the internal buffers. It should be used mainly for + * reducing the size of the queue or to restore it after + * is has been previously reduced. + * + * WARNING: this is very a dangerous operation if executed + * while the queue is being used; if wrongly used, it + * may lead to data loss or memory corruption! + * + */ + size_t changesize(size_t newsz) { + assert(buf_r == buf_w); // just a sanity check that the queue is not being used + size_t tmp=buf_w->changesize(newsz); + assert(size == tmp); + size = newsz; + pool.changesize(newsz); + return tmp; + } + + /** * \brief number of elements in the queue * \note This is just a rough estimation of the actual queue length. Not really possible @@ -512,7 +552,7 @@ class uSWSR_Ptr_Buffer { #endif unsigned long in_use_buffers; // used to estimate queue length - const unsigned long size; + unsigned long size; bool fixedsize; BufferPool pool; }; diff --git a/tests/Makefile b/tests/Makefile index ad1dcfa2..5d57ac81 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+farm2 test_farm+A2A test_farm+A2A2 test_farm+A2A3 test_farm+A2A4 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+farm2 test_farm+A2A test_farm+A2A2 test_farm+A2A3 test_farm+A2A4 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode test_changesize #test_taskf2 test_taskf3 diff --git a/tests/test_changesize.cpp b/tests/test_changesize.cpp new file mode 100644 index 00000000..7f37963a --- /dev/null +++ b/tests/test_changesize.cpp @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ +/* + * + * |--> Worker0 -->| + * | | + * Emitter --> |--> Worker1 -->|--> Collector + * | | + * | ...... | + * |--> WorkerK -->| + * + * This program tests the change_outputqueuesize() method when called in the svc_init. + * Only the Worker0 has both input and output queues set to size=1, the other + * Workers use the default queue length as set in the config file. + * + * + */ +/* Author: Massimo Torquati + * + */ +#include +#include + +using namespace ff; + +struct Emitter: ff_monode_t { +Emitter(int nworkers, long ntasks):nworkers(nworkers),ntasks(ntasks) {} + + int svc_init() { + // + // this is the preferred way to change the output queue capacity + // + svector w; + this->get_out_nodes(w); + size_t oldsz; + w[0]->change_outputqueuesize(1, oldsz); + return 0; + } + + + long *svc(long*) { + for(long i=1;i<=ntasks;++i) + ff_send_out_to((long*)i, i % nworkers); + return EOS; + } + int nworkers; + long ntasks; +}; +struct Worker: ff_node_t { + int svc_init() { + if (get_my_id() == 0) { +#if 0 + size_t oldsz; + this->change_outputqueuesize(1,oldsz); +#else + // preferred way to change the output queue capacity + svector w; + this->get_out_nodes(w); + assert(w.size() == 1); + size_t oldsz; + w[0]->change_outputqueuesize(1, oldsz); +#endif + } + return 0; + } + + + long *svc(long *in) { + return in; + } +}; +struct Collector: ff_minode_t { + Collector(long ntasks):ntasks(ntasks) {} + long *svc(long *) { + --ntasks; + return GO_ON; + } + void svc_end() { + if (ntasks != 0) { + std::cerr << "ERROR in the test\n" << "\n"; + abort(); + } + } + long ntasks; +}; + +int main(int argc, char* argv[]) { + int nworkers = 4; + int ntasks = 10000; + if (argc>1) { + if (argc!=3) { + std::cerr << "use: " << argv[0] << " [nworkers ntasks]\n"; + return -1; + } + nworkers= atol(argv[1]); + ntasks = atol(argv[2]); + } + + + Emitter E(nworkers,ntasks); + Collector C(ntasks); + std::vector> W; + for(int i=0;i()); + + ff_Farm<> farm(std::move(W), E, C); + if (farm.run_and_wait_end()== -1) { + error("running farm"); + return -1; + } + fprintf(stdout, "OK!\n"); + return 0; +} + From 018df3717a1238cae787dc3acd2b7927451edbcb Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 14 Mar 2022 19:39:26 +0100 Subject: [PATCH 127/202] added the change_inputqueuesize() methods. More tests. --- ff/node.hpp | 11 +++ tests/Makefile | 2 +- tests/test_changesize.cpp | 13 ++- tests/test_changesize2.cpp | 172 +++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 tests/test_changesize2.cpp diff --git a/ff/node.hpp b/ff/node.hpp index abcdf55e..9be69d2f 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1041,6 +1041,17 @@ class ff_node { oldsz = out->changesize(newsz); return true; } + /** + * Change the size of the inputchannel. + * WARNING: this method should not be used if the queue is being used!!!! + * + */ + virtual bool change_inputqueuesize(size_t newsz, size_t &oldsz) { + if (!in) { oldsz=0; return false; } + oldsz = in->changesize(newsz); + return true; + } + #if defined(FF_TASK_CALLBACK) virtual void callbackIn(void * =NULL) { } diff --git a/tests/Makefile b/tests/Makefile index 5d57ac81..00d156e0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -126,7 +126,7 @@ endif #INCLUDES = -I. $(INCS) INCLUDES = $(INCS) -TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+farm2 test_farm+A2A test_farm+A2A2 test_farm+A2A3 test_farm+A2A4 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode test_changesize +TARGET = simplest test1 test1b test2 test3 test3b test3_farm test4 test5 test6 test7 test8 perf_test1 test_accelerator test_accelerator2 test_accelerator3 test_accelerator_farm+pipe test_accelerator_pipe test_ofarm test_ofarm2 test_accelerator_ofarm test_accelerator_ofarm_multiple_freezing test_accelerator_pipe+farm test_farm+pipe test_farm+pipe2 test_freeze test_masterworker bench_masterworker test_multi_masterworker test_pipe+masterworker test_scheduling test_dt test_torus test_torus2 perf_test_alloc1 perf_test_alloc2 perf_test_alloc3 perf_test_noalloc test_uBuffer test_sendq test_spinBarrier test_multi_input test_multi_input2 test_multi_input3 test_multi_input4 test_multi_input5 test_multi_input6 test_multi_input7 test_multi_input8 test_multi_input9 test_multi_input10 test_multi_input11 test_accelerator+pinning test_dataflow test_dataflow2 test_noinput_pipe test_stopstartthreads test_stopstartthreads2 test_stopstartthreads3 test_stopstartall test_MISD test_parfor test_parfor2 test_parforpipereduce test_dotprod_parfor test_parfor_unbalanced test_parfor_multireduce test_parfor_multireduce2 test_lb_affinity test_farm test_farm2 test_pipe test_pipe2 perf_parfor perf_parfor2 test_graphsearch test_multi_output test_multi_output2 test_multi_output3 test_multi_output4 test_multi_output5 test_multi_output6 test_pool1 test_pool2 test_pool3 test_devicequery test_map test_mdf test_taskf latptr11 test_taskcallbacks test_eosw test_nodeselector test_stats test_dc test_combine test_combine1 test_combine2 test_combine3 test_combine4 test_combine5 test_combine6 test_combine7 test_combine8 test_combine9 test_combine10 test_combine11 test_combine12 test_combine13 test_combine14 test_all-to-all test_all-to-all2 test_all-to-all3 test_all-to-all4 test_all-to-all5 test_all-to-all6 test_all-to-all7 test_all-to-all8 test_all-to-all9 test_all-to-all10 test_all-to-all11 test_all-to-all12 test_all-to-all13 test_all-to-all14 test_all-to-all15 test_all-to-all16 test_all-to-all17 test_all-to-all18 test_all-to-all19 test_optimize test_optimize2 test_optimize3 test_optimize4 test_optimize5 test_all-or-none test_farm+farm test_farm+farm2 test_farm+A2A test_farm+A2A2 test_farm+A2A3 test_farm+A2A4 test_staticallocator test_staticallocator2 test_staticallocator3 test_changenode test_changesize test_changesize2 #test_taskf2 test_taskf3 diff --git a/tests/test_changesize.cpp b/tests/test_changesize.cpp index 7f37963a..07b98499 100644 --- a/tests/test_changesize.cpp +++ b/tests/test_changesize.cpp @@ -52,12 +52,17 @@ Emitter(int nworkers, long ntasks):nworkers(nworkers),ntasks(ntasks) {} int svc_init() { // - // this is the preferred way to change the output queue capacity - // + // This is the preferred way to change the input queue capacity + // of the next node. + // This action cannot be done directly by the worker because, at + // the time of execution of the change_inputqueuesize in the worker, + // the previous node (i.e., this node) might have + // already pushed some data into the queue we want to modify. + // svector w; this->get_out_nodes(w); size_t oldsz; - w[0]->change_outputqueuesize(1, oldsz); + w[0]->change_inputqueuesize(1, oldsz); return 0; } @@ -77,7 +82,7 @@ struct Worker: ff_node_t { size_t oldsz; this->change_outputqueuesize(1,oldsz); #else - // preferred way to change the output queue capacity + // this is the preferred way to change the output queue capacity svector w; this->get_out_nodes(w); assert(w.size() == 1); diff --git a/tests/test_changesize2.cpp b/tests/test_changesize2.cpp new file mode 100644 index 00000000..6678e0a2 --- /dev/null +++ b/tests/test_changesize2.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + * + * + */ + +/* pipe( First, A2A, Last ) + * + * |-> Worker1 ->| + * | | -> Worker2 -->| + * |-> Worker1 ->| | + * First ----> | | -> Worker2 -->| -----> Last + * |-> Worker1 ->| | + * | | -> Worker2 -->| + * |-> Worker1 ->| + * + */ + +/* Author: Massimo Torquati + * + */ + +#include +#include +using namespace ff; + +const int NTASKS = 500; +const int NWORKER1 = 4; +const int NWORKER2 = 3; + +struct First: ff_monode_t { + int svc_init() { + svector w; + this->get_out_nodes(w); + size_t oldsz; + for(size_t i=0;ichange_inputqueuesize(1, oldsz); + return 0; + } + + long* svc(long*) { + int nworkers = this->get_num_outchannels(); + for(int k=0;k { + long *svc(long *in) { return in; } +}; + +struct Worker1: ff_monode_t { + int svc_init() { + svector w; + this->get_out_nodes(w); + size_t oldsz; + for(size_t i=0;ichange_inputqueuesize(1, oldsz); + return 0; + } + long *svc(long *in) { + if (get_my_id()==1) usleep(4000); + ff_send_out(in); + return GO_ON; + } +}; + +struct MultiInputHelper2: ff_minode_t { + long *svc(long *in) { return in; } +}; + +struct Worker2: ff_monode_t { + int svc_init() { + svector w; + this->get_out_nodes(w); + size_t oldsz; + for(size_t i=0;ichange_inputqueuesize(1, oldsz); + return 0; + } + long* svc(long* in) { + if (get_my_id() == 0) usleep(5000); + return in; + } +}; + +struct Last: ff_minode_t { + long* svc(long* in) { + ++cnt; + delete in; + return GO_ON; + } + void svc_end() { + if (cnt != NTASKS*NWORKER1) { + std::cerr << "Error received " << cnt << " instead of " << NTASKS*NWORKER1 << "\n"; + exit(-1); + } + } + + long cnt=0; +}; + +int main() { + // ---- first stage + First first; + + // ----- building all-to-all + + const MultiInputHelper helper; + const MultiInputHelper2 helper2; + const Worker1 w1; + auto comb1 = combine_nodes(helper, w1); + auto comb2 = combine_nodes(helper, w1); + auto comb3 = combine_nodes(helper, w1); + auto comb4 = combine_nodes(helper, w1); + + std::vector firstSet={ &comb1, &comb2, &comb3, &comb4 }; + + assert(firstSet.size() == NWORKER1); + + const Worker2 w2; + std::vector secondSet={ + new ff_comb(helper2,w2), + new ff_comb(helper2,w2), + new ff_comb(helper2,w2) + }; + + assert(firstSet.size() == NWORKER2); + ff_a2a a2a; + a2a.add_firstset(firstSet, 1000, false); + a2a.add_secondset(secondSet, true); + + // ---- last stage + Last last; + + // ---- building the topology + ff_Pipe<> pipe(first, a2a, last); + + if (pipe.run_and_wait_end() <0) { + error("running pipe\n"); + return -1; + } + return 0; +} From 2e42633ce5ce4b4f707649152ded95b581344018 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 18 Mar 2022 16:03:10 +0100 Subject: [PATCH 128/202] New receiver and sender: default policy ondemand --- ff/distributed/ff_dadapters.hpp | 26 ++- ff/distributed/ff_dgroup.hpp | 6 +- ff/distributed/ff_dreceiver.hpp | 32 +-- ff/distributed/ff_dsender.hpp | 363 ++++++++++++++++---------------- 4 files changed, 226 insertions(+), 201 deletions(-) diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index a46c0c4f..1a622330 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -28,11 +28,16 @@ class SquareBoxRight : public ff_minode { class SquareBoxLeft : public ff_monode { std::unordered_map destinations; + int next_rr_destination = 0; public: SquareBoxLeft(const std::unordered_map localDestinations) : destinations(localDestinations) {} void* svc(void* in){ - this->ff_send_out_to(in, destinations[reinterpret_cast(in)->chid]); + if (reinterpret_cast(in)->chid == -1) { + ff_send_out_to(in, next_rr_destination); + next_rr_destination = (next_rr_destination + 1) % destinations.size(); + } + else this->ff_send_out_to(in, destinations[reinterpret_cast(in)->chid]); return this->GO_ON; } }; @@ -41,7 +46,7 @@ class EmitterAdapter: public internal_mo_transformer { private: int totalWorkers, index; std::unordered_map localWorkersMap; - int nextDestination; + int nextDestination = -1; public: /** Parameters: * - n: rightmost sequential node of the builiding block representing the left-set worker @@ -66,9 +71,18 @@ class EmitterAdapter: public internal_mo_transformer { } bool forward(void* task, int destination){ - if (destination == -1) { - destination = nextDestination; - nextDestination = (nextDestination + 1) % totalWorkers; + + if (destination == -1){ + for(int i = 0; i < localWorkersMap.size(); i++){ + int actualDestination = (nextDestination + 1 + i) % localWorkersMap.size(); + if (ff_send_out_to(task, actualDestination, 1)){ // non blocking ff_send_out_to, we try just once + nextDestination = actualDestination; + return true; + } + } + message_t* msg = new message_t(index, destination); + this->n->serializeF(task, msg->data); + return ff_send_out_to(msg, localWorkersMap.size()); } auto pyshicalDestination = localWorkersMap.find(destination); @@ -122,7 +136,7 @@ class CollectorAdapter: public internal_mi_transformer { } int svc_init() { - if (this->n->isMultiInput()) { ////???????? MASSIMO??? + if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_running(localWorkers.size() + 1); } diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index c131054f..688ac150 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -88,11 +88,9 @@ class dGroup : public ff::ff_farm { dGroup(ff_IR& ir){ int outputChannels = 0; - if (ir.hasSender){ - ff::cout << "Size of routintable " << ir.routingTable.size() << std::endl; + if (ir.hasSender) outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+f.second.first.size();}); - ff::cout << "Outputchannels: " << outputChannels << std::endl; - } + if (ir.isVertical()){ std::vector reverseOutputIndexes(ir.hasLeftChildren() ? ir.outputL.rbegin() : ir.outputR.rbegin(), ir.hasLeftChildren() ? ir.outputL.rend() : ir.outputR.rend()); diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 0e5f9b59..6d759522 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -88,21 +88,12 @@ class ff_dreceiver: public ff_monode_t { } virtual void registerEOS(int sck){ - /*switch(connectionsTypes[sck]){ - case ConnectionType::EXTERNAL: - if (++Eneos == Einput_channels) - for(auto& c : routingTable) if (c.first >= 0) ff_send_out_to(this->EOS, c.second); - break; - case ConnectionType::INTERNAL: - if (++Ineos == Iinput_channels) - for(auto & c : routingTable) if (c.first <= -100) ff_send_out(this->EOS, c.second); - break; - }*/ neos++; } virtual void forward(message_t* task, int){ - ff_send_out_to(task, this->routingTable[task->chid]); // assume the routing table is consistent WARNING!!! + if (task->chid == -1) ff_send_out(task); + else ff_send_out_to(task, this->routingTable[task->chid]); // assume the routing table is consistent WARNING!!! } virtual int handleRequest(int sck){ @@ -131,7 +122,7 @@ class ff_dreceiver: public ff_monode_t { char* buff = new char [sz]; assert(buff); if(readn(sck, buff, sz) < 0){ - error("Error reading from socket\n"); + error("Error reading from socket in handleRequest\n"); delete [] buff; return -1; } @@ -141,6 +132,15 @@ class ff_dreceiver: public ff_monode_t { out->chid = chid; this->forward(out, sck); + + // always sending back the acknowledgement + if (writen(sck, reinterpret_cast(&ACK), sizeof(ack_t)) < 0){ + if (errno != ECONNRESET || errno != EPIPE) { + error("Error sending back ACK to the sender (errno=%d)\n",errno); + return -1; + } + } + return 0; } @@ -286,6 +286,7 @@ class ff_dreceiver: public ff_monode_t { std::map routingTable; int last_receive_fd = -1; int coreid; + ack_t ACK; }; @@ -295,6 +296,7 @@ class ff_dreceiverH : public ff_dreceiver { std::map isInternalConnection; std::set internalGroupsNames; size_t internalNEos = 0, externalNEos = 0; + int next_rr_destination = 0; void registerEOS(int sck){ neos++; @@ -340,7 +342,11 @@ class ff_dreceiverH : public ff_dreceiver { void forward(message_t* task, int sck){ if (isInternalConnection[sck]) ff_send_out_to(task, this->get_num_outchannels()-1); - else ff_dreceiver::forward(task, sck); + else if (task->chid != -1) ff_send_out_to(task, this->routingTable[task->chid]); + else { + ff_send_out_to(task, next_rr_destination); + next_rr_destination = ++next_rr_destination % (this->get_num_outchannels()-1); + } } public: diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 3f8151d0..aa4429ab 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -25,6 +25,9 @@ #ifndef FF_DSENDER_H #define FF_DSENDER_H +#define QUEUEDIM 1 +#define INTERNALQUEUEDIM 1 + #include #include #include @@ -49,14 +52,15 @@ using namespace ff; class ff_dsender: public ff_minode_t { protected: size_t neos=0; - int next_rr_destination = 0; //next destiation to send for round robin policy std::vector dest_endpoints; std::map dest2Socket; - //std::unordered_map> type2sck; std::vector sockets; - //int internalGateways; + int last_rr_socket; + std::map socketsCounters; std::string gName; int coreid; + fd_set set, tmpset; + int fdmax = -1; int receiveReachableDestinations(int sck, std::map& m){ @@ -78,7 +82,7 @@ class ff_dsender: public ff_minode_t { assert(buff); if(readn(sck, buff, sz) < 0){ - error("Error reading from socket\n"); + error("Error reading from socket in routing table!\n"); delete [] buff; return -1; } @@ -216,6 +220,71 @@ class ff_dsender: public ff_minode_t { return 0; } + int waitAckFrom(int sck){ + while (socketsCounters[sck] == 0){ + for(auto& [sck_, counter] : socketsCounters){ + int r; ack_t a; + if ((r = recvnnb(sck_, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ + if (errno == EWOULDBLOCK){ + assert(r == -1); + continue; + } + perror("recvnnb ack"); + return -1; + } else + //printf("received ACK from conn %d\n", i); + counter++; + + } + + if (socketsCounters[sck] == 0){ + tmpset = set; + if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ + perror("select"); + return -1; + } + } + } + return 1; + } + + int waitAckFromAny() { + tmpset = set; + if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ + perror("select"); + return -1; + } + // try to receive from all connections in a non blocking way + for (auto& [sck, counter] : socketsCounters){ + int r; ack_t a; + if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ + if (errno == EWOULDBLOCK){ + //assert(r == -1); // a cosa serve?? + continue; + } + perror("recvnnb ack any"); + return -1; + } else { + counter++; + return sck; + } + } + + return -1; + } + + int getNextReady(){ + for(size_t i = 0; i < this->sockets.size(); i++){ + int actualSocketIndex = (last_rr_socket + 1 + i) % this->sockets.size(); + int sck = sockets[actualSocketIndex]; + if (socketsCounters[sck] > 0) { + last_rr_socket = actualSocketIndex; + return sck; + } + } + return waitAckFromAny(); + } + public: ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int coreid=-1): gName(gName), coreid(coreid) { @@ -229,35 +298,64 @@ class ff_dsender: public ff_minode_t { int svc_init() { if (coreid!=-1) ff_mapThreadToCpu(coreid); + + FD_ZERO(&set); + FD_ZERO(&tmpset); - sockets.resize(this->dest_endpoints.size()); + sockets.resize(dest_endpoints.size()); for(size_t i=0; i < this->dest_endpoints.size(); i++){ - if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; - if (handshakeHandler(sockets[i], false) < 0) return -1; + int sck = tryConnect(this->dest_endpoints[i]); + if (sck <= 0) return -1; + sockets[i] = sck; + socketsCounters[sck] = QUEUEDIM; + if (handshakeHandler(sck, false) < 0) return -1; + + FD_SET(sck, &set); + if (sck > fdmax) fdmax = sck; } + // we can erase the list of endpoints + this->dest_endpoints.clear(); + return 0; } void svc_end() { + + long totalack = socketsCounters.size()*QUEUEDIM; + long currack = 0; + for(const auto& pair : socketsCounters) + currack += pair.second; + while(curracksockets.size(); i++) - close(sockets[i]); + for (auto& sck : this->sockets) close(sck); } message_t *svc(message_t* task) { - /* here i should send the task via socket */ - if (task->chid == -1){ // roundrobin over the destinations - task->chid = next_rr_destination; - next_rr_destination = (next_rr_destination + 1) % dest2Socket.size(); - } + int sck; + if (task->chid != -1){ + sck = dest2Socket[task->chid]; + if (socketsCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand + error("Error waiting Ack from....\n"); + delete task; return this->GO_ON; + } + } else + sck = getNextReady(); // blocking call if scheduling is ondemand + + sendToSck(sck, task); + + // update the counters + socketsCounters[sck]--; - sendToSck(dest2Socket[task->chid], task); delete task; return this->GO_ON; } - void eosnotify(ssize_t id) { + virtual void eosnotify(ssize_t id) { if (++neos >= this->get_num_inchannels()){ message_t E_O_S(0,0); for(const auto& sck : sockets) sendToSck(sck, &E_O_S); @@ -270,10 +368,31 @@ class ff_dsender: public ff_minode_t { class ff_dsenderH : public ff_dsender { std::map internalDest2Socket; - std::map::const_iterator rr_iterator; std::vector internalSockets; + int last_rr_socket_Internal = -1; std::set internalGroupNames; + int getNextReadyInternal(){ + for(size_t i = 0; i < this->internalSockets.size(); i++){ + int actualSocketIndex = (last_rr_socket_Internal + 1 + i) % this->internalSockets.size(); + int sck = internalSockets[actualSocketIndex]; + if (socketsCounters[sck] > 0) { + last_rr_socket_Internal = actualSocketIndex; + return sck; + } + } + + int sck; + decltype(internalSockets)::iterator it; + + do + sck = waitAckFromAny(); + while ((it = std::find(internalSockets.begin(), internalSockets.end(), sck)) != internalSockets.end()); + + last_rr_socket_Internal = it - internalSockets.begin(); + return sck; + } + public: ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int coreid=-1) : ff_dsender(e, gName, coreid), internalGroupNames(internalGroups) {} @@ -287,8 +406,7 @@ class ff_dsenderH : public ff_dsender { int svc_init() { - sockets.resize(this->dest_endpoints.size()); - for(const auto& endpoint : this->dest_endpoints){ + /* for(const auto& endpoint : this->dest_endpoints){ int sck = tryConnect(endpoint); if (sck <= 0) { error("Error on connecting!\n"); @@ -297,22 +415,53 @@ class ff_dsenderH : public ff_dsender { bool isInternal = internalGroupNames.contains(endpoint.groupName); if (isInternal) internalSockets.push_back(sck); else sockets.push_back(sck); + socketsCounters[sck] = isInternal ? INTERNALQUEUEDIM : QUEUEDIM; handshakeHandler(sck, isInternal); } - rr_iterator = internalDest2Socket.cbegin(); + return 0; */ + + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + FD_ZERO(&set); + FD_ZERO(&tmpset); + + for(const auto& endpoint : this->dest_endpoints){ + int sck = tryConnect(endpoint); + if (sck <= 0) return -1; + bool isInternal = internalGroupNames.contains(endpoint.groupName); + if (isInternal) internalSockets.push_back(sck); + else sockets.push_back(sck); + socketsCounters[sck] = isInternal ? INTERNALQUEUEDIM : QUEUEDIM; + if (handshakeHandler(sck, isInternal) < 0) return -1; + + FD_SET(sck, &set); + if (sck > fdmax) fdmax = sck; + } + + // we can erase the list of endpoints + this->dest_endpoints.clear(); + return 0; } message_t *svc(message_t* task) { if (this->get_channel_id() == (ssize_t)(this->get_num_inchannels() - 1)){ + int sck; + // pick destination from the list of internal connections! - if (task->chid == -1){ // roundrobin over the destinations - task->chid = rr_iterator->first; - if (++rr_iterator == internalDest2Socket.cend()) rr_iterator = internalDest2Socket.cbegin(); - } + if (task->chid != -1){ // roundrobin over the destinations + sck = internalDest2Socket[task->chid]; + if (socketsCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand + error("Error waiting Ack from....\n"); + delete task; return this->GO_ON; + } + } else + sck = getNextReadyInternal(); - sendToSck(internalDest2Socket[task->chid], task); + sendToSck(sck, task); + socketsCounters[sck]--; delete task; return this->GO_ON; } @@ -324,8 +473,18 @@ class ff_dsenderH : public ff_dsender { if (id == (ssize_t)(this->get_num_inchannels() - 1)){ // send the EOS to all the internal connections message_t E_O_S(0,0); - for(const auto& sck : internalSockets) sendToSck(sck, &E_O_S); + for(const auto& sck : internalSockets) { + sendToSck(sck, &E_O_S); + FD_CLR(sck, &set); + socketsCounters.erase(sck); + if (sck == fdmax) { + fdmax = 0; + for(auto& [sck_, _] : socketsCounters) if (sck_ > fdmax && FD_ISSET(sck_, &set)) fdmax = sck_; + } + + } ++neos; // count anyway a new EOS received! + } else if (++neos >= this->get_num_inchannels()){ message_t E_O_S(0,0); for(const auto& sck : sockets) sendToSck(sck, &E_O_S); @@ -334,156 +493,4 @@ class ff_dsenderH : public ff_dsender { }; - - - -/* - ONDEMAND specification -*/ - -class ff_dsenderOD: public ff_dsender { -private: - int last_rr_socket = 0; //next destiation to send for round robin policy - std::map sockCounters; - const int queueDim; - fd_set set, tmpset; - int fdmax = -1; - - -public: - ff_dsenderOD(ff_endpoint dest_endpoint, int queueDim = 1, std::string gName = "", int coreid=-1) - : ff_dsender(dest_endpoint, gName, coreid), queueDim(queueDim) {} - - ff_dsenderOD(std::vector dest_endpoints_, int queueDim = 1, std::string gName = "", int coreid=-1) - : ff_dsender(dest_endpoints_, gName, coreid), queueDim(queueDim) {} - - int svc_init() { - if (coreid!=-1) - ff_mapThreadToCpu(coreid); - - FD_ZERO(&set); - FD_ZERO(&tmpset); - - sockets.resize(this->dest_endpoints.size()); - for(size_t i=0; i < this->dest_endpoints.size(); i++){ - if ((sockets[i] = tryConnect(this->dest_endpoints[i])) <= 0 ) return -1; - if (handshakeHandler(sockets[i], false) < 0) return -1; - // execute the following block only if the scheduling is onDemand - - sockCounters[sockets[i]] = queueDim; - FD_SET(sockets[i], &set); - if (sockets[i] > fdmax) - fdmax = sockets[i]; - - } - - return 0; - } - - int waitAckFrom(int sck){ - while (sockCounters[sck] == 0){ - for (size_t i = 0; i < this->sockets.size(); ++i){ - int r; ack_t a; - if ((r = recvnnb(sockets[i], reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ - if (errno == EWOULDBLOCK){ - assert(r == -1); - continue; - } - perror("recvnnb ack"); - return -1; - } else - //printf("received ACK from conn %d\n", i); - sockCounters[sockets[i]]++; - - } - - if (sockCounters[sck] == 0){ - tmpset = set; - if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ - perror("select"); - return -1; - } - } - } - return 1; - } - - int waitAckFromAny() { - tmpset = set; - if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ - perror("select"); - return -1; - } - // try to receive from all connections in a non blocking way - for (size_t i = 0; i < this->sockets.size(); ++i){ - int r; ack_t a; - int sck = sockets[i]; - if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ - if (errno == EWOULDBLOCK){ - assert(r == -1); - continue; - } - perror("recvnnb ack"); - return -1; - } else { - sockCounters[sck]++; - last_rr_socket = i; - return sck; - } - } - assert(1==0); - return -1; - } - - int getNextReady(){ - for(size_t i = 0; i < this->sockets.size(); i++){ - int actualSocket = (last_rr_socket + 1 + i) % this->sockets.size(); - int sck = sockets[actualSocket]; - if (sockCounters[sck] > 0) { - last_rr_socket = actualSocket; - return sck; - } - } - return waitAckFromAny(); - } - - - void svc_end() { - long totalack = sockets.size()*queueDim; - long currack = 0; - for(const auto& pair : sockCounters) - currack += pair.second; - while(curracksockets.size(); i++) { - close(sockets[i]); - } - } - message_t *svc(message_t* task) { - int sck; - if (task->chid != -1){ - sck = dest2Socket[task->chid]; - if (sockCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand - error("Error waiting Ack from....\n"); - delete task; return this->GO_ON; - } - } else - sck = getNextReady(); // blocking call if scheduling is ondemand - - sendToSck(sck, task); - - // update the counters - sockCounters[sck]--; - - delete task; - return this->GO_ON; - } - - -}; - #endif From 8eec48280456d47044f0443fe799329562f8e388 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 18 Mar 2022 19:10:29 +0100 Subject: [PATCH 129/202] New pipline handling code --- ff/distributed/ff_dadapters.hpp | 22 +++++- ff/distributed/ff_dgroups.hpp | 57 +++++++++++++- ff/pipeline.hpp | 37 ++++++++- tests/distributed/test_group19.cpp | 116 +++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 tests/distributed/test_group19.cpp diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index 1a622330..a5d855ef 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -17,6 +17,19 @@ using namespace ff; class SquareBoxRight : public ff_minode { ssize_t neos = 0; public: + + int svc_init() { + + // change the size of the queue to the Sender, since the distributed-memory communications are all on-demand + svector w; + this->get_out_nodes(w); + size_t oldsz; + assert(w.size() == 1); + w[0]->change_inputqueuesize(1, oldsz); + + return 0; + } + void* svc(void* in) {return in;} void eosnotify(ssize_t id) { @@ -60,7 +73,7 @@ class EmitterAdapter: public internal_mo_transformer { this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } - + void * svc(void* in) { void* out = n->svc(in); if (out > FF_TAG_MIN) return out; @@ -101,6 +114,13 @@ class EmitterAdapter: public internal_mo_transformer { //mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers mo->set_running(totalWorkers); } + + // change the size of the queue to the SquareBoxRight, since the distributed-memory communications are all on-demand + svector w; + this->get_out_nodes(w); + size_t oldsz; + w[localWorkersMap.size()]->change_inputqueuesize(1, oldsz); + return n->svc_init(); } diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index dae4b603..48f14148 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -249,8 +249,60 @@ class dGroups { } // if i'm here it means that from this 1st level building block, multiple groups have been created! (An Error or an A2A or a Farm BB) - std::set> children = getChildBB(pair.first); + + if (pair.first->isPipe()){ + + // check that all stages were annotated + for(ff_node* child : reinterpret_cast(pair.first)->getStages()) + if (!annotated.contains(child)){ + error("Need to abla "); + abort(); + } + + for(auto& gName: pair.second){ + ff_pipeline* originalPipe = reinterpret_cast(pair.first); + ff_pipeline* mypipe = new ff_pipeline; + + for(ff_node* child : originalPipe->getStages()){ + if (annotated[child] == gName){ + if (mypipe->getStages().size() != 0 && originalPipe->get_stageindex(mypipe->get_laststage())+1 != originalPipe->get_stageindex(child)) { + error("There are some stages missing in the annottation!\n"); + abort(); + } else + mypipe->add_stage(child); + } + } + + bool head = mypipe->get_firststage() == originalPipe->get_firststage(); + bool tail = mypipe->get_laststage() == originalPipe->get_laststage(); + + if (((head && isSrc) || mypipe->isDeserializable()) && ((tail && isSnk) || mypipe->isSerializable())) + annotatedGroups[gName].insertInList(std::make_pair(mypipe, SetEnum::L)); + else { + error("The group cannot serialize something!\n"); + abort(); + } + + if (head && isSrc) annotatedGroups[gName].isSource = true; + if (tail && isSnk) annotatedGroups[gName].isSink = true; + + + if (!tail) + annotatedGroups[gName].destinationEndpoints.push_back(annotatedGroups[annotated[originalPipe->get_nextstage(mypipe->get_laststage())]].listenEndpoint); + + if (!head) + annotatedGroups[gName].expectedEOS = 1; + + } + + + + } else { + // all2all here! + + std::set> children = getChildBB(pair.first); + std::erase_if(children, [&](auto& p){ if (!annotated.contains(p.first)) return false; std::string& groupName = annotated[p.first]; @@ -300,7 +352,8 @@ class dGroups { // populate the set with the names of other groups created from this 1st level BB _ir.otherGroupsFromSameParentBB = pair.second; } - + + } //runningGroup_IR.isSink = isSnk; runningGroup_IR.isSource = isSrc; //runningGroup_IR.otherGroupsFromSameParentBB = pair.second; diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 5f6af327..2a84281c 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -661,6 +661,13 @@ class ff_pipeline: public ff_node { if (cleanup) internalSupportNodes.push_back(node); } + int get_stageindex(const ff_node* stage){ + if (!stage) return -1; + for(size_t i=0; i(nodes_list.size())-1; + return nodes_list[last]; + } + + /** + * \brief returns the first stage of the pipeline. + */ + ff_node* get_firststage() const { + if (!nodes_list.size()) return nullptr; + return nodes_list[0]; + } + + ff_node* get_nextstage(const ff_node* s){ + int index = this->get_stageindex(s); + if (index == -1 || index == nodes_list.size()-1) return nullptr; + return nodes_list[index+1]; + } + + ff_node* get_prevstage(const ff_node* s){ + int index = this->get_stageindex(s); + if (index <= 0) return nullptr; + return nodes_list[index-1]; + } inline void get_out_nodes(svector&w) { assert(nodes_list.size()>0); diff --git a/tests/distributed/test_group19.cpp b/tests/distributed/test_group19.cpp new file mode 100644 index 00000000..740d29b1 --- /dev/null +++ b/tests/distributed/test_group19.cpp @@ -0,0 +1,116 @@ +/* + * + * Node1-->Node2 --> Node3 --> Node4 + * + * /<-- pipe0-->/ /<---pipe1--->/ + * /<----------- pipe ------------>/ + * + * G1: pipe0 + * G2: pipe1 + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + //std::cout << "Node2: " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + + return t; + } +}; + +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += 1; + t->S.f += 1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + abort(); + } + ff::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + ff_pipeline pipeMain; + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2; + Node3 n3; + Node4 n4(ntasks); + + + + pipeMain.add_stage(&pipe); + + //----- defining the distributed groups ------ + + pipe.createGroup("G1") << &n1 << &n2; + pipe.createGroup("G2") << &n3 << &n4; + + // ------------------------------------------- + + if (pipeMain.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} From 49f5a99340e751ce03e461e470dff9fd62645f4e Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 18 Mar 2022 20:02:21 +0100 Subject: [PATCH 130/202] minor fixes --- ff/distributed/ff_dgroups.hpp | 9 +++++++++ tests/distributed/test_group19.cpp | 10 +++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 48f14148..11168e18 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -129,6 +129,15 @@ class dGroups { return -1; } + bool allDeriveFromParent = true; + for(auto& [name, ir]: annotatedGroups) + if (ir.parentBB != parent) { allDeriveFromParent = false; break; } + + if (allDeriveFromParent) { + ff_pipeline *mypipe = new ff_pipeline; + mypipe->add_stage(parent); + parent = mypipe; + } // qui dovrei creare la rappresentazione intermedia di tutti this->prepareIR(parent); diff --git a/tests/distributed/test_group19.cpp b/tests/distributed/test_group19.cpp index 740d29b1..fd38b6d8 100644 --- a/tests/distributed/test_group19.cpp +++ b/tests/distributed/test_group19.cpp @@ -90,17 +90,17 @@ int main(int argc, char*argv[]){ if (argc>1) { ntasks = std::stol(argv[1]); } - ff_pipeline pipeMain; ff_pipeline pipe; Node1 n1(ntasks); Node2 n2; Node3 n3; Node4 n4(ntasks); - + pipe.add_stage(&n1); + pipe.add_stage(&n2); + pipe.add_stage(&n3); + pipe.add_stage(&n4); - pipeMain.add_stage(&pipe); - //----- defining the distributed groups ------ pipe.createGroup("G1") << &n1 << &n2; @@ -108,7 +108,7 @@ int main(int argc, char*argv[]){ // ------------------------------------------- - if (pipeMain.run_and_wait_end()<0) { + if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); return -1; } From 858f898ebddbce1e4243d09e984d854dd5337b87 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 21 Mar 2022 17:40:52 +0100 Subject: [PATCH 131/202] removed some warnings, fixed some issues and added more tests --- ff/distributed/ff_dadapters.hpp | 18 ++- ff/distributed/ff_dgroup.hpp | 7 + ff/distributed/ff_dreceiver.hpp | 4 +- ff/distributed/ff_dsender.hpp | 6 +- ff/pipeline.hpp | 17 +-- tests/distributed/dwordcount/dwordcount.cpp | 39 +++--- tests/distributed/dwordcount/dwordcountb.cpp | 39 +++--- tests/distributed/test_a2aOnDemand.cpp | 46 +++--- tests/distributed/test_group19.cpp | 20 ++- tests/distributed/test_group19.json | 12 ++ tests/distributed/test_group20.cpp | 140 +++++++++++++++++++ tests/distributed/test_group20.json | 12 ++ tests/distributed/test_group9.cpp | 3 + 13 files changed, 278 insertions(+), 85 deletions(-) create mode 100644 tests/distributed/test_group19.json create mode 100644 tests/distributed/test_group20.cpp create mode 100644 tests/distributed/test_group20.json diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index a5d855ef..de1e2885 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -41,7 +41,7 @@ class SquareBoxRight : public ff_minode { class SquareBoxLeft : public ff_monode { std::unordered_map destinations; - int next_rr_destination = 0; + long next_rr_destination = 0; public: SquareBoxLeft(const std::unordered_map localDestinations) : destinations(localDestinations) {} @@ -86,8 +86,8 @@ class EmitterAdapter: public internal_mo_transformer { bool forward(void* task, int destination){ if (destination == -1){ - for(int i = 0; i < localWorkersMap.size(); i++){ - int actualDestination = (nextDestination + 1 + i) % localWorkersMap.size(); + for(size_t i = 0; i < localWorkersMap.size(); i++){ + long actualDestination = (nextDestination + 1 + i) % localWorkersMap.size(); if (ff_send_out_to(task, actualDestination, 1)){ // non blocking ff_send_out_to, we try just once nextDestination = actualDestination; return true; @@ -115,12 +115,16 @@ class EmitterAdapter: public internal_mo_transformer { mo->set_running(totalWorkers); } - // change the size of the queue to the SquareBoxRight, since the distributed-memory communications are all on-demand + // change the size of the queue to the SquareBoxRight (if present), + // since the distributed-memory communications are all on-demand svector w; this->get_out_nodes(w); - size_t oldsz; - w[localWorkersMap.size()]->change_inputqueuesize(1, oldsz); - + assert(w.size()>0); + if (w.size() > localWorkersMap.size()) { + assert(w.size() == localWorkersMap.size()+1); + size_t oldsz; + w[localWorkersMap.size()]->change_inputqueuesize(1, oldsz); + } return n->svc_init(); } diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 688ac150..9211ad43 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -133,6 +133,13 @@ class dGroup : public ff::ff_farm { if (ir.hasSender) this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName), true); + + if (ir.hasRightChildren() && ir.parentBB->isAll2All()) { + ff_a2a *a2a = reinterpret_cast(ir.parentBB); + if (a2a->ondemand_buffer() > 0) { + this->set_scheduling_ondemand(a2a->ondemand_buffer()); + } + } } else { // the group is horizontal! ff_a2a* innerA2A = new ff_a2a(); diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 6d759522..debdc5eb 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -296,7 +296,7 @@ class ff_dreceiverH : public ff_dreceiver { std::map isInternalConnection; std::set internalGroupsNames; size_t internalNEos = 0, externalNEos = 0; - int next_rr_destination = 0; + long next_rr_destination = 0; void registerEOS(int sck){ neos++; @@ -345,7 +345,7 @@ class ff_dreceiverH : public ff_dreceiver { else if (task->chid != -1) ff_send_out_to(task, this->routingTable[task->chid]); else { ff_send_out_to(task, next_rr_destination); - next_rr_destination = ++next_rr_destination % (this->get_num_outchannels()-1); + next_rr_destination = (next_rr_destination + 1) % (this->get_num_outchannels()-1); } } diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index aa4429ab..05ab807c 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -259,7 +259,6 @@ class ff_dsender: public ff_minode_t { int r; ack_t a; if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ if (errno == EWOULDBLOCK){ - //assert(r == -1); // a cosa serve?? continue; } perror("recvnnb ack any"); @@ -340,8 +339,9 @@ class ff_dsender: public ff_minode_t { if (task->chid != -1){ sck = dest2Socket[task->chid]; if (socketsCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand - error("Error waiting Ack from....\n"); - delete task; return this->GO_ON; + error("Error waiting Ack from....\n"); + delete task; + return this->GO_ON; } } else sck = getNextReady(); // blocking call if scheduling is ondemand diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 2a84281c..f838ec99 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -135,7 +135,7 @@ class ff_pipeline: public ff_node { nodes_list[i-1]->get_out_nodes(w1); if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } if (isa2a_prev) { - const svector& w1=isa2a_getsecondset(nodes_list[i-1]); + const svector& w1=isa2a_getsecondset(get_node_last(i-1)); assert(w1.size()>0); if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } @@ -147,7 +147,7 @@ class ff_pipeline: public ff_node { nodes_list[i-1]->get_out_nodes(w1); if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } if (isa2a_prev) { - const svector& w1=isa2a_getsecondset(nodes_list[i-1]); + const svector& w1=isa2a_getsecondset(get_node_last(i-1)); assert(w1.size()>0); if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } @@ -661,10 +661,10 @@ class ff_pipeline: public ff_node { if (cleanup) internalSupportNodes.push_back(node); } - int get_stageindex(const ff_node* stage){ + ssize_t get_stageindex(const ff_node* stage){ if (!stage) return -1; - for(size_t i=0; iget_stageindex(s); - if (index == -1 || index == nodes_list.size()-1) return nullptr; + ssize_t index = get_stageindex(s); + if (index == -1 || (index+1) == (ssize_t)nodes_list.size()) + return nullptr; return nodes_list[index+1]; } ff_node* get_prevstage(const ff_node* s){ - int index = this->get_stageindex(s); + ssize_t index = get_stageindex(s); if (index <= 0) return nullptr; return nodes_list[index-1]; } diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index d17f8113..bf1732e1 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -67,20 +67,20 @@ using namespace ff; const size_t qlen = DEFAULT_BUFFER_CAPACITY; -const int MAXLINE=128; +const int MAXLINE=128; // character per line (CPL), a typically value is 80 CPL const int MAXWORD=32; struct tuple_t { - char text_line[MAXLINE]; // line of the parsed dataset (text, book, ...) - size_t key; // line number - uint64_t id; // id set to zero - uint64_t ts; // timestamp + char text_line[MAXLINE]; // parsed line + size_t key; // line number + uint64_t id; // id set to zero + uint64_t ts; // timestamp }; struct result_t { - char key[MAXWORD]; // key word - uint64_t id; // id that indicates the current number of occurrences of the key word - uint64_t ts; // timestamp + char key[MAXWORD]; // key word + uint64_t id; // indicates the current number of occurrences of the word + uint64_t ts; // timestamp template void serialize(Archive & archive) { @@ -158,12 +158,10 @@ struct Source: ff_monode_t { } }; struct Splitter: ff_monode_t { - Splitter(long noutch):noutch(noutch) {} - - // int svc_init() { - // noutch=get_num_outchannels(); // TODO: this doesn't work, it must be fixed! - // /return 0; - // } + int svc_init() { + noutch=get_num_outchannels(); // number of output channels + return 0; + } result_t* svc(tuple_t* in) { char *tmpstr; @@ -197,7 +195,7 @@ struct Splitter: ff_monode_t { struct Counter: ff_minode_t { result_t* svc(result_t* in) { ++M[std::string(in->key)]; - // number of occurrences of the string word up to now + // number of occurrences of the word up to now in->id = M[std::string(in->key)]; return in; } @@ -212,7 +210,7 @@ struct Counter: ff_minode_t { }; struct Sink: ff_node_t { - result_t* svc(result_t* in) { + result_t* svc(result_t* in) { ++words; delete in; return GO_ON; @@ -220,7 +218,6 @@ struct Sink: ff_node_t { size_t words=0; // total number of words received }; - /** * @brief Parse the input file and create all the tuples * @@ -260,7 +257,6 @@ int parse_dataset_and_create_tuples(const std::string& file_path) { } - int main(int argc, char* argv[]) { if (DFF_Init(argc, argv) != 0) { error("DFF_Init\n"); @@ -325,7 +321,8 @@ int main(int argc, char* argv[]) { /// data pre-processing if (parse_dataset_and_create_tuples(file_path)< 0) return -1; - + + std::cout << "\n\n"; std::cout << "Executing WordCount with parameters:" << endl; std::cout << " * source/splitter : " << source_par_deg << endl; std::cout << " * counter/sink : " << sink_par_deg << endl; @@ -350,7 +347,7 @@ int main(int argc, char* argv[]) { ff_pipeline* pipe0 = new ff_pipeline(false, qlen, qlen, true); pipe0->add_stage(new Source(app_start_time), true); - Splitter* sp = new Splitter(sink_par_deg); + Splitter* sp = new Splitter; pipe0->add_stage(sp, true); L.push_back(pipe0); @@ -379,7 +376,7 @@ int main(int argc, char* argv[]) { threadMapper::instance()->setMappingList("12,13,14,15,16,17,18,19,20,21,22,23, 36,37,38,39,40,41,42,43,44,45,46,47"); } #endif - std::cout << "Starting " << pipeMain.numThreads() << " threads\n"; + std::cout << "Starting " << pipeMain.numThreads() << " threads\n\n"; /// evaluate topology execution time volatile unsigned long start_time_main_usecs = current_time_usecs(); if (pipeMain.run_and_wait_end()<0) { diff --git a/tests/distributed/dwordcount/dwordcountb.cpp b/tests/distributed/dwordcount/dwordcountb.cpp index e59c0900..023944e7 100644 --- a/tests/distributed/dwordcount/dwordcountb.cpp +++ b/tests/distributed/dwordcount/dwordcountb.cpp @@ -66,20 +66,20 @@ using namespace ff; const size_t qlen = DEFAULT_BUFFER_CAPACITY; -const int MAXLINE=128; +const int MAXLINE=128; // character per line (CPL), a typically value is 80 CPL const int MAXWORD=32; struct tuple_t { - char text_line[MAXLINE]; // line of the parsed dataset (text, book, ...) - size_t key; // line number - uint64_t id; // id set to zero - uint64_t ts; // timestamp + char text_line[MAXLINE]; // parsed line + size_t key; // line number + uint64_t id; // id set to zero + uint64_t ts; // timestamp }; struct result_t { - char key[MAXWORD]; // key word - uint64_t id; // id that indicates the current number of occurrences of the key word - uint64_t ts; // timestamp + char key[MAXWORD]; // key word + uint64_t id; // indicates the current number of occurrences of the word + uint64_t ts; // timestamp template void serialize(Archive & archive) { @@ -153,12 +153,13 @@ struct Source: ff_monode_t { } }; struct Splitter: ff_monode_t { - Splitter(long noutch, long buffered_lines):noutch(noutch),buffered_lines(buffered_lines), outV(noutch,nullptr) { } + Splitter(long buffered_lines):buffered_lines(buffered_lines) { } - // int svc_init() { - // noutch=get_num_outchannels(); // TODO: this doesn't work, it must be fixed! - // /return 0; - // } + int svc_init() { + noutch=get_num_outchannels(); // number of output channels + outV.resize(noutch,nullptr); + return 0; + } Result_t* svc(tuple_t* in) { char *tmpstr; @@ -351,10 +352,12 @@ int main(int argc, char* argv[]) { /// data pre-processing if (parse_dataset_and_create_tuples(file_path)< 0) return -1; - + + std::cout << "\n\n"; std::cout << "Executing WordCount with parameters:" << endl; std::cout << " * source/splitter : " << source_par_deg << endl; std::cout << " * counter/sink : " << sink_par_deg << endl; + std::cout << " * buffered lines : " << buffered_lines << endl; std::cout << " * running time : " << app_run_time << " (s)\n"; } @@ -375,11 +378,11 @@ int main(int argc, char* argv[]) { ff_pipeline* pipe0 = new ff_pipeline(false, qlen, qlen, true); pipe0->add_stage(new Source(app_start_time)); - Splitter* sp = new Splitter(sink_par_deg, buffered_lines); + Splitter* sp = new Splitter(buffered_lines); pipe0->add_stage(sp); L.push_back(pipe0); - G1.out << sp; + G1 << pipe0; } for (size_t i=0;iadd_stage(S[i]); R.push_back(pipe1); - G2.in << C[i]; + G2 << pipe1; } a2a.add_firstset(L, 0, true); @@ -404,7 +407,7 @@ int main(int argc, char* argv[]) { } #endif - std::cout << "Starting " << pipeMain.numThreads() << " threads\n"; + std::cout << "Starting " << pipeMain.numThreads() << " threads\n\n"; /// evaluate topology execution time volatile unsigned long start_time_main_usecs = current_time_usecs(); if (pipeMain.run_and_wait_end()<0) { diff --git a/tests/distributed/test_a2aOnDemand.cpp b/tests/distributed/test_a2aOnDemand.cpp index 9aed3886..5f15cf9d 100644 --- a/tests/distributed/test_a2aOnDemand.cpp +++ b/tests/distributed/test_a2aOnDemand.cpp @@ -11,10 +11,14 @@ * /<----------- pipeMain ------------->/ * * distributed version: - * - * G1: MoNode - * G2: a2a - * + * G2 + * G1 ----------- + * G0 -------------- | -> MiNode | + * -------- | -> MoNode -> | | | + * | MoNode |-->| | -->| -> MiNode | + * -------- | -> MoNode -> | | | + * -------------- | -> MiNode | + * ----------- */ @@ -66,14 +70,22 @@ struct MoNode : ff::ff_monode_t{ } return this->EOS; } + ++cnt; return in; } void svc_end() { - const std::lock_guard lock(mtx); - std::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << std::endl; + if (items) { + const std::lock_guard lock(mtx); + ff::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << std::endl; + } + if (cnt) { + const std::lock_guard lock(mtx); + ff::cout << "[MoNode" << this->get_my_id() << "] Received Items: " << cnt << std::endl; + } + } - long items; + long items, cnt=0; }; struct MiNode : ff::ff_minode_t{ @@ -108,13 +120,9 @@ int main(int argc, char*argv[]){ int numWorkerDx = atoi(argv[3]); ff_pipeline mainPipe; - ff_pipeline pipe; - ff::ff_a2a a2a; - MoNode generator(items); - pipe.add_stage(&generator); - - mainPipe.add_stage(&pipe); + mainPipe.add_stage(&generator); + ff_a2a a2a; mainPipe.add_stage(&a2a); std::vector sxWorkers; @@ -124,21 +132,21 @@ int main(int argc, char*argv[]){ sxWorkers.push_back(new MoNode(0)); for(int i = 0; i < numWorkerDx; i++) - dxWorkers.push_back(new MiNode(i*100)); + dxWorkers.push_back(new MiNode((i+1)*10)); a2a.add_firstset(sxWorkers, 1); // enabling on-demand distribution policy a2a.add_secondset(dxWorkers); //----- defining the distributed groups ------ - auto g0 = generator.createGroup("G0"); - auto g1 = a2a.createGroup("G1"); - auto g2 = a2a.createGroup("G2"); + auto G0 = generator.createGroup("G0"); + auto G1 = a2a.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); for(int i = 0; i < numWorkerSx; i++) - g1 << sxWorkers[i]; + G1 << sxWorkers[i]; for(int i = 0; i < numWorkerDx; i++) - g2 << dxWorkers[i]; + G2 << dxWorkers[i]; // ------------------------------------------- diff --git a/tests/distributed/test_group19.cpp b/tests/distributed/test_group19.cpp index fd38b6d8..6f784f5f 100644 --- a/tests/distributed/test_group19.cpp +++ b/tests/distributed/test_group19.cpp @@ -1,12 +1,18 @@ /* - * - * Node1-->Node2 --> Node3 --> Node4 + * FastFlow concurrent network: + * + * Node1 --> Node2 --> Node3 --> Node4 * - * /<-- pipe0-->/ /<---pipe1--->/ - * /<----------- pipe ------------>/ + * /<------------- pipe ------------>/ + * + * distributed version: + * + * G1 G2 + * ----------------- ----------------- + * | Node1 --> Node2 | ---> | Node3 --> Node4 | + * | | | | + * ----------------- ----------------- * - * G1: pipe0 - * G2: pipe1 */ @@ -29,7 +35,6 @@ struct myTask_t { }; - struct Node1: ff_monode_t{ Node1(long ntasks):ntasks(ntasks) {} myTask_t* svc(myTask_t*){ @@ -90,6 +95,7 @@ int main(int argc, char*argv[]){ if (argc>1) { ntasks = std::stol(argv[1]); } + ff_pipeline pipe; Node1 n1(ntasks); Node2 n2; diff --git a/tests/distributed/test_group19.json b/tests/distributed/test_group19.json new file mode 100644 index 00000000..79e1b1af --- /dev/null +++ b/tests/distributed/test_group19.json @@ -0,0 +1,12 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1" + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} diff --git a/tests/distributed/test_group20.cpp b/tests/distributed/test_group20.cpp new file mode 100644 index 00000000..97971ec5 --- /dev/null +++ b/tests/distributed/test_group20.cpp @@ -0,0 +1,140 @@ +/* + * FastFlow concurrent network: + * + * | -> Node3 -->| + * Node1-->Node2 -->| -> Node3 -->| -> Node4 + * | -> Node3 -->| + * + * /<----------- a2a ------>/ + * /<----------------- pipe ---------------->/ + * + * distributed version: + * + * G2 + * G1 ----------------------- + * ----------------- | -> Node3 -->| | + * | Node1 --> Node2 | | | | + * | | ---> | -> Node3 -->|-> Node4 | + * ----------------- | | | + * | -> Node3 -->| | + * ----------------------- + * + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_node_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + return t; + } +}; + +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } + void eosnotify(ssize_t) { + + ff::cout << "Node3 " << get_my_id() << " EOS RECEIVED\n"; + } + +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + //std::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void eosnotify(ssize_t id) { + ff::cout << "Node4 EOS RECEIVED from " << id << ff::endl; + } + + void svc_end() { + if (processed != ntasks) { + abort(); + } + ff::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2; + Node3 n31, n32, n33; + Node4 n4(ntasks); + ff_a2a a2a; + a2a.add_firstset({&n31, &n32, &n33}); + a2a.add_secondset({&n4}); + pipe.add_stage(&n1); + pipe.add_stage(&n2); + pipe.add_stage(&a2a); + + //----- defining the distributed groups ------ + + auto G1 = pipe.createGroup("G1"); + auto G2 = pipe.createGroup("G2"); + + G1 << &n1 << &n2; + G2 << &a2a; + + // ------------------------------------------- + + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group20.json b/tests/distributed/test_group20.json new file mode 100644 index 00000000..79e1b1af --- /dev/null +++ b/tests/distributed/test_group20.json @@ -0,0 +1,12 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1" + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} diff --git a/tests/distributed/test_group9.cpp b/tests/distributed/test_group9.cpp index f4873289..b77d2c39 100644 --- a/tests/distributed/test_group9.cpp +++ b/tests/distributed/test_group9.cpp @@ -7,6 +7,9 @@ * * G1: pipe0 * G2: pipe1 + * + * See also test_group19, which contains a simplified version. + * */ From ba79b84c45519dc832f6250563965517037f97a7 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Mon, 21 Mar 2022 17:45:15 +0100 Subject: [PATCH 132/202] Fixed minor related to get_num_outchannels for horizontal groups --- ff/distributed/ff_dgroup.hpp | 10 +++++----- ff/distributed/ff_dintermediate.hpp | 13 ++++++++++++- ff/distributed/ff_dsender.hpp | 5 ++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 688ac150..8fe0b8c0 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -86,12 +86,11 @@ class dGroup : public ff::ff_farm { public: dGroup(ff_IR& ir){ - - int outputChannels = 0; - if (ir.hasSender) - outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+f.second.first.size();}); if (ir.isVertical()){ + int outputChannels = 0; + if (ir.hasSender) + outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+f.second.first.size();}); std::vector reverseOutputIndexes(ir.hasLeftChildren() ? ir.outputL.rbegin() : ir.outputR.rbegin(), ir.hasLeftChildren() ? ir.outputL.rend() : ir.outputR.rend()); for(ff_node* child: (ir.hasLeftChildren() ? ir.L : ir.R)){ @@ -182,7 +181,8 @@ class dGroup : public ff::ff_farm { innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? - + int outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? 0 : f.second.first.size());}); + std::vector reverseRightOutputIndexes(ir.outputR.rbegin(), ir.outputR.rend()); std::vector secondSet; for(ff_node* child : ir.R){ diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index b424c02d..9cfd5ce6 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace ff { @@ -122,7 +123,17 @@ class ff_IR { std::cout << "Destination endpoints: " << std::endl; for(ff_endpoint& e : destinationEndpoints) std::cout << "\t* " << e.groupName << "\t[[" << e.address << ":" << e.port << "]]" << std::endl; - + + std::cout << "Precomputed routing table: \n"; + for(auto& [gName, p] : routingTable){ + std::cout << "\t* " << gName << (p.second ? "(Internal) :" : "(External) :"); + for(auto i : p.first) std::cout << i << " "; + std::cout << std::endl; + } + + std::cout << "\nPrecomputed external destinations: " << std::accumulate(routingTable.begin(), routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? 0 : f.second.first.size());}) << std::endl; + std::cout << "Precomputed internal destinations: " << std::accumulate(routingTable.begin(), routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? f.second.first.size() : 0);}) << std::endl; + std::cout << "\n\nIndex Input Left: "; for(int i : inputL) std::cout << i << " "; std::cout << "\n"; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index aa4429ab..7c0d232d 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -232,7 +232,6 @@ class ff_dsender: public ff_minode_t { perror("recvnnb ack"); return -1; } else - //printf("received ACK from conn %d\n", i); counter++; } @@ -460,12 +459,12 @@ class ff_dsenderH : public ff_dsender { } else sck = getNextReadyInternal(); - sendToSck(sck, task); + sendToSck(sck, task); socketsCounters[sck]--; delete task; return this->GO_ON; } - + return ff_dsender::svc(task); } From e34c688a8dfc6b96253cfdc7c5e5d9b86e2a87a8 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Tue, 22 Mar 2022 16:54:00 +0100 Subject: [PATCH 133/202] First not working batch system --- ff/distributed/ff_batchbuffer.hpp | 77 +++++++++++++++++++ ff/distributed/ff_dreceiver.hpp | 38 +++++++--- ff/distributed/ff_dsender.hpp | 122 +++++++++++++++++++----------- 3 files changed, 182 insertions(+), 55 deletions(-) create mode 100644 ff/distributed/ff_batchbuffer.hpp diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp new file mode 100644 index 00000000..6d782bba --- /dev/null +++ b/ff/distributed/ff_batchbuffer.hpp @@ -0,0 +1,77 @@ +#include "ff_network.hpp" +#include +using namespace ff; + +class ff_batchBuffer { + std::function callback; + int batchSize; + struct iovec iov[1024]; +public: + int size = 0; + ff_batchBuffer() {} + ff_batchBuffer(int _size, std::function cbk) : callback(cbk), batchSize(_size) { + if (_size*2+1 > 1024){ + error("Size too big!\n"); + abort(); + } + iov[0].iov_base = &(this->size); + iov[0].iov_len = sizeof(int); + } + + void push(message_t* m){ + + ff::cout << "Pushing something of size: " << m->data.getLen(); + + int sender_ = htonl(m->sender); + int chid_ = htonl(m->chid); + size_t size_ = htobe64(m->data.getLen()); + + char* buffer = new char[sizeof(int)+sizeof(int)+sizeof(size_t)+1]; + memcpy(buffer, &sender_, sizeof(int)); + memcpy(buffer+sizeof(int), &chid_, sizeof(int)); + memcpy(buffer+sizeof(int)+sizeof(int), &size_, sizeof(size_t)); + + /**reinterpret_cast(buffer) = htonl(m->sender); + *reinterpret_cast(buffer+sizeof(int)) = htonl(m->chid); + *reinterpret_cast(buffer+sizeof(int)+sizeof(int)) = htobe64(m->data.getLen()); + */ + + int indexBase = size * 2; + iov[indexBase+1].iov_base = buffer; + iov[indexBase+1].iov_len = sizeof(int)+sizeof(int)+sizeof(size_t); + iov[indexBase+2].iov_base = m->data.getPtr(); + iov[indexBase+2].iov_len = m->data.getLen(); + + m->data.doNotCleanup(); + delete m; + + if (++size == batchSize) + this->flush(); + + } + + void sendEOS(){ + push(new message_t(0,0)); + flush(true); + } + + void flush(bool last = false){ + if (size == 0) return; + + ff::cout << "Sending out a buffer os size: " << size << std::endl; + if (!callback(iov, size*2+1-last)) + error("Callback of the batchbuffer got something wrong!\n"); + + for(size_t i = 0; i < size; i++){ + + iov[i*2+1].iov_len = 0; + delete [] (char*)iov[i*2+1].iov_base; + if (last && i == (size-1)) break; + iov[i*2+2].iov_len = 0; + delete [] (char*)iov[i*2+2].iov_base; + } + + size = 0; + } + +}; \ No newline at end of file diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index debdc5eb..7b5501af 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -96,6 +96,32 @@ class ff_dreceiver: public ff_monode_t { else ff_send_out_to(task, this->routingTable[task->chid]); // assume the routing table is consistent WARNING!!! } + virtual int handleBatch(int sck){ + int requestSize; + + if (readn(sck, reinterpret_cast(&requestSize), sizeof(requestSize)) != sizeof(requestSize)){ + perror("BatchReadn"); + error("Error receiving the number of tasks in the batch\n"); + return -1; + } + + ff::cout << "Receive a btach of " << requestSize << " items\n"; + + for(int i = 0; i < requestSize; i++) + handleRequest(sck); + + + // always sending back the acknowledgement + if (writen(sck, reinterpret_cast(&ACK), sizeof(ack_t)) < 0){ + if (errno != ECONNRESET || errno != EPIPE) { + error("Error sending back ACK to the sender (errno=%d)\n",errno); + return -1; + } + } + + return 0; + } + virtual int handleRequest(int sck){ int sender; int chid; @@ -130,16 +156,8 @@ class ff_dreceiver: public ff_monode_t { assert(out); out->sender = sender; out->chid = chid; - + ff::cout << "Forwarding something of size: " << out->data.getLen() << std::endl; this->forward(out, sck); - - // always sending back the acknowledgement - if (writen(sck, reinterpret_cast(&ACK), sizeof(ack_t)) < 0){ - if (errno != ECONNRESET || errno != EPIPE) { - error("Error sending back ACK to the sender (errno=%d)\n",errno); - return -1; - } - } return 0; } @@ -256,7 +274,7 @@ class ff_dreceiver: public ff_monode_t { this->last_receive_fd = actualFD; - if (this->handleRequest(actualFD) < 0){ + if (this->handleBatch(actualFD) < 0){ close(actualFD); FD_CLR(actualFD, &set); diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 4130fd12..45b6e50a 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ class ff_dsender: public ff_minode_t { std::vector sockets; int last_rr_socket; std::map socketsCounters; + std::map batchBuffers; std::string gName; int coreid; fd_set set, tmpset; @@ -283,6 +285,19 @@ class ff_dsender: public ff_minode_t { return waitAckFromAny(); } + int getMostFilledBufferSck(){ + int sckMax = 0; + int sizeMax = 0; + for(auto& [sck, buffer] : batchBuffers){ + if (buffer.size > sizeMax) sckMax = sck; + } + if (sckMax > 0) return sckMax; + + last_rr_socket = (last_rr_socket + 1) % this->sockets.size(); + return sockets[last_rr_socket]; + + } + public: ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int coreid=-1): gName(gName), coreid(coreid) { @@ -306,6 +321,23 @@ class ff_dsender: public ff_minode_t { if (sck <= 0) return -1; sockets[i] = sck; socketsCounters[sck] = QUEUEDIM; + batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(1, [this, sck](struct iovec* v, int size) -> bool { + + if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ + error("Errore waiting ack from socket inside the callback\n"); + return false; + } + + if (writevn(sck, v, size) < 0){ + error("Error sending the iovector inside the callback!\n"); + return false; + } + + this->socketsCounters[sck]--; + + return true; + })); // change with the correct size + if (handshakeHandler(sck, false) < 0) return -1; FD_SET(sck, &set); @@ -335,30 +367,19 @@ class ff_dsender: public ff_minode_t { message_t *svc(message_t* task) { int sck; - if (task->chid != -1){ + if (task->chid != -1) sck = dest2Socket[task->chid]; - if (socketsCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand - error("Error waiting Ack from....\n"); - delete task; - return this->GO_ON; - } - } else - sck = getNextReady(); // blocking call if scheduling is ondemand + else + sck = getMostFilledBufferSck(); // get the most filled buffer socket or a rr socket - sendToSck(sck, task); + batchBuffers[sck].push(task); - // update the counters - socketsCounters[sck]--; - - delete task; return this->GO_ON; } virtual void eosnotify(ssize_t id) { - if (++neos >= this->get_num_inchannels()){ - message_t E_O_S(0,0); - for(const auto& sck : sockets) sendToSck(sck, &E_O_S); - } + if (++neos >= this->get_num_inchannels()) + for(const auto& sck : sockets) batchBuffers[sck].sendEOS(); } }; @@ -392,6 +413,22 @@ class ff_dsenderH : public ff_dsender { return sck; } + int getMostFilledInternalBufferSck(){ + int sckMax = 0; + int sizeMax = 0; + for(int sck : internalSockets){ + auto& b = batchBuffers[sck]; + if (b.size > sizeMax) { + sckMax = sck; + sizeMax = b.size; + } + } + if (sckMax > 0) return sckMax; + + last_rr_socket_Internal = (last_rr_socket_Internal + 1) % this->internalSockets.size(); + return sockets[last_rr_socket_Internal]; + } + public: ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int coreid=-1) : ff_dsender(e, gName, coreid), internalGroupNames(internalGroups) {} @@ -405,21 +442,6 @@ class ff_dsenderH : public ff_dsender { int svc_init() { - /* for(const auto& endpoint : this->dest_endpoints){ - int sck = tryConnect(endpoint); - if (sck <= 0) { - error("Error on connecting!\n"); - return -1; - } - bool isInternal = internalGroupNames.contains(endpoint.groupName); - if (isInternal) internalSockets.push_back(sck); - else sockets.push_back(sck); - socketsCounters[sck] = isInternal ? INTERNALQUEUEDIM : QUEUEDIM; - handshakeHandler(sck, isInternal); - } - - return 0; */ - if (coreid!=-1) ff_mapThreadToCpu(coreid); @@ -433,6 +455,22 @@ class ff_dsenderH : public ff_dsender { if (isInternal) internalSockets.push_back(sck); else sockets.push_back(sck); socketsCounters[sck] = isInternal ? INTERNALQUEUEDIM : QUEUEDIM; + batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(10, [this, sck](struct iovec* v, int size) -> bool { + + if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ + error("Errore waiting ack from socket inside the callback\n"); + return false; + } + + if (writevn(sck, v, size) < 0){ + error("Error sending the iovector inside the callback!\n"); + return false; + } + + this->socketsCounters[sck]--; + + return true; + })); // change with the correct size if (handshakeHandler(sck, isInternal) < 0) return -1; FD_SET(sck, &set); @@ -452,16 +490,11 @@ class ff_dsenderH : public ff_dsender { // pick destination from the list of internal connections! if (task->chid != -1){ // roundrobin over the destinations sck = internalDest2Socket[task->chid]; - if (socketsCounters[sck] == 0 && waitAckFrom(sck) == -1){ // blocking call if scheduling is ondemand - error("Error waiting Ack from....\n"); - delete task; return this->GO_ON; - } } else - sck = getNextReadyInternal(); + sck = getMostFilledInternalBufferSck(); + + batchBuffers[sck].push(task); - sendToSck(sck, task); - socketsCounters[sck]--; - delete task; return this->GO_ON; } @@ -473,7 +506,8 @@ class ff_dsenderH : public ff_dsender { // send the EOS to all the internal connections message_t E_O_S(0,0); for(const auto& sck : internalSockets) { - sendToSck(sck, &E_O_S); + batchBuffers[sck].sendEOS(); + while(socketsCounters[sck] == INTERNALQUEUEDIM) waitAckFrom(sck); FD_CLR(sck, &set); socketsCounters.erase(sck); if (sck == fdmax) { @@ -484,10 +518,8 @@ class ff_dsenderH : public ff_dsender { } ++neos; // count anyway a new EOS received! - } else if (++neos >= this->get_num_inchannels()){ - message_t E_O_S(0,0); - for(const auto& sck : sockets) sendToSck(sck, &E_O_S); - } + } else if (++neos >= this->get_num_inchannels()) + for(const auto& sck : sockets) batchBuffers[sck].sendEOS(); } }; From 60dab65be9139f7f19f8b609b6906bb866be6b29 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Tue, 22 Mar 2022 18:42:10 +0100 Subject: [PATCH 134/202] Working version of the batching feature --- ff/distributed/ff_batchbuffer.hpp | 60 +++++++++++++---------------- ff/distributed/ff_dgroup.hpp | 6 +-- ff/distributed/ff_dgroups.hpp | 7 ++++ ff/distributed/ff_dintermediate.hpp | 2 +- ff/distributed/ff_dreceiver.hpp | 10 ++--- ff/distributed/ff_dsender.hpp | 13 ++++--- 6 files changed, 48 insertions(+), 50 deletions(-) diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp index 6d782bba..e15ce639 100644 --- a/ff/distributed/ff_batchbuffer.hpp +++ b/ff/distributed/ff_batchbuffer.hpp @@ -6,11 +6,12 @@ class ff_batchBuffer { std::function callback; int batchSize; struct iovec iov[1024]; + std::vector> toCleanup; public: int size = 0; ff_batchBuffer() {} ff_batchBuffer(int _size, std::function cbk) : callback(cbk), batchSize(_size) { - if (_size*2+1 > 1024){ + if (_size*4+1 > 1024){ error("Size too big!\n"); abort(); } @@ -19,31 +20,22 @@ class ff_batchBuffer { } void push(message_t* m){ - - ff::cout << "Pushing something of size: " << m->data.getLen(); - - int sender_ = htonl(m->sender); - int chid_ = htonl(m->chid); - size_t size_ = htobe64(m->data.getLen()); - - char* buffer = new char[sizeof(int)+sizeof(int)+sizeof(size_t)+1]; - memcpy(buffer, &sender_, sizeof(int)); - memcpy(buffer+sizeof(int), &chid_, sizeof(int)); - memcpy(buffer+sizeof(int)+sizeof(int), &size_, sizeof(size_t)); + m->sender = htonl(m->sender); + m->chid = htonl(m->chid); + size_t* sz = new size_t(htobe64(m->data.getLen())); - /**reinterpret_cast(buffer) = htonl(m->sender); - *reinterpret_cast(buffer+sizeof(int)) = htonl(m->chid); - *reinterpret_cast(buffer+sizeof(int)+sizeof(int)) = htobe64(m->data.getLen()); - */ - int indexBase = size * 2; - iov[indexBase+1].iov_base = buffer; - iov[indexBase+1].iov_len = sizeof(int)+sizeof(int)+sizeof(size_t); - iov[indexBase+2].iov_base = m->data.getPtr(); - iov[indexBase+2].iov_len = m->data.getLen(); + int indexBase = size * 4; + iov[indexBase+1].iov_base = &m->sender; + iov[indexBase+1].iov_len = sizeof(int); + iov[indexBase+2].iov_base = &m->chid; + iov[indexBase+2].iov_len = sizeof(int); + iov[indexBase+3].iov_base = sz; + iov[indexBase+3].iov_len = sizeof(size_t); + iov[indexBase+4].iov_base = m->data.getPtr(); + iov[indexBase+4].iov_len = m->data.getLen(); - m->data.doNotCleanup(); - delete m; + toCleanup.emplace_back(sz, m); if (++size == batchSize) this->flush(); @@ -52,24 +44,24 @@ class ff_batchBuffer { void sendEOS(){ push(new message_t(0,0)); - flush(true); + flush(); } - void flush(bool last = false){ + void flush(){ if (size == 0) return; + + int size_ = size; + size = htonl(size); - ff::cout << "Sending out a buffer os size: " << size << std::endl; - if (!callback(iov, size*2+1-last)) + if (!callback(iov, size_*4+1)) error("Callback of the batchbuffer got something wrong!\n"); - for(size_t i = 0; i < size; i++){ - - iov[i*2+1].iov_len = 0; - delete [] (char*)iov[i*2+1].iov_base; - if (last && i == (size-1)) break; - iov[i*2+2].iov_len = 0; - delete [] (char*)iov[i*2+2].iov_base; + while (!toCleanup.empty()){ + delete toCleanup.back().first; + delete toCleanup.back().second; + toCleanup.pop_back(); } + size = 0; } diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 20f53a20..8c1b6f5d 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -131,7 +131,7 @@ class dGroup : public ff::ff_farm { this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); if (ir.hasSender) - this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName), true); + this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize), true); if (ir.hasRightChildren() && ir.parentBB->isAll2All()) { ff_a2a *a2a = reinterpret_cast(ir.parentBB); @@ -186,7 +186,7 @@ class dGroup : public ff::ff_farm { return n; }); - innerA2A->add_firstset(firstSet); // ondemand ??? clenaup?? + innerA2A->add_firstset(firstSet, reinterpret_cast(ir.parentBB)->ondemand_buffer()); // note the ondemand!! int outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? 0 : f.second.first.size());}); @@ -235,7 +235,7 @@ class dGroup : public ff::ff_farm { this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); if (ir.hasSender) - this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB) , true); + this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize) , true); } if (this->getNWorkers() == 0){ diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 11168e18..c648450d 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -187,6 +187,7 @@ class dGroups { std::string name; std::string address; int port; + int batchSize = 1; template void load( Archive & ar ){ @@ -198,6 +199,10 @@ class dGroups { address = endp[0]; port = std::stoi(endp[1]); } catch (cereal::Exception&) {ar.setNextName(nullptr);} + try { + ar(cereal::make_nvp("batchSize", batchSize)); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + } }; @@ -231,6 +236,8 @@ class dGroups { // annotate the listen endpoint for the specified group annotatedGroups[g.name].listenEndpoint = endpoint; + // set the batch size for each group + if (g.batchSize > 1) annotatedGroups[g.name].outBatchSize = g.batchSize; } // build the map parentBB -> diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index 9cfd5ce6..fb0d0a1c 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -79,7 +79,7 @@ class ff_IR { std::vector destinationEndpoints; std::set otherGroupsFromSameParentBB; size_t expectedEOS = 0; - + int outBatchSize = 1; // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table std::vector inputL, outputL, inputR, outputR; diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 7b5501af..9bdf1c1d 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -99,13 +99,12 @@ class ff_dreceiver: public ff_monode_t { virtual int handleBatch(int sck){ int requestSize; - if (readn(sck, reinterpret_cast(&requestSize), sizeof(requestSize)) != sizeof(requestSize)){ - perror("BatchReadn"); - error("Error receiving the number of tasks in the batch\n"); - return -1; + switch(readn(sck, reinterpret_cast(&requestSize), sizeof(requestSize))) { + case -1: error("Something went wrong in receiving the number of tasks!\n"); + case 0: return -1; } - ff::cout << "Receive a btach of " << requestSize << " items\n"; + requestSize = ntohl(requestSize); for(int i = 0; i < requestSize; i++) handleRequest(sck); @@ -156,7 +155,6 @@ class ff_dreceiver: public ff_monode_t { assert(out); out->sender = sender; out->chid = chid; - ff::cout << "Forwarding something of size: " << out->data.getLen() << std::endl; this->forward(out, sck); return 0; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 45b6e50a..67976e79 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -60,6 +60,7 @@ class ff_dsender: public ff_minode_t { std::map socketsCounters; std::map batchBuffers; std::string gName; + int batchSize; int coreid; fd_set set, tmpset; int fdmax = -1; @@ -300,11 +301,11 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int coreid=-1): gName(gName), coreid(coreid) { + ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int batchSize = 1, int coreid=-1): gName(gName), batchSize(batchSize), coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector dest_endpoints_, std::string gName = "", int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), coreid(coreid) {} + ff_dsender( std::vector dest_endpoints_, std::string gName = "", int batchSize = 1, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), coreid(coreid) {} @@ -321,7 +322,7 @@ class ff_dsender: public ff_minode_t { if (sck <= 0) return -1; sockets[i] = sck; socketsCounters[sck] = QUEUEDIM; - batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(1, [this, sck](struct iovec* v, int size) -> bool { + batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ error("Errore waiting ack from socket inside the callback\n"); @@ -431,8 +432,8 @@ class ff_dsenderH : public ff_dsender { public: - ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int coreid=-1) : ff_dsender(e, gName, coreid), internalGroupNames(internalGroups) {} - ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int coreid=-1) : ff_dsender(dest_endpoints_, gName, coreid), internalGroupNames(internalGroups) {} + ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int coreid=-1) : ff_dsender(e, gName, batchSize, coreid), internalGroupNames(internalGroups) {} + ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, coreid), internalGroupNames(internalGroups) {} int handshakeHandler(const int sck, bool isInternal){ if (sendGroupName(sck) < 0) return -1; @@ -455,7 +456,7 @@ class ff_dsenderH : public ff_dsender { if (isInternal) internalSockets.push_back(sck); else sockets.push_back(sck); socketsCounters[sck] = isInternal ? INTERNALQUEUEDIM : QUEUEDIM; - batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(10, [this, sck](struct iovec* v, int size) -> bool { + batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ error("Errore waiting ack from socket inside the callback\n"); From bea9870341fd616291fd0604af100ce1e8105010 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 30 Mar 2022 08:40:13 +0200 Subject: [PATCH 135/202] fixed termination problem in the distributed run-time. The test 'test_a2a_h3.cpp' has been extended. --- README.md | 31 +++-- ff/distributed/ff_dadapters.hpp | 10 +- ff/distributed/ff_dsender.hpp | 12 +- tests/distributed/test_a2a_h3.cpp | 201 ++++++++++++++++++------------ 4 files changed, 156 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index a7253107..53c6f74b 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,16 @@ # FastFlow: high-performance parallel patterns and building blocks in C++ -FastFlow is a programming library implemented in modern C++ and targeting -multi/many-cores (there exists an experimental version based on ZeroMQ targeting -distributed systems). It offers both a set of high-level ready-to-use parallel -patterns and a set of mechanisms and composable components -(called building blocks) to support low-latency and high-throughput data-flow -streaming networks. +FastFlow is a programming library implemented in modern C++ targeting +multi/many-cores and distributed systems (the distributed run-time is experimental). +It offers both a set of high-level ready-to-use parallel patterns and a set +of mechanisms and composable components (called building blocks) to support low-latency and high-throughput data-flow streaming networks. FastFlow simplifies the development of parallel applications modelled as a structured directed graph of processing nodes. The graph of concurrent nodes is constructed by the assembly of sequential -and parallel building blocks as well as higher-level easy-to-use components -(i.e. parallel patterns) modelling typical schemas of parallel computations -(e.g., pipeline, task-farm, parallel-for, etc.). -FastFlow efficiency stems from the optimized implementation of the base communication -and synchronization mechanisms and from its layered software design. +and parallel building blocks as well as higher-level parallel patterns modelling typical schemas of parallel computations (e.g., pipeline, task-farm, parallel-for, etc.). +FastFlow efficiency stems from the optimized implementation of the base communication and synchronization mechanisms and from its layered software design. ## FastFlow's Building Blocks @@ -69,16 +64,26 @@ they are added to the high-level layer and provided to the user. ## Building the library -FastFlow is header-only, no need for building. + +FastFlow is a header-only library, for the shared-memory run-time, there are basically no dependencies. +For the distributed-memory run-time, you need to install: + - Cereal for (automatic) serialization/deserialization purposes (https://uscilab.github.io/cereal/) + - OpenMPI for experimenting with the MPI communication back-end (https://www.open-mpi.org/software/ompi) + +While Cereal is mandatory, OpenMPI installation is optional and can be disabled at compile-time by compiling the +code with '-DDFF_EXCLUDE_MPI'. To compile tests with the distributed run-time you need a recent compiler +supporting the -std=c++20 standard. See the [BUILD.ME](BUILD.ME) file for instructions about building unit tests and examples. +NOTES: currently, the cmake-based compilation of distributed tests has been disabled. ## Supported Platforms FastFlow is currently actively supported for Linux with gcc >4.8, x86_64 and ARM Since version 2.0.4, FastFlow is expected to work on any platform with a C++11 compiler. ## FastFlow Maintainer -Massimo Torquati (University of Pisa) +Massimo Torquati (University of Pisa) + ## FastFlow History The FastFlow project started in the beginning of 2010 by Massimo Torquati (University of Pisa) and diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index a46c0c4f..ab105d54 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -29,6 +29,9 @@ class SquareBoxRight : public ff_minode { class SquareBoxLeft : public ff_monode { std::unordered_map destinations; public: + /* + * - localWorkers: list of pairs where logical_destination is the original destination of the shared-memory graph + */ SquareBoxLeft(const std::unordered_map localDestinations) : destinations(localDestinations) {} void* svc(void* in){ @@ -47,7 +50,7 @@ class EmitterAdapter: public internal_mo_transformer { * - n: rightmost sequential node of the builiding block representing the left-set worker * - totalWorkers: number of the workers on the right set (i.e., all the possible destinations) of the original entire a2a * - index: index of nodde n in the output list of the left set of the orgiginal entire a2a - * - localWorkers: list of pairs where logical_destination + * - localWorkers: list of pairs where logical_destination is the original destination of the shared-memory graph * - cleanup **/ EmitterAdapter(ff_node* n, int totalWorkers, int index, std::unordered_map localWorkers = {0,0}, bool cleanup=false): internal_mo_transformer(this, false), totalWorkers(totalWorkers), index(index), localWorkersMap(localWorkers) { @@ -111,7 +114,10 @@ class CollectorAdapter: public internal_mi_transformer { private: std::vector localWorkers; public: - + /* localWorkers: contains the ids of "real" (square boxes not included) left-workers + * connected with this node + * + */ CollectorAdapter(ff_node* n, std::vector localWorkers, bool cleanup=false): internal_mi_transformer(this, false), localWorkers(localWorkers) { this->n = n; this->cleanup = cleanup; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 3f8151d0..a25f64fe 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -324,12 +324,12 @@ class ff_dsenderH : public ff_dsender { if (id == (ssize_t)(this->get_num_inchannels() - 1)){ // send the EOS to all the internal connections message_t E_O_S(0,0); - for(const auto& sck : internalSockets) sendToSck(sck, &E_O_S); - ++neos; // count anyway a new EOS received! - } else if (++neos >= this->get_num_inchannels()){ - message_t E_O_S(0,0); - for(const auto& sck : sockets) sendToSck(sck, &E_O_S); - } + for(const auto& sck : internalSockets) sendToSck(sck, &E_O_S); + } + if (++neos >= this->get_num_inchannels()){ + message_t E_O_S(0,0); + for(const auto& sck : sockets) sendToSck(sck, &E_O_S); + } } }; diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp index 7611eec0..1ad430bd 100644 --- a/tests/distributed/test_a2a_h3.cpp +++ b/tests/distributed/test_a2a_h3.cpp @@ -1,15 +1,38 @@ /* - * |-> Forwarder1 ->| - * | | -> |-> Sink1 ->| - * Source ->|-> Forwarder2 ->| | | -> StringPrinter - * | | -> |-> Sink2 ->| - * |-> Forwarder3 ->| + * FastFlow concurrent network: * + * | -> Rnode2 -> | + * | | + * |-> Lnode1 ->| -> Rnode2 -> | + * Source ->| | | ---> Sink + * |-> Lnode2 ->| -> Rnode2 -> | + * | | + * | -> Rnode2 -> | * - * G0: Source - * G1: Forwarer1, Forwarder2, Sink1 - * G2: Forwarder3, Sink2 - * G3: StringPrinter + * + * distributed version: + * + * + * G1 G2 + * -------- ----------------------- + * | | | |-> Rnode1 | + * | Source | ----> | Lnode1 -->| | -->| ------ + * | | | | |-> Rnode2 | | | | + * -------- | ----------------------- |--> | Sink | + * | | ^ | | | + * | | | | ------ + * | v | | G4 + * | ----------------------- | + * ---> | |-> Rnode3 | -->| + * | Lnode2 -->| | + * | |-> Rnode4 | + * ----------------------- + * G3 + * + * + * ** MANUAL ** implementation of the distributed groups! + * + * WARNING: This test is intended for FastFlow developers!! * */ @@ -22,53 +45,75 @@ using namespace ff; std::mutex mtx; -struct RealSource : ff_monode_t{ - std::string* svc(std::string*){ - for(int i = 0; i < 2; i++) ff_send_out_to(new std::string("Trigger string!"), i); - return EOS; + + +struct myTask_t { + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + + + +struct Source : ff_monode_t{ + myTask_t* svc(myTask_t*){ + for(int i = 0; i < 100; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; } }; -struct Source : ff_monode_t{ +struct Lnode : ff_monode_t{ int numWorker, generatorID; - Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} - - std::string* svc(std::string* in){ - delete in; - std::cout << "Source starting generating tasks!" << std::endl; - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); - - std::cout << "Source generated all task sending now EOS!" << std::endl; - return EOS; + Lnode(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} + + myTask_t* svc(myTask_t* in){ + std::cout << "Lnode " << generatorID << "( " << get_my_id() << ") starting generating tasks!" << std::endl; + for(int i = 0; i < numWorker; i++) { + myTask_t* out = new myTask_t; + out->str = std::string(std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i))); + out->S.t = in->S.t; + out->S.f = in->S.f; + + ff_send_out_to(out, i); + } + delete in; + std::cout << "Lnode " << generatorID << " generated all task sending now EOS!" << std::endl; + return GO_ON; } }; -struct Sink : ff_minode_t{ - int sinkID; - Sink(int id): sinkID(id) {} - std::string* svc(std::string* in){ - std::string* output = new std::string(*in + " received by Sink " + std::to_string(sinkID) + " from " + std::to_string(get_channel_id())); - delete in; - return output; +struct Rnode : ff_minode_t{ + int ID; + Rnode(int id): ID(id) {} + myTask_t* svc(myTask_t* in){ + in->str =std::string(std::string(in->str + " received by Rnode " + std::to_string(ID) + " from channel " + std::to_string(get_channel_id()))); + return in; } }; -struct StringPrinter : ff_node_t{ - std::string* svc(std::string* in){ +struct Sink : ff_minode_t{ + myTask_t* svc(myTask_t* in){ const std::lock_guard lock(mtx); - std::cout << "Received something! Addr:" << in << "\n"; -#if 1 - try { - std::cout << *in << std::endl; - delete in; - } catch (const std::exception& ex){ - std::cerr << ex.what(); - } -#endif + std::cout << in->str << std::endl; + delete in; return this->GO_ON; } + }; @@ -102,59 +147,61 @@ int main(int argc, char*argv[]){ ff_farm gFarm; ff_a2a a2a; - a2a.createGroup("G1"); - a2a.createGroup("G2"); - ff_pipeline dummypipe; dummypipe.createGroup("G3"); dummypipe.createGroup("G0"); - - if (atoi(argv[1]) == 0){ - dGroups::Instance()->setRunningGroup("G0"); - gFarm.add_collector(new ff_dsender({g1, g2})); - gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); + gFarm.add_collector(new ff_dsender({g1, g2}, "G0")); + gFarm.add_workers({new WrapperOUT(new Source(), 1, true)}); gFarm.run_and_wait_end(); return 0; } else if (atoi(argv[1]) == 1){ - dGroups::Instance()->setRunningGroup("G1"); - gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0})); - gFarm.add_collector(new ff_dsenderH({g2,g3})); - - auto s = new Source(2,0); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); + gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0,1}, {"G2"})); + gFarm.add_collector(new ff_dsenderH({g2,g3}, "G1", {"G2"})); - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(0,0)})}); - auto sink = new Sink(0); - a2a.add_secondset({new ff_comb(new CollectorAdapter(sink, {0}, true), new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true)), new SquareBoxRight}); + auto s = new Lnode(4,0); + + auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 4, 0, {{0,0}, {1,1}}, true), true, true); + + a2a.add_firstset({ea, new SquareBoxLeft({{0,0}, {1,1}})}); + auto rnode0 = new Rnode(0); + auto rnode1 = new Rnode(1); + a2a.add_secondset({ + new ff_comb(new CollectorAdapter(rnode0, {0}, true), + new WrapperOUT(new ForwarderNode(rnode0->serializeF), 0, 1, true)), + new ff_comb(new CollectorAdapter(rnode1, {0}, true), + new WrapperOUT(new ForwarderNode(rnode1->serializeF), 1, 1, true)), + new SquareBoxRight}); // this box should be the last one! } else if (atoi(argv[1]) == 2) { - dGroups::Instance()->setRunningGroup("G2"); - gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH({g1, g3})); + gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {2,3}, {"G1"})); + gFarm.add_collector(new ff_dsenderH({g1, g3}, "G2", {"G1"})); + gFarm.cleanup_emitter(); gFarm.cleanup_collector(); - auto s = new Source(2,1); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); + auto s = new Lnode(4,1); + auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 4, 1, {{2,0}, {3,1}}, true), true, true); - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}, 0, true); + a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(2,0), std::make_pair(3,1)})}, 0, true); - auto sink = new Sink(1); + auto rnode2 = new Rnode(2); + auto rnode3 = new Rnode(3); a2a.add_secondset({ - new ff_comb(new CollectorAdapter(sink, {1}, true), - new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true), true, true), - new SquareBoxRight - }, true); - - + new ff_comb(new CollectorAdapter(rnode2, {1}, true), + new WrapperOUT(new ForwarderNode(rnode2->serializeF), 2, 1, true), true, true), + new ff_comb(new CollectorAdapter(rnode3, {1}, true), + new WrapperOUT(new ForwarderNode(rnode3->serializeF), 3, 1, true), true, true), + new SquareBoxRight // this box should be the last one! + }, true); } else { - dGroups::Instance()->setRunningGroup("G3"); gFarm.add_emitter(new ff_dreceiver(g3, 2)); - gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; + gFarm.add_workers({new WrapperIN(new Sink(), 1, true)}); + + gFarm.run_and_wait_end(); + return 0; } gFarm.add_workers({&a2a}); gFarm.run_and_wait_end(); + + return 0; } From f4d7484a8221de33b536c99b71b8733b06326f4c Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 1 Apr 2022 16:05:59 +0200 Subject: [PATCH 136/202] Fixed loader bug --- ff/distributed/ff_dreceiverMPI.hpp | 111 ++++++++++++++++---- ff/distributed/ff_dsenderMPI.hpp | 159 +++++++++++------------------ ff/distributed/ff_network.hpp | 9 +- ff/distributed/loader/dff_run.cpp | 2 +- tests/distributed/test_a2a_h3.cpp | 19 +--- tests/distributed/test_group1.cpp | 10 +- tests/distributed/test_group4.cpp | 4 +- 7 files changed, 166 insertions(+), 148 deletions(-) diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index fe190f36..4bac3115 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -17,14 +17,10 @@ using namespace ff; class ff_dreceiverMPI: public ff_monode_t { protected: - int sendRoutingTable(int rank){ + static int sendRoutingTable(const int rank, const std::vector& dest){ dataBuffer buff; std::ostream oss(&buff); cereal::PortableBinaryOutputArchive oarchive(oss); - std::vector reachableDestinations; - - for(auto const& p : this->routingTable) reachableDestinations.push_back(p.first); - - oarchive << reachableDestinations; + oarchive << dest; if (MPI_Send(buff.getPtr(), buff.getLen(), MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD) != MPI_SUCCESS){ error("Something went wrong sending the routing table!\n"); @@ -33,6 +29,30 @@ class ff_dreceiverMPI: public ff_monode_t { return 0; } + virtual int handshakeHandler(){ + int sz; + MPI_Status status; + MPI_Probe(MPI_ANY_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, &status); + MPI_Get_count(&status, MPI_BYTE, &sz); + char* buff = new char [sz]; + MPI_Recv(buff, sz, MPI_BYTE, status.MPI_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + std::vector reachableDestinations; + for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); + + return this->sendRoutingTable(status.MPI_SOURCE, reachableDestinations); + } + + virtual void registerEOS(int rank){ + neos++; + } + + virtual void forward(message_t* task, int){ + if (task->chid == -1) ff_send_out(task); + else ff_send_out_to(task, this->routingTable[task->chid]); // assume the routing table is consistent WARNING!!! + } + + public: ff_dreceiverMPI(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) : input_channels(input_channels), routingTable(routingTable), coreid(coreid) {} @@ -43,11 +63,8 @@ class ff_dreceiverMPI: public ff_monode_t { int r; - MPI_Status status; - for(size_t i = 0; i < input_channels; i++){ - MPI_Recv(&r, 1, MPI_INT, MPI_ANY_SOURCE, DFF_ROUTING_TABLE_REQUEST_TAG, MPI_COMM_WORLD, &status); - sendRoutingTable(status.MPI_SOURCE); - } + for(size_t i = 0; i < input_channels; i++) + handshakeHandler(); return 0; } @@ -57,6 +74,7 @@ class ff_dreceiverMPI: public ff_monode_t { Everything will be handled inside a while true in the body of this node where data is pulled from network */ message_t *svc(message_t* task) { + MPI_Request tmpAckReq; MPI_Status status; long header[3]; while(neos < input_channels){ @@ -68,7 +86,7 @@ class ff_dreceiverMPI: public ff_monode_t { size_t sz = header[0]; if (sz == 0){ - neos++; + registerEOS(status.MPI_SOURCE); continue; } @@ -82,12 +100,11 @@ class ff_dreceiverMPI: public ff_monode_t { assert(out); out->sender = header[1]; out->chid = header[2]; + + this->forward(out, status.MPI_SOURCE); - assert(out->chid>=0); - - //std::cout << "received something from " << sender << " directed to " << chid << std::endl; - - ff_send_out_to(out, this->routingTable[out->chid]); // assume the routing table is consistent WARNING!!! + MPI_Isend(&ACK, sizeof(ack_t), MPI_BYTE, status.MPI_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &tmpAckReq); + MPI_Request_free(&tmpAckReq); } @@ -99,11 +116,69 @@ class ff_dreceiverMPI: public ff_monode_t { size_t input_channels; std::map routingTable; int coreid; + ack_t ACK; }; -/** versione Ondemand */ +class ff_dreceiverHMPI : public ff_dreceiverMPI { + std::vector internalDestinations; + std::set internalGroupNames; + std::set internalRanks; + size_t internalNEos = 0, externalNEos = 0; + int next_rr_destination = 0; + + + virtual void registerEOS(int rank){ + neos++; + + if (!internalRanks.contains(rank)){ + if (++externalNEos == (input_channels-internalRanks.size())) + for(size_t i = 0; i < this->get_num_outchannels()-1; i++) ff_send_out_to(this->EOS, i); + } else + if (++internalNEos == internalRanks.size()) + ff_send_out_to(this->EOS, this->get_num_outchannels()-1); + } + + virtual int handshakeHandler(){ + int sz; + MPI_Status status; + MPI_Probe(MPI_ANY_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, &status); + MPI_Get_count(&status, MPI_BYTE, &sz); + char* buff = new char [sz]; + MPI_Recv(buff, sz, MPI_BYTE, status.MPI_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + // the connection is internal! + if (internalGroupNames.contains(std::string(buff, sz))) { + internalRanks.insert(status.MPI_SOURCE); + return this->sendRoutingTable(status.MPI_SOURCE, internalDestinations); + } + + std::vector reachableDestinations; + for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); + + return this->sendRoutingTable(status.MPI_SOURCE, reachableDestinations); + } + + void forward(message_t* task, int rank){ + if (internalRanks.contains(rank)) ff_send_out_to(task, this->get_num_outchannels()-1); + else if (task->chid != -1) ff_send_out_to(task, this->routingTable[task->chid]); + else { + ff_send_out_to(task, next_rr_destination); + next_rr_destination = ++next_rr_destination % (this->get_num_outchannels()-1); + } + } + +public: + ff_dreceiverHMPI(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, std::vector internalRoutingTable = {0}, std::set internalGroups = {}, int coreid=-1) + : ff_dreceiverMPI(input_channels, routingTable, coreid), internalDestinations(internalRoutingTable), internalGroupNames(internalGroups) {} + +}; + + + + +/** versione Ondemand */ class ff_dreceiverMPIOD: public ff_dreceiverMPI { public: ff_dreceiverMPIOD(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 7df12335..5a830511 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -22,17 +22,19 @@ using namespace ff; class ff_dsenderMPI: public ff_minode_t { protected: size_t neos=0; - int next_rr_destination = 0; //next destiation to send for round robin policy + int last_rr_rank = 0; //next destiation to send for round robin policy std::map dest2Rank; + std::map rankCounters; std::vector destRanks; + std::string gName; int coreid; - int receiveReachableDestinations(int rank){ + static int receiveReachableDestinations(int rank, std::map& m){ int sz; - int cmd = DFF_REQUEST_ROUTING_TABLE; + //int cmd = DFF_REQUEST_ROUTING_TABLE; MPI_Status status; - MPI_Send(&cmd, 1, MPI_INT, rank, DFF_ROUTING_TABLE_REQUEST_TAG, MPI_COMM_WORLD); + //MPI_Send(&cmd, 1, MPI_INT, rank, DFF_ROUTING_TABLE_REQUEST_TAG, MPI_COMM_WORLD); MPI_Probe(rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD, &status); MPI_Get_count(&status,MPI_BYTE, &sz); char* buff = new char [sz]; @@ -47,96 +49,73 @@ class ff_dsenderMPI: public ff_minode_t { iarchive >> destinationsList; - for (int d : destinationsList) dest2Rank[d] = rank; + for (const int& d : destinationsList) m[d] = rank; return 0; } - -public: - ff_dsenderMPI(ff_endpoint destRank, int coreid=-1) - : coreid(coreid) { - this->destRanks.push_back(std::move(destRank)); - } - - ff_dsenderMPI( std::vector destRanks_, int coreid=-1) - : destRanks(std::move(destRanks_)),coreid(coreid) {} + virtual int handshakeHandler(const int rank, bool){ + MPI_Send(gName.c_str(), gName.size(), MPI_BYTE, rank, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD); - int svc_init() { - if (coreid!=-1) - ff_mapThreadToCpu(coreid); - - for(ff_endpoint& ep: this->destRanks) - receiveReachableDestinations(ep.getRank()); - - return 0; + return receiveReachableDestinations(rank, dest2Rank); } - - message_t *svc(message_t* task) { - /* here i should send the task via socket */ - if (task->chid == -1){ // roundrobin over the destinations - task->chid = next_rr_destination; - next_rr_destination = (next_rr_destination + 1) % dest2Rank.size(); + int waitAckFrom(int rank){ + ack_t tmpAck; + MPI_Status status; + while(true){ + if (MPI_Recv(&tmpAck, sizeof(ack_t), MPI_BYTE, MPI_ANY_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) + return -1; + rankCounters[status.MPI_SOURCE]++; + if (rank == status.MPI_SOURCE) return 0; } - - size_t sz = task->data.getLen(); - int rank =dest2Rank[task->chid]; - - long header[3] = {(long)sz, task->sender, task->chid}; - - MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); - - MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); - - - delete task; - return this->GO_ON; } - void eosnotify(ssize_t) { - if (++neos >= this->get_num_inchannels()){ - long header[3] = {0,0,0}; - - for(auto& ep : destRanks) - MPI_Send(header, 3, MPI_LONG, ep.getRank(), DFF_HEADER_TAG, MPI_COMM_WORLD); + int waitAckFromAny(){ + ack_t tmpAck; + MPI_Status status; + if (MPI_Recv(&tmpAck, sizeof(ack_t), MPI_BYTE, MPI_ANY_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) + return -1; + rankCounters[status.MPI_SOURCE]++; + return status.MPI_SOURCE; + } + int getNextReady(){ + for(size_t i = 0; i < this->destRanks.size(); i++){ + int rankIndex = (last_rr_rank + 1 + i) % this->destRanks.size(); + int rank = destRanks[rankIndex].getRank(); + if (rankCounters[rank] > 0) { + last_rr_rank = rankIndex; + return rank; + } } + return waitAckFromAny(); } -}; - - -/* versione Ondemand */ -class ff_dsenderMPIOD: public ff_dsenderMPI { -private: - int last_rr_rank = 0; //next destiation to send for round robin policy - std::map rankCounters; - int queueDim; - public: - ff_dsenderMPIOD(ff_endpoint destRank, int queueDim_ = 1, int coreid=-1) - : ff_dsenderMPI(destRank, coreid), queueDim(queueDim_) {} + ff_dsenderMPI(ff_endpoint destRank, std::string gName = "", int coreid=-1) + : gName(gName), coreid(coreid) { + this->destRanks.push_back(std::move(destRank)); + } - ff_dsenderMPIOD( std::vector destRanks_, int queueDim_ = 1, int coreid=-1) - : ff_dsenderMPI(destRanks_, coreid), queueDim(queueDim_){} + ff_dsenderMPI( std::vector destRanks_, std::string gName = "", int coreid=-1) + : destRanks(std::move(destRanks_)), gName(gName), coreid(coreid) {} int svc_init() { - std::cout << "instantiating the ondemand mpi sender!\n"; - if (coreid!=-1) ff_mapThreadToCpu(coreid); for(ff_endpoint& ep: this->destRanks){ - receiveReachableDestinations(ep.getRank()); - rankCounters[ep.getRank()] = queueDim; - } + handshakeHandler(ep.getRank(), false); + rankCounters[ep.getRank()] = QUEUEDIM; + + } return 0; } void svc_end(){ - long totalack = destRanks.size()*queueDim; + long totalack = destRanks.size()*QUEUEDIM; long currack = 0; for(const auto& pair : rankCounters) currack += pair.second; @@ -146,39 +125,6 @@ class ff_dsenderMPIOD: public ff_dsenderMPI { currack++; } } - - inline int waitAckFromAny(){ - ack_t tmpAck; - MPI_Status status; - if (MPI_Recv(&tmpAck, sizeof(ack_t), MPI_BYTE, MPI_ANY_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) - return -1; - rankCounters[status.MPI_SOURCE]++; - return status.MPI_SOURCE; - } - - int waitAckFrom(int rank){ - ack_t tmpAck; - MPI_Status status; - while(true){ - if (MPI_Recv(&tmpAck, sizeof(ack_t), MPI_BYTE, MPI_ANY_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) - return -1; - rankCounters[status.MPI_SOURCE]++; - if (rank == status.MPI_SOURCE) return 0; - } - } - - int getNextReady(){ - for(size_t i = 0; i < this->destRanks.size(); i++){ - int rankIndex = (last_rr_rank + 1 + i) % this->destRanks.size(); - int rank = destRanks[rankIndex].getRank(); - if (rankCounters[rank] > 0) { - last_rr_rank = rankIndex; - return rank; - } - } - return waitAckFromAny(); - } - message_t *svc(message_t* task) { int rank; @@ -188,9 +134,8 @@ class ff_dsenderMPIOD: public ff_dsenderMPI { error("Error waiting ACK\n"); delete task; return this->GO_ON; } - } else { + } else rank = getNextReady(); - } size_t sz = task->data.getLen(); @@ -205,6 +150,16 @@ class ff_dsenderMPIOD: public ff_dsenderMPI { delete task; return this->GO_ON; } + + void eosnotify(ssize_t) { + if (++neos >= this->get_num_inchannels()){ + long header[3] = {0,0,0}; + + for(auto& ep : destRanks) + MPI_Send(header, 3, MPI_LONG, ep.getRank(), DFF_HEADER_TAG, MPI_COMM_WORLD); + + } + } }; #endif diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 4e810c26..7d33aa21 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -98,14 +98,6 @@ class dataBuffer: public std::stringbuf { using ffDbuffer = std::pair; -struct SMmessage_t { - SMmessage_t(){} - SMmessage_t(void* t, int s, int d) : task(t), sender(s), dst(d) {} - void * task; - int sender; - int dst; -}; - struct message_t { message_t(){} message_t(int sender, int chid) : sender(sender), chid(chid) {} @@ -217,6 +209,7 @@ static inline ssize_t recvnnb(int fd, char *buf, size_t size) { #define DFF_TASK_TAG 3 #define DFF_HEADER_TAG 4 #define DFF_ACK_TAG 5 + #define DFF_GROUP_NAME_TAG 6 #define DFF_REQUEST_ROUTING_TABLE 10 #endif diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 5e3c8dd9..30e3d5ef 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -101,7 +101,7 @@ struct G { void run(){ char b[1024]; // ssh -t // trovare MAX ARGV - sprintf(b, " %s %s %s %s --DFF_Config=%s --DFF_GName=%s %s 2>&1 %s", (isRemote() ? "ssh -t '" : ""), (isRemote() ? host.c_str() : "") , this->preCmd.c_str(), executable.c_str(), configFile.c_str(), this->name.c_str(), toBePrinted(this->name) ? "" : "> /dev/null", (isRemote() ? "'" : "")); + sprintf(b, " %s %s %s %s %s --DFF_Config=%s --DFF_GName=%s %s 2>&1 %s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : ""), (isRemote() ? "'" : ""), this->preCmd.c_str(), executable.c_str(), configFile.c_str(), this->name.c_str(), toBePrinted(this->name) ? "" : "> /dev/null", (isRemote() ? "'" : "")); std::cout << "Executing the following command: " << b << std::endl; file = popen(b, "r"); fd = fileno(file); diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp index 7611eec0..a3c9ce2f 100644 --- a/tests/distributed/test_a2a_h3.cpp +++ b/tests/distributed/test_a2a_h3.cpp @@ -102,22 +102,15 @@ int main(int argc, char*argv[]){ ff_farm gFarm; ff_a2a a2a; - a2a.createGroup("G1"); - a2a.createGroup("G2"); - ff_pipeline dummypipe; dummypipe.createGroup("G3"); dummypipe.createGroup("G0"); - - if (atoi(argv[1]) == 0){ - dGroups::Instance()->setRunningGroup("G0"); - gFarm.add_collector(new ff_dsender({g1, g2})); + gFarm.add_collector(new ff_dsender({g1, g2}, "G0")); gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); gFarm.run_and_wait_end(); return 0; } else if (atoi(argv[1]) == 1){ - dGroups::Instance()->setRunningGroup("G1"); - gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0})); - gFarm.add_collector(new ff_dsenderH({g2,g3})); + gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0}, {"G2"})); + gFarm.add_collector(new ff_dsenderH({g2,g3}, "G1", {"G2"})); auto s = new Source(2,0); auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); @@ -127,9 +120,8 @@ int main(int argc, char*argv[]){ a2a.add_secondset({new ff_comb(new CollectorAdapter(sink, {0}, true), new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true)), new SquareBoxRight}); } else if (atoi(argv[1]) == 2) { - dGroups::Instance()->setRunningGroup("G2"); - gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH({g1, g3})); + gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1}, {"G1"})); + gFarm.add_collector(new ff_dsenderH({g1, g3}, "G2", {"G1"})); gFarm.cleanup_emitter(); gFarm.cleanup_collector(); @@ -148,7 +140,6 @@ int main(int argc, char*argv[]){ } else { - dGroups::Instance()->setRunningGroup("G3"); gFarm.add_emitter(new ff_dreceiver(g3, 2)); gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); diff --git a/tests/distributed/test_group1.cpp b/tests/distributed/test_group1.cpp index 07f172c2..be2cdd5d 100644 --- a/tests/distributed/test_group1.cpp +++ b/tests/distributed/test_group1.cpp @@ -47,6 +47,10 @@ struct Source : ff_monode_t{ return EOS; } + + void svc_end(){ + ff::cout << "Source ended!\n"; + } }; struct MoNode : ff_monode_t{ @@ -58,7 +62,7 @@ struct MoNode : ff_monode_t{ void svc_end(){ const std::lock_guard lock(mtx); - std::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + ff::cout << "[SxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; } }; @@ -71,7 +75,7 @@ struct MiNode : ff_minode_t{ void svc_end(){ const std::lock_guard lock(mtx); - std::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; + ff::cout << "[DxNode" << this->get_my_id() << "] Processed Items: " << processedItems << std::endl; } }; @@ -87,7 +91,7 @@ struct Sink : ff_minode_t{ int local_sum = 0; for(int i = 0; i < ITEMS; i++) local_sum += i; const std::lock_guard lock(mtx); - std::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; + ff::cout << "Sum: " << sum << " (Expected: " << local_sum << ")" << std::endl; } }; diff --git a/tests/distributed/test_group4.cpp b/tests/distributed/test_group4.cpp index 38e2b3e1..5bd95188 100644 --- a/tests/distributed/test_group4.cpp +++ b/tests/distributed/test_group4.cpp @@ -33,7 +33,7 @@ struct Source : ff_monode_t{ std::string* svc(std::string* in){ long outchannels = get_num_outchannels(); - + ff::cout << "Expected out channels: " << outchannels << std::endl; for(long i = 0; i < outchannels; i++) ff_send_out_to(new std::string("Task generated from " + std::to_string(get_my_id()) + " for " + std::to_string(i)), i); @@ -45,7 +45,7 @@ struct Source : ff_monode_t{ struct Sink : ff_minode_t{ std::string* svc(std::string* in){ const std::lock_guard lock(mtx); - std::cout << *in << " received by Sink " << get_my_id() << " from " << get_channel_id() << std::endl; + ff::cout << *in << " received by Sink " << get_my_id() << " from " << get_channel_id() << std::endl; delete in; return this->GO_ON; } From ecb531404c6d492934685656ab7f85210c9a239c Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 1 Apr 2022 16:40:54 +0200 Subject: [PATCH 137/202] Fixed bugs on parametricPerf --- tests/distributed/runParametricPerf.sh | 18 ++++-------------- tests/distributed/test_parametricPerf.cpp | 15 ++++++++------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/tests/distributed/runParametricPerf.sh b/tests/distributed/runParametricPerf.sh index 402da7da..9d15dc51 100755 --- a/tests/distributed/runParametricPerf.sh +++ b/tests/distributed/runParametricPerf.sh @@ -5,8 +5,8 @@ ITEMS=1000 BYTExITEM=1000 EXECTIMESOURCE=10 EXECTIMESINK=10 -WORKERSXPROCESS=1 - +WORKERSXPROCESSSX=1 +WORKERSXPROCESSDX=20 ## this scripts allow to run a solution in the cluster automatically assigning addresses and ports ## @@ -24,14 +24,6 @@ if [ -z ${4+x} ]; then start_node=1; else start_node=$4; fi echo "Running $1 with $2 Sx processes and $3 Dx processes starting from node $start_node" rm tmpFilePP.json -connString="" - -for (( i=0; i<$3-1; i++)) -do - connString+="\"D${i}\", " -done - -connString+="\"D$(($3-1))\"" echo "{ \"groups\" : [" >> tmpFilePP.json @@ -41,8 +33,7 @@ for (( i=0; i<$2; i++)) do echo " { \"endpoint\" : \"compute$(($start_node+$i)):8000\", - \"name\" : \"S${i}\", - \"OConn\" : [${connString}] + \"name\" : \"S${i}\" } ," >> tmpFilePP.json done @@ -67,8 +58,7 @@ echo " ] ## #items #byteXitem #execTimeSource #execTimeSink #nw_sx #nw_dx ### -DFF_RUN_HOME=../../ff/distributed/loader -$DFF_RUN_HOME/dff_run -V -f tmpFilePP.json $1 $ITEMS $BYTExITEM $EXECTIMESOURCE $EXECTIMESINK $2 $3 $WORKERSXPROCESS +dff_run -V -f tmpFilePP.json $1 $ITEMS $BYTExITEM $EXECTIMESOURCE $EXECTIMESINK $2 $3 $WORKERSXPROCESSSX $WORKERSXPROCESSDX #rm tmpFilePP.json #exiting just for testin purpose diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 3f7c474c..edb8765d 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -165,7 +165,7 @@ int main(int argc, char*argv[]){ return -1; } - if (argc < 8){ + if (argc < 9){ std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_sx #np_dx #nwXp" << std::endl; return -1; } @@ -176,7 +176,8 @@ int main(int argc, char*argv[]){ int execTimeSink = atoi(argv[4]); int numProcSx = atoi(argv[5]); int numProcDx = atoi(argv[6]); - int numWorkerXProcess = atoi(argv[7]); + int numWorkerXProcessSx = atoi(argv[7]); + int numWorkerXProcessDx = atoi(argv[8]); char* p=nullptr; if ((p=getenv("CHECK_DATA"))!=nullptr) check=true; printf("chackdata = %s\n", p); @@ -186,10 +187,10 @@ int main(int argc, char*argv[]){ std::vector sxWorkers; std::vector dxWorkers; - for(int i = 0; i < (numProcSx*numWorkerXProcess); i++) - sxWorkers.push_back(new MoNode(ceil((double)items/(numProcSx*numWorkerXProcess)), execTimeSource, bytexItem, check)); + for(int i = 0; i < (numProcSx*numWorkerXProcessSx); i++) + sxWorkers.push_back(new MoNode(ceil((double)items/(numProcSx*numWorkerXProcessSx)), execTimeSource, bytexItem, check)); - for(int i = 0; i < (numProcDx*numWorkerXProcess); i++) + for(int i = 0; i < (numProcDx*numWorkerXProcessDx); i++) dxWorkers.push_back(new MiNode(execTimeSink, check)); a2a.add_firstset(sxWorkers); @@ -197,14 +198,14 @@ int main(int argc, char*argv[]){ for(int i = 0; i < numProcSx; i++){ auto g = a2a.createGroup(std::string("S")+std::to_string(i)); - for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ + for(int j = i*numWorkerXProcessSx; j < (i+1)*numWorkerXProcessSx; j++){ g << sxWorkers[j]; } } for(int i = 0; i < numProcDx; i++){ auto g = a2a.createGroup(std::string("D")+std::to_string(i)); - for(int j = i*numWorkerXProcess; j < (i+1)*numWorkerXProcess; j++){ + for(int j = i*numWorkerXProcessDx; j < (i+1)*numWorkerXProcessDx; j++){ g << dxWorkers[j]; } } From 0bf18cfea3f09b2b0b89b92f2d3caf4efbe283c0 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 2 Apr 2022 12:23:59 +0200 Subject: [PATCH 138/202] fixed problem in the parametricPerf, minor optimizations for distributed sender/receiver --- ff/distributed/ff_dreceiver.hpp | 15 ++++++--------- ff/distributed/ff_dsender.hpp | 11 ++++++----- tests/distributed/test_parametricPerf.cpp | 3 ++- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 9bdf1c1d..2fa55b06 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -103,21 +103,18 @@ class ff_dreceiver: public ff_monode_t { case -1: error("Something went wrong in receiving the number of tasks!\n"); case 0: return -1; } - - requestSize = ntohl(requestSize); - - for(int i = 0; i < requestSize; i++) - handleRequest(sck); - - - // always sending back the acknowledgement + // always sending back the acknowledgement if (writen(sck, reinterpret_cast(&ACK), sizeof(ack_t)) < 0){ if (errno != ECONNRESET || errno != EPIPE) { error("Error sending back ACK to the sender (errno=%d)\n",errno); return -1; } } - + + requestSize = ntohl(requestSize); + for(int i = 0; i < requestSize; i++) + handleRequest(sck); + return 0; } diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 67976e79..64a34171 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -25,8 +25,8 @@ #ifndef FF_DSENDER_H #define FF_DSENDER_H -#define QUEUEDIM 1 -#define INTERNALQUEUEDIM 1 +#define QUEUEDIM 100 +#define INTERNALQUEUEDIM 10 #include #include @@ -517,10 +517,11 @@ class ff_dsenderH : public ff_dsender { } } - ++neos; // count anyway a new EOS received! + //++neos; // count anyway a new EOS received! - } else if (++neos >= this->get_num_inchannels()) - for(const auto& sck : sockets) batchBuffers[sck].sendEOS(); + } + if (++neos >= this->get_num_inchannels()) + for(const auto& sck : sockets) batchBuffers[sck].sendEOS(); } }; diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 3f7c474c..17337c62 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -20,7 +20,7 @@ #include #include -//#define MANUAL_SERIALIZATION 1 +#define MANUAL_SERIALIZATION 1 // ------------------------------------------------------ std::mutex mtx; // used only for pretty printing @@ -82,6 +82,7 @@ template void deserialize(const Buffer&b, ExcType*& Ptr){ ExcType* p = new (b.first) ExcType(true); p->clen = b.second - sizeof(ExcType); + p->C = (char*)p + sizeof(ExcType); Ptr = p; } #endif From d3b9bf4825b0ae760d251e4981c6423512143ea5 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Sat, 2 Apr 2022 14:49:19 +0200 Subject: [PATCH 139/202] Added new feature to specify in the config file on the fly messages for both internal and external connection. Added new parametricPerf test horizontal --- ff/distributed/ff_dgroup.hpp | 4 +- ff/distributed/ff_dgroups.hpp | 12 ++ ff/distributed/ff_dintermediate.hpp | 1 + ff/distributed/ff_dsender.hpp | 12 +- tests/distributed/test_parametricPerfH.cpp | 215 ++++++++++++++++++++ tests/distributed/test_parametricPerfH.json | 17 ++ 6 files changed, 254 insertions(+), 7 deletions(-) create mode 100644 tests/distributed/test_parametricPerfH.cpp create mode 100644 tests/distributed/test_parametricPerfH.json diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 8c1b6f5d..b4897d97 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -131,7 +131,7 @@ class dGroup : public ff::ff_farm { this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); if (ir.hasSender) - this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize), true); + this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); if (ir.hasRightChildren() && ir.parentBB->isAll2All()) { ff_a2a *a2a = reinterpret_cast(ir.parentBB); @@ -235,7 +235,7 @@ class dGroup : public ff::ff_farm { this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); if (ir.hasSender) - this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize) , true); + this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF) , true); } if (this->getNWorkers() == 0){ diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index c648450d..785057fe 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -188,6 +188,8 @@ class dGroups { std::string address; int port; int batchSize = 1; + int internalMessageOTF = 10; + int messageOTF = 100; template void load( Archive & ar ){ @@ -203,6 +205,14 @@ class dGroups { ar(cereal::make_nvp("batchSize", batchSize)); } catch (cereal::Exception&) {ar.setNextName(nullptr);} + try { + ar(cereal::make_nvp("internalMessageOTF", internalMessageOTF)); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + + try { + ar(cereal::make_nvp("messageOTF", messageOTF)); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + } }; @@ -238,6 +248,8 @@ class dGroups { annotatedGroups[g.name].listenEndpoint = endpoint; // set the batch size for each group if (g.batchSize > 1) annotatedGroups[g.name].outBatchSize = g.batchSize; + if (g.messageOTF) annotatedGroups[g.name].messageOTF = g.messageOTF; + if (g.internalMessageOTF) annotatedGroups[g.name].internalMessageOTF = g.internalMessageOTF; } // build the map parentBB -> diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index fb0d0a1c..e2c53956 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -80,6 +80,7 @@ class ff_IR { std::set otherGroupsFromSameParentBB; size_t expectedEOS = 0; int outBatchSize = 1; + int messageOTF, internalMessageOTF; // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table std::vector inputL, outputL, inputR, outputR; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 64a34171..85c75f0b 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -61,6 +61,7 @@ class ff_dsender: public ff_minode_t { std::map batchBuffers; std::string gName; int batchSize; + int messageOTF; int coreid; fd_set set, tmpset; int fdmax = -1; @@ -301,11 +302,11 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int batchSize = 1, int coreid=-1): gName(gName), batchSize(batchSize), coreid(coreid) { + ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int batchSize = 1, int messageOTF = 100, int coreid=-1): gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector dest_endpoints_, std::string gName = "", int batchSize = 1, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), coreid(coreid) {} + ff_dsender( std::vector dest_endpoints_, std::string gName = "", int batchSize = 1, int messageOTF = 100, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} @@ -321,7 +322,7 @@ class ff_dsender: public ff_minode_t { int sck = tryConnect(this->dest_endpoints[i]); if (sck <= 0) return -1; sockets[i] = sck; - socketsCounters[sck] = QUEUEDIM; + socketsCounters[sck] = messageOTF; batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ @@ -392,6 +393,7 @@ class ff_dsenderH : public ff_dsender { std::vector internalSockets; int last_rr_socket_Internal = -1; std::set internalGroupNames; + int internalMessageOTF; int getNextReadyInternal(){ for(size_t i = 0; i < this->internalSockets.size(); i++){ @@ -432,8 +434,8 @@ class ff_dsenderH : public ff_dsender { public: - ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int coreid=-1) : ff_dsender(e, gName, batchSize, coreid), internalGroupNames(internalGroups) {} - ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, coreid), internalGroupNames(internalGroups) {} + ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int messageOTF = 100, int internalMessageOTF = 10, int coreid=-1) : ff_dsender(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int messageOTF = 100, int internalMessageOTF = 10, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} int handshakeHandler(const int sck, bool isInternal){ if (sendGroupName(sck) < 0) return -1; diff --git a/tests/distributed/test_parametricPerfH.cpp b/tests/distributed/test_parametricPerfH.cpp new file mode 100644 index 00000000..90d39523 --- /dev/null +++ b/tests/distributed/test_parametricPerfH.cpp @@ -0,0 +1,215 @@ +/* + * FastFlow concurrent network: + * + * |--> MiNode + * MoNode-->| + * |--> MiNode + * MoNode-->| + * |--> MiNode + * + * /<------- a2a ------>/ + * + * S: all left-hand side nodes + * D: all righ-hand side nodes + * + */ + + +#include +#include +#include +#include + +//#define MANUAL_SERIALIZATION 1 + +// ------------------------------------------------------ +std::mutex mtx; // used only for pretty printing +static inline float active_delay(int msecs) { + // read current time + float x = 1.25f; + auto start = std::chrono::high_resolution_clock::now(); + auto end = false; + while(!end) { + auto elapsed = std::chrono::high_resolution_clock::now() - start; + auto msec = std::chrono::duration_cast(elapsed).count(); + x *= sin(x) / atan(x) * tanh(x) * sqrt(x); + if(msec>=msecs) + end = true; + } + return x; +} +// this assert will not be removed by -DNDEBUG +#define myassert(c) { \ + if (!(c)) { \ + std::cerr << "ERROR: assert at line " << __LINE__ << " failed\n"; \ + abort(); \ + } \ + } +// ----------------------------------------------------- +struct ExcType { + ExcType():contiguous(false) {} + ExcType(bool): contiguous(true) {} + ~ExcType() { + if (!contiguous) + delete [] C; + } + + size_t clen = 0; + char* C = nullptr; + bool contiguous; + + + template + void serialize(Archive & archive) { + archive(clen); + if (!C) { + myassert(!contiguous); + C = new char[clen]; + } + archive(cereal::binary_data(C, clen)); + } + +}; + + +#ifdef MANUAL_SERIALIZATION +template +void serialize(Buffer&b, ExcType* input){ + b = {(char*)input, input->clen+sizeof(ExcType)}; +} + +template +void deserialize(const Buffer&b, ExcType*& Ptr){ + ExcType* p = new (b.first) ExcType(true); + p->C = (char*)p + sizeof(ExcType); + p->clen = b.second - sizeof(ExcType); + Ptr = p; +} +#endif + +static ExcType* allocateExcType(size_t size, bool setdata=false) { + char* _p = (char*)malloc(size+sizeof(ExcType)); + ExcType* p = new (_p) ExcType(true); // contiguous allocation + + p->clen = size; + p->C = (char*)p+sizeof(ExcType); + if (setdata) { + bzero(p->C, p->clen); + p->C[0] = 'c'; + if (size>10) + p->C[10] = 'i'; + if (size>100) + p->C[100] = 'a'; + if (size>500) + p->C[500] = 'o'; + } + p->C[p->clen-1] = 'F'; + return p; +} + + +struct MoNode : ff::ff_monode_t{ + int items, execTime; + long dataLength; + bool checkdata; + MoNode(int itemsToGenerate, int execTime, long dataLength, bool checkdata): + items(itemsToGenerate), execTime(execTime), dataLength(dataLength), checkdata(checkdata) {} + + ExcType* svc(ExcType*){ + for(int i=0; i< items; i++){ + if (execTime) active_delay(this->execTime); + ff_send_out(allocateExcType(dataLength, checkdata)); + } + return this->EOS; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + ff::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << ff::endl; + } +}; + +struct MiNode : ff::ff_minode_t{ + int processedItems = 0; + int execTime; + bool checkdata; + MiNode(int execTime, bool checkdata=false): execTime(execTime),checkdata(checkdata) {} + + ExcType* svc(ExcType* in){ + if (execTime) active_delay(this->execTime); + ++processedItems; + if (checkdata) { + myassert(in->C[0] == 'c'); + if (in->clen>10) + myassert(in->C[10] == 'i'); + if (in->clen>100) + myassert(in->C[100] == 'a'); + if (in->clen>500) + myassert(in->C[500] == 'o'); + ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; + } + myassert(in->C[in->clen-1] == 'F'); + delete in; + return this->GO_ON; + } + + void svc_end(){ + const std::lock_guard lock(mtx); + ff::cout << "[MiNode" << this->get_my_id() << "] Processed Items: " << processedItems << ff::endl; + } +}; + +int main(int argc, char*argv[]){ + + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + if (argc < 8){ + std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_dx #nwXp" << std::endl; + return -1; + } + bool check = false; + int items = atoi(argv[1]); + long bytexItem = atol(argv[2]); + int execTimeSource = atoi(argv[3]); + int execTimeSink = atoi(argv[4]); + int numProcDx = atoi(argv[5]); + int numWorkerXProcessSx = atoi(argv[6]); + int numWorkerXProcessDx = atoi(argv[7]); + char* p=nullptr; + if ((p=getenv("CHECK_DATA"))!=nullptr) check=true; + printf("chackdata = %s\n", p); + + ff::ff_a2a a2a; + + std::vector sxWorkers; + std::vector dxWorkers; + + sxWorkers.push_back(new MoNode(items, execTimeSource, bytexItem, check)); + + for(int i = 0; i < ((numProcDx+1)*numWorkerXProcessDx); i++) + dxWorkers.push_back(new MiNode(execTimeSink, check)); + + a2a.add_firstset(sxWorkers, 1); //ondemand on!! + a2a.add_secondset(dxWorkers); + + auto master = a2a.createGroup("M"); + master << sxWorkers.front(); + for(int j = 0; j < numWorkerXProcessDx; j++) + master << dxWorkers[j]; + + for(int i = 0; i < numProcDx; i++){ + auto g = a2a.createGroup(std::string("D")+std::to_string(i)); + for(int j = (i+1)*numWorkerXProcessDx; j < (i+2)*numWorkerXProcessDx; j++){ + g << dxWorkers[j]; + } + } + + if (a2a.run_and_wait_end()<0) { + error("running mainPipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_parametricPerfH.json b/tests/distributed/test_parametricPerfH.json new file mode 100644 index 00000000..64fc519c --- /dev/null +++ b/tests/distributed/test_parametricPerfH.json @@ -0,0 +1,17 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8000", + "name" : "M" + }, + { + "endpoint" : "localhost:8002", + "name" : "D0" + } + , + { + "endpoint" : "localhost:8003", + "name" : "D1" + } + ] +} From 0c933eee852983abe2bb36bc770fd67c05e05788 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 2 Apr 2022 19:33:26 +0200 Subject: [PATCH 140/202] fixed several issues with the on-demand task scheduling policy between distributed groups --- ff/all2all.hpp | 9 +++- ff/dff.hpp | 12 +++++ ff/distributed/ff_dadapters.hpp | 57 ++++++++++----------- ff/distributed/ff_dgroups.hpp | 22 +++++--- ff/distributed/ff_dreceiver.hpp | 7 ++- ff/distributed/ff_dsender.hpp | 32 ++++-------- ff/lb.hpp | 5 ++ ff/pipeline.hpp | 9 +++- ff/ubuffer.hpp | 8 +-- tests/distributed/test_parametricPerfH.cpp | 40 +++++++-------- tests/distributed/test_parametricPerfH.json | 3 +- 11 files changed, 117 insertions(+), 87 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 7813085e..7cfbe2b5 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -148,8 +148,13 @@ class ff_a2a: public ff_node { } } for(size_t i=0;iondemand_buffer()==0)) - workers1[i]->set_scheduling_ondemand(ondemand_chunk); + if (ondemand_chunk && (workers1[i]->ondemand_buffer()==0)) { + svector w; + workers1[i]->get_out_nodes(w); + for(size_t k=0;kset_scheduling_ondemand(ondemand_chunk); + //workers1[i]->set_scheduling_ondemand(ondemand_chunk); + } workers1[i]->set_id(int(i)); } // checking R-Workers diff --git a/ff/dff.hpp b/ff/dff.hpp index 43987529..725eb443 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -36,6 +36,18 @@ #undef BLOCKING_MODE #endif + +#if !defined(DEFAULT_BATCH_SIZE) +#define DEFAULT_BATCH_SIZE 1 +#endif +#if !defined(DEFAULT_INTERNALMSG_OTF) +#define DEFAULT_INTERNALMSG_OTF 10 +#endif +#if !defined(DEFAULT_MESSAGE_OTF) +#define DEFAULT_MESSAGE_OTF 100 +#endif + + #include #include #include diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index de1e2885..9097d9e3 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -19,18 +19,17 @@ class SquareBoxRight : public ff_minode { public: int svc_init() { - - // change the size of the queue to the Sender, since the distributed-memory communications are all on-demand - svector w; - this->get_out_nodes(w); - size_t oldsz; - assert(w.size() == 1); - w[0]->change_inputqueuesize(1, oldsz); - + // change the size of the queue towards the Sender + // forcing the queue to be bounded of capacity 1 + size_t oldsz; + change_outputqueuesize(1, oldsz); + assert(oldsz != 0); return 0; } - void* svc(void* in) {return in;} + void* svc(void* in) { + return in; + } void eosnotify(ssize_t id) { if (id == (ssize_t)(this->get_num_inchannels() - 1)) return; // EOS coming from the SquareLeft, we must ignore it @@ -74,6 +73,26 @@ class EmitterAdapter: public internal_mo_transformer { registerCallback(ff_send_out_to_cbk, this); } + int svc_init() { + if (this->n->isMultiOutput()) { + ff_monode* mo = reinterpret_cast(this->n); + //mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers + mo->set_running(totalWorkers); + } + + // change the size of the queue to the SquareBoxRight (if present), + // since the distributed-memory communications are all on-demand + svector w; + this->get_out_nodes(w); + assert(w.size()>0); + if (w.size() > localWorkersMap.size()) { + assert(w.size() == localWorkersMap.size()+1); + size_t oldsz; + w[localWorkersMap.size()]->change_inputqueuesize(1, oldsz); + } + return n->svc_init(); + } + void * svc(void* in) { void* out = n->svc(in); if (out > FF_TAG_MIN) return out; @@ -108,26 +127,6 @@ class EmitterAdapter: public internal_mo_transformer { } } - int svc_init() { - if (this->n->isMultiOutput()) { - ff_monode* mo = reinterpret_cast(this->n); - //mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers - mo->set_running(totalWorkers); - } - - // change the size of the queue to the SquareBoxRight (if present), - // since the distributed-memory communications are all on-demand - svector w; - this->get_out_nodes(w); - assert(w.size()>0); - if (w.size() > localWorkersMap.size()) { - assert(w.size() == localWorkersMap.size()+1); - size_t oldsz; - w[localWorkersMap.size()]->change_inputqueuesize(1, oldsz); - } - return n->svc_init(); - } - void svc_end(){n->svc_end();} int run(bool skip_init=false) { diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 785057fe..4a54096a 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -25,6 +25,9 @@ #ifndef FF_DGROUPS_H #define FF_DGROUPS_H +#include +#include + #include #include #include @@ -32,8 +35,6 @@ #include #include -#include - #include #include @@ -187,9 +188,9 @@ class dGroups { std::string name; std::string address; int port; - int batchSize = 1; - int internalMessageOTF = 10; - int messageOTF = 100; + int batchSize = DEFAULT_BATCH_SIZE; + int internalMessageOTF = DEFAULT_INTERNALMSG_OTF; + int messageOTF = DEFAULT_MESSAGE_OTF; template void load( Archive & ar ){ @@ -470,7 +471,16 @@ class dGroups { static inline int DFF_Init(int& argc, char**& argv){ - std::string configFile, groupName; + struct sigaction s; + memset(&s,0,sizeof(s)); + s.sa_handler=SIG_IGN; + if ( (sigaction(SIGPIPE,&s,NULL) ) == -1 ) { + perror("sigaction"); + return -1; + } + + + std::string configFile, groupName; for(int i = 0; i < argc; i++){ if (strstr(argv[i], "--DFF_Config") != NULL){ diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 2fa55b06..d82e96b9 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -98,14 +98,13 @@ class ff_dreceiver: public ff_monode_t { virtual int handleBatch(int sck){ int requestSize; - switch(readn(sck, reinterpret_cast(&requestSize), sizeof(requestSize))) { case -1: error("Something went wrong in receiving the number of tasks!\n"); case 0: return -1; } // always sending back the acknowledgement if (writen(sck, reinterpret_cast(&ACK), sizeof(ack_t)) < 0){ - if (errno != ECONNRESET || errno != EPIPE) { + if (errno != ECONNRESET && errno != EPIPE) { error("Error sending back ACK to the sender (errno=%d)\n",errno); return -1; } @@ -370,11 +369,10 @@ class ff_dreceiverH : public ff_dreceiver { }; +#if 0 /* ONDEMAND specification */ - - class ff_dreceiverOD: public ff_dreceiver { protected: virtual int handleRequest(int sck) override { @@ -441,5 +439,6 @@ class ff_dreceiverOD: public ff_dreceiver { private: ack_t ACK; }; +#endif // if 0 #endif diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 85c75f0b..832c3d37 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -25,9 +25,6 @@ #ifndef FF_DSENDER_H #define FF_DSENDER_H -#define QUEUEDIM 100 -#define INTERNALQUEUEDIM 10 - #include #include #include @@ -302,11 +299,11 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int batchSize = 1, int messageOTF = 100, int coreid=-1): gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { + ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1): gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector dest_endpoints_, std::string gName = "", int batchSize = 1, int messageOTF = 100, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} + ff_dsender( std::vector dest_endpoints_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} @@ -353,16 +350,6 @@ class ff_dsender: public ff_minode_t { } void svc_end() { - - long totalack = socketsCounters.size()*QUEUEDIM; - long currack = 0; - for(const auto& pair : socketsCounters) - currack += pair.second; - while(curracksockets) close(sck); } @@ -429,13 +416,13 @@ class ff_dsenderH : public ff_dsender { if (sckMax > 0) return sckMax; last_rr_socket_Internal = (last_rr_socket_Internal + 1) % this->internalSockets.size(); - return sockets[last_rr_socket_Internal]; + return internalSockets[last_rr_socket_Internal]; } public: - ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int messageOTF = 100, int internalMessageOTF = 10, int coreid=-1) : ff_dsender(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} - ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = 1, int messageOTF = 100, int internalMessageOTF = 10, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} int handshakeHandler(const int sck, bool isInternal){ if (sendGroupName(sck) < 0) return -1; @@ -447,7 +434,7 @@ class ff_dsenderH : public ff_dsender { if (coreid!=-1) ff_mapThreadToCpu(coreid); - + FD_ZERO(&set); FD_ZERO(&tmpset); @@ -457,7 +444,7 @@ class ff_dsenderH : public ff_dsender { bool isInternal = internalGroupNames.contains(endpoint.groupName); if (isInternal) internalSockets.push_back(sck); else sockets.push_back(sck); - socketsCounters[sck] = isInternal ? INTERNALQUEUEDIM : QUEUEDIM; + socketsCounters[sck] = isInternal ? internalMessageOTF: messageOTF; batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ @@ -496,6 +483,7 @@ class ff_dsenderH : public ff_dsender { } else sck = getMostFilledInternalBufferSck(); + batchBuffers[sck].push(task); return this->GO_ON; @@ -510,7 +498,9 @@ class ff_dsenderH : public ff_dsender { message_t E_O_S(0,0); for(const auto& sck : internalSockets) { batchBuffers[sck].sendEOS(); - while(socketsCounters[sck] == INTERNALQUEUEDIM) waitAckFrom(sck); + + //while(socketsCounters[sck] == internalMessageOTF) waitAckFrom(sck); + FD_CLR(sck, &set); socketsCounters.erase(sck); if (sck == fdmax) { diff --git a/ff/lb.hpp b/ff/lb.hpp index a704064b..25919afa 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -242,6 +242,7 @@ class ff_loadbalancer: public ff_thread { unsigned long ticks=TICKS2WAIT) { unsigned long cnt; if (blocking_out) { + unsigned long r = 0; do { cnt=0; do { @@ -259,6 +260,8 @@ class ff_loadbalancer: public ff_thread { ++cnt; if (cnt == nattempts()) break; } while(1); + + if (++r >= retry) return false; struct timespec tv; timedwait_timeout(tv); @@ -718,12 +721,14 @@ class ff_loadbalancer: public ff_thread { unsigned long retry=((unsigned long)-1), unsigned long ticks=(TICKS2WAIT)) { if (blocking_out) { + unsigned long r=0; _retry: bool empty=workers[id]->get_in_buffer()->empty(); if (workers[id]->put(task)) { FFTRACE(++taskcnt); if (empty) put_done(id); } else { + if (++r >= retry) return false; struct timespec tv; timedwait_timeout(tv); pthread_mutex_lock(prod_m); diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index f838ec99..d6210ec7 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -1661,7 +1661,14 @@ class ff_pipeline: public ff_node { return nodes_list[last]->set_output_buffer(node->get_out_buffer()); } - + inline int ondemand_buffer() const { + int last = static_cast(nodes_list.size())-1; + + svector w; + nodes_list[last]->get_out_nodes(w); + return w[0]->ondemand_buffer(); // NOTE: we suppose that all others are the same !!!!! + } + private: bool has_input_channel; // for accelerator bool node_cleanup; diff --git a/ff/ubuffer.hpp b/ff/ubuffer.hpp index 34baef37..80700c94 100644 --- a/ff/ubuffer.hpp +++ b/ff/ubuffer.hpp @@ -471,8 +471,9 @@ class uSWSR_Ptr_Buffer { /** * It changes the size of the queue WITHOUT reallocating * the internal buffers. It should be used mainly for - * reducing the size of the queue or to restore it after - * is has been previously reduced. + * reducing the size of the queue or to restore after + * it has been previously reduced. + * NOTE: it forces the queue to behave as bounded queue! * * WARNING: this is very a dangerous operation if executed * while the queue is being used; if wrongly used, it @@ -484,7 +485,8 @@ class uSWSR_Ptr_Buffer { size_t tmp=buf_w->changesize(newsz); assert(size == tmp); size = newsz; - pool.changesize(newsz); + pool.changesize(newsz); + fixedsize=true; return tmp; } diff --git a/tests/distributed/test_parametricPerfH.cpp b/tests/distributed/test_parametricPerfH.cpp index 90d39523..affa0abb 100644 --- a/tests/distributed/test_parametricPerfH.cpp +++ b/tests/distributed/test_parametricPerfH.cpp @@ -136,21 +136,22 @@ struct MiNode : ff::ff_minode_t{ MiNode(int execTime, bool checkdata=false): execTime(execTime),checkdata(checkdata) {} ExcType* svc(ExcType* in){ - if (execTime) active_delay(this->execTime); - ++processedItems; - if (checkdata) { - myassert(in->C[0] == 'c'); - if (in->clen>10) - myassert(in->C[10] == 'i'); - if (in->clen>100) - myassert(in->C[100] == 'a'); - if (in->clen>500) - myassert(in->C[500] == 'o'); - ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; - } - myassert(in->C[in->clen-1] == 'F'); - delete in; - return this->GO_ON; + if (execTime) active_delay(this->execTime); + + ++processedItems; + if (checkdata) { + myassert(in->C[0] == 'c'); + if (in->clen>10) + myassert(in->C[10] == 'i'); + if (in->clen>100) + myassert(in->C[100] == 'a'); + if (in->clen>500) + myassert(in->C[500] == 'o'); + ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; + } + myassert(in->C[in->clen-1] == 'F'); + delete in; + return this->GO_ON; } void svc_end(){ @@ -166,8 +167,8 @@ int main(int argc, char*argv[]){ return -1; } - if (argc < 8){ - std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_dx #nwXp" << std::endl; + if (argc < 7){ + std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_dx #nwXpDx" << std::endl; return -1; } bool check = false; @@ -176,8 +177,7 @@ int main(int argc, char*argv[]){ int execTimeSource = atoi(argv[3]); int execTimeSink = atoi(argv[4]); int numProcDx = atoi(argv[5]); - int numWorkerXProcessSx = atoi(argv[6]); - int numWorkerXProcessDx = atoi(argv[7]); + int numWorkerXProcessDx = atoi(argv[6]); char* p=nullptr; if ((p=getenv("CHECK_DATA"))!=nullptr) check=true; printf("chackdata = %s\n", p); @@ -192,7 +192,7 @@ int main(int argc, char*argv[]){ for(int i = 0; i < ((numProcDx+1)*numWorkerXProcessDx); i++) dxWorkers.push_back(new MiNode(execTimeSink, check)); - a2a.add_firstset(sxWorkers, 1); //ondemand on!! + a2a.add_firstset(sxWorkers, 10); //ondemand on!! a2a.add_secondset(dxWorkers); auto master = a2a.createGroup("M"); diff --git a/tests/distributed/test_parametricPerfH.json b/tests/distributed/test_parametricPerfH.json index 64fc519c..01111678 100644 --- a/tests/distributed/test_parametricPerfH.json +++ b/tests/distributed/test_parametricPerfH.json @@ -2,7 +2,8 @@ "groups" : [ { "endpoint" : "localhost:8000", - "name" : "M" + "name" : "M", + "internalMessageOTF" : 10 }, { "endpoint" : "localhost:8002", From f35af70dcc1f4236fd956a8c5e6296f6bab9669b Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 2 Apr 2022 21:18:09 +0200 Subject: [PATCH 141/202] fixed a problem with the connection shutdown protocol --- ff/distributed/ff_dsender.hpp | 3 ++- tests/distributed/test_parametricPerfH.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 832c3d37..384bb75c 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -351,7 +351,8 @@ class ff_dsender: public ff_minode_t { void svc_end() { // close the socket not matter if local or remote - for (auto& sck : this->sockets) close(sck); + for (auto& sck : this->sockets) + shutdown(sck, SHUT_WR); //close(sck); } message_t *svc(message_t* task) { diff --git a/tests/distributed/test_parametricPerfH.json b/tests/distributed/test_parametricPerfH.json index 01111678..88b941eb 100644 --- a/tests/distributed/test_parametricPerfH.json +++ b/tests/distributed/test_parametricPerfH.json @@ -3,7 +3,7 @@ { "endpoint" : "localhost:8000", "name" : "M", - "internalMessageOTF" : 10 + "internalMessageOTF" : 1 }, { "endpoint" : "localhost:8002", From 0abd80d5e4c7d54e01fad4729fecd61e18e1bfd0 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 4 Apr 2022 05:54:15 +0200 Subject: [PATCH 142/202] bug fix in the termination protocol --- ff/distributed/ff_dreceiver.hpp | 89 +++--------------- ff/distributed/ff_dsender.hpp | 104 ++++++++++++++++------ tests/distributed/test_parametricPerf.cpp | 22 +++-- 3 files changed, 100 insertions(+), 115 deletions(-) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index d82e96b9..92926f65 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -99,8 +99,12 @@ class ff_dreceiver: public ff_monode_t { virtual int handleBatch(int sck){ int requestSize; switch(readn(sck, reinterpret_cast(&requestSize), sizeof(requestSize))) { - case -1: error("Something went wrong in receiving the number of tasks!\n"); - case 0: return -1; + case -1: { + perror("readn"); + error("Something went wrong in receiving the number of tasks!\n"); + return -1; + } break; + case 0: return -1; } // always sending back the acknowledgement if (writen(sck, reinterpret_cast(&ACK), sizeof(ack_t)) < 0){ @@ -214,11 +218,10 @@ class ff_dreceiver: public ff_monode_t { } void svc_end() { - close(this->listen_sck); - - #ifdef LOCAL - unlink(this->acceptAddr.address.c_str()); - #endif + close(this->listen_sck); +#ifdef LOCAL + unlink(this->acceptAddr.address.c_str()); +#endif } /* Here i should not care of input type nor input data since they come from a socket listener. @@ -369,76 +372,4 @@ class ff_dreceiverH : public ff_dreceiver { }; -#if 0 -/* - ONDEMAND specification -*/ -class ff_dreceiverOD: public ff_dreceiver { -protected: - virtual int handleRequest(int sck) override { - int sender; - int chid; - size_t sz; - struct iovec iov[3]; - iov[0].iov_base = &sender; - iov[0].iov_len = sizeof(sender); - iov[1].iov_base = &chid; - iov[1].iov_len = sizeof(chid); - iov[2].iov_base = &sz; - iov[2].iov_len = sizeof(sz); - - switch (readvn(sck, iov, 3)) { - case -1: error("Error reading from socket\n"); // fatal error - case 0: return -1; // connection close - } - - // convert values to host byte order - sender = ntohl(sender); - chid = ntohl(chid); - sz = be64toh(sz); - - if (sz > 0){ - char* buff = new char [sz]; - assert(buff); - if(readn(sck, buff, sz) < 0){ - error("Error reading from socket\n"); - delete [] buff; - return -1; - } - message_t* out = new message_t(buff, sz, true); - assert(out); - out->sender = sender; - out->chid = chid; - - if (chid != -1) - ff_send_out_to(out, this->routingTable[chid]); // assume the routing table is consistent WARNING!!! - else - ff_send_out(out); - - - if (writen(sck, reinterpret_cast(&ACK),sizeof(ack_t)) < 0){ - if (errno != ECONNRESET || errno != EPIPE) { - error("Error sending back ACK to the sender (errno=%d)\n",errno); - return -1; - } - } - - - return 0; - } - - registerEOS(sck); - - return -1; - } - -public: - ff_dreceiverOD(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) - : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid) {} - -private: - ack_t ACK; -}; -#endif // if 0 - #endif diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 384bb75c..c49d5019 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -100,7 +100,7 @@ class ff_dsender: public ff_minode_t { /*for (const auto& p : m) std::cout << p.first << " - " << p.second << std::endl; */ - ff::cout << "Receiving routing table (" << sz << " bytes)" << ff::endl; + //ff::cout << "Receiving routing table (" << sz << " bytes)" << ff::endl; return 0; } @@ -349,12 +349,6 @@ class ff_dsender: public ff_minode_t { return 0; } - void svc_end() { - // close the socket not matter if local or remote - for (auto& sck : this->sockets) - shutdown(sck, SHUT_WR); //close(sck); - } - message_t *svc(message_t* task) { int sck; if (task->chid != -1) @@ -367,11 +361,40 @@ class ff_dsender: public ff_minode_t { return this->GO_ON; } - virtual void eosnotify(ssize_t id) { - if (++neos >= this->get_num_inchannels()) - for(const auto& sck : sockets) batchBuffers[sck].sendEOS(); + void eosnotify(ssize_t id) { + if (++neos >= this->get_num_inchannels()) { + // all input EOS received, now sending the EOS to all connections + for(const auto& sck : sockets) { + batchBuffers[sck].sendEOS(); + shutdown(sck, SHUT_WR); + } + } } + void svc_end() { + // here we wait all acks from all connections + size_t totalack = sockets.size()*messageOTF; + size_t currentack = 0; + for(const auto& [_, counter] : socketsCounters) + currentack += counter; + + ack_t a; + while(currentack fdmax && FD_ISSET(sck_, &set)) fdmax = sck_; - } - - } - //++neos; // count anyway a new EOS received! - - } - if (++neos >= this->get_num_inchannels()) - for(const auto& sck : sockets) batchBuffers[sck].sendEOS(); - } + shutdown(sck, SHUT_WR); + } + } + if (++neos >= this->get_num_inchannels()) { + // all input EOS received, now sending the EOS to all + // others connections + for(const auto& sck : sockets) { + batchBuffers[sck].sendEOS(); + shutdown(sck, SHUT_WR); + } + } + } + + void svc_end() { + // here we wait all acks from all connections + size_t totalack = internalSockets.size()*internalMessageOTF; + totalack += sockets.size()*messageOTF; + size_t currentack = 0; + for(const auto& [_, counter] : socketsCounters) + currentack += counter; + + ack_t a; + while(currentack{ myassert(in->C[100] == 'a'); if (in->clen>500) myassert(in->C[500] == 'o'); - ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; + //ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; + } + if (in->C[in->clen-1] != 'F') { + ff::cout << "ERROR: " << in->C[in->clen-1] << " != 'F'\n"; + myassert(in->C[in->clen-1] == 'F'); } - myassert(in->C[in->clen-1] == 'F'); delete in; return this->GO_ON; } @@ -167,7 +170,7 @@ int main(int argc, char*argv[]){ } if (argc < 9){ - std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_sx #np_dx #nwXp" << std::endl; + std::cout << "Usage: " << argv[0] << " #items #byteXitem #execTimeSource #execTimeSink #np_sx #np_dx #nwXpsx #nwXpdx" << std::endl; return -1; } bool check = false; @@ -210,10 +213,11 @@ int main(int argc, char*argv[]){ g << dxWorkers[j]; } } - + auto t0=getusec(); if (a2a.run_and_wait_end()<0) { error("running mainPipe\n"); return -1; - } + } + ff::cout << "Time (ms) = " << (getusec()-t0)/1000.0 << "\n"; return 0; } From d1eb0853647973c673fa956e15df1fbb8c733419 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 4 Apr 2022 05:58:43 +0200 Subject: [PATCH 143/202] minor modifications to the test_parametricPerf test --- tests/distributed/test_parametricPerf.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 3d595a67..4527633b 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -14,13 +14,17 @@ * */ +// running the tests with limited buffer capacity +#define FF_BOUNDED_BUFFER +#define DEFAULT_BUFFER_CAPACITY 128 + #include #include #include #include -#define MANUAL_SERIALIZATION 1 +//#define MANUAL_SERIALIZATION 1 // ------------------------------------------------------ std::mutex mtx; // used only for pretty printing From 0e600431d40e2db34d039d06ef211f41fa6b1b90 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 6 Apr 2022 16:38:26 +0200 Subject: [PATCH 144/202] Fixed some minor bug of dgroup implementation --- ff/distributed/ff_dgroup.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index b4897d97..2e82a6ae 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -185,8 +185,7 @@ class dGroup : public ff::ff_farm { return new ff_Pipe(n); return n; }); - - innerA2A->add_firstset(firstSet, reinterpret_cast(ir.parentBB)->ondemand_buffer()); // note the ondemand!! + innerA2A->add_firstset(firstSet, 1 /*reinterpret_cast(ir.parentBB)->ondemand_buffer()*/); // note the ondemand!! int outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? 0 : f.second.first.size());}); From 491225f9625278786d2b55b1f4a6fc232d8c2570 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 7 Apr 2022 09:12:52 +0200 Subject: [PATCH 145/202] fixed a problem in the svc_end of the sender thread --- ff/distributed/ff_dreceiver.hpp | 10 ++++----- ff/distributed/ff_dsender.hpp | 36 ++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 92926f65..075d5b3d 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -116,7 +116,7 @@ class ff_dreceiver: public ff_monode_t { requestSize = ntohl(requestSize); for(int i = 0; i < requestSize; i++) - handleRequest(sck); + if (handleRequest(sck)<0) return -1; return 0; } @@ -134,8 +134,8 @@ class ff_dreceiver: public ff_monode_t { iov[2].iov_len = sizeof(sz); switch (readvn(sck, iov, 3)) { - case -1: error("Error reading from socket\n"); // fatal error - case 0: return -1; // connection close + case -1: error("Error reading from socket errno=%d\n",errno); // fatal error + case 0: return -1; // connection close } // convert values to host byte order @@ -160,9 +160,7 @@ class ff_dreceiver: public ff_monode_t { return 0; } - registerEOS(sck); - return -1; } @@ -289,7 +287,7 @@ class ff_dreceiver: public ff_monode_t { } } } - + return this->EOS; } diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index c49d5019..41c2ce93 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -379,16 +379,22 @@ class ff_dsender: public ff_minode_t { currentack += counter; ack_t a; - while(currentackfirst; + auto& counter = scit->second; + + switch(recvnnb(sck, (char*)&a, sizeof(a))) { case 0: case -1: + if (errno==EWOULDBLOCK) { ++scit; continue; } currentack += (messageOTF-counter); + socketsCounters.erase(scit++); break; default: { currentack++; counter++; + ++scit; } } } @@ -420,7 +426,7 @@ class ff_dsenderH : public ff_dsender { decltype(internalSockets)::iterator it; do - sck = waitAckFromAny(); + sck = waitAckFromAny(); // FIX: error management! while ((it = std::find(internalSockets.begin(), internalSockets.end(), sck)) != internalSockets.end()); last_rr_socket_Internal = it - internalSockets.begin(); @@ -540,26 +546,36 @@ class ff_dsenderH : public ff_dsender { size_t totalack = internalSockets.size()*internalMessageOTF; totalack += sockets.size()*messageOTF; size_t currentack = 0; - for(const auto& [_, counter] : socketsCounters) + for(const auto& [sck, counter] : socketsCounters) { currentack += counter; - + } + ack_t a; - while(currentackfirst; + auto& counter = scit->second; + + switch(recvnnb(sck, (char*)&a, sizeof(a))) { case 0: case -1: { + if (errno == EWOULDBLOCK) { + ++scit; + continue; + } decltype(internalSockets)::iterator it; it = std::find(internalSockets.begin(), internalSockets.end(), sck); if (it != internalSockets.end()) currentack += (internalMessageOTF-counter); else currentack += (messageOTF-counter); + socketsCounters.erase(scit++); } break; default: { currentack++; counter++; - } + ++scit; + } } } } From ec646eb00b6a0c420c2bf5213e755d31fc46ddd6 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Thu, 7 Apr 2022 13:58:40 +0200 Subject: [PATCH 146/202] Added wordcount in horizontal split configuration --- ff/combine.hpp | 14 +- ff/distributed/ff_dgroup.hpp | 17 +- ff/node.hpp | 9 + tests/distributed/dwordcount/dwordcountH.cpp | 411 ++++++++++++++++++ tests/distributed/dwordcount/dwordcountH.json | 12 + 5 files changed, 454 insertions(+), 9 deletions(-) create mode 100644 tests/distributed/dwordcount/dwordcountH.cpp create mode 100644 tests/distributed/dwordcount/dwordcountH.json diff --git a/ff/combine.hpp b/ff/combine.hpp index b0140fae..219c74ed 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -387,6 +387,14 @@ class ff_comb: public ff_minode { ff_node::skipfirstpop(sk); } +#ifdef DFF_ENABLED + void skipallpop(bool sk) { + getFirst()->skipallpop(sk); + ff_node::skipallpop(sk); + } +#endif + + bool put(void * ptr) { return ff_node::put(ptr); } @@ -510,7 +518,11 @@ class ff_comb: public ff_minode { if (comp_nodes[0]->isComp()) ret = comp_nodes[0]->svc(task); else { - if (task || comp_nodes[0]->skipfirstpop()) { +#ifdef DFF_ENABLED + if (task || comp_nodes[0]->skipfirstpop() || comp_nodes[0]->skipallpop()) { +#else + if (task || comp_nodes[0]->skipfirstpop()) +#endif r1= comp_nodes[0]->svc(task); if (!(r1 == FF_GO_ON || r1 == FF_GO_OUT || r1 == FF_EOS_NOFREEZE)) { comp_nodes[0]->ff_send_out(r1); diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 2e82a6ae..cd535fb0 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -151,18 +151,19 @@ class dGroup : public ff::ff_farm { if (isSeq(child)) if (ir.isSource){ ff_node* wrapped = new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers); - wrapped->skipallpop(true); + if (ir.hasReceiver) + wrapped->skipallpop(true); firstSet.push_back(wrapped); } else { firstSet.push_back(new ff_comb(new WrapperIN(new ForwarderNode(child->getDeserializationFunction()), 1, true), new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers), true, true)); } else { - ff::svector inputs; child->get_in_nodes(inputs); - for(ff_node* input : inputs){ - if (ir.isSource) - input->skipallpop(true); - else { + if (ir.isSource){ + if (ir.hasReceiver) child->skipallpop(true); + } else { + ff::svector inputs; child->get_in_nodes(inputs); + for(ff_node* input : inputs){ ff_node* inputParent = getBB(child, input); if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); // cleanup??? remove_fromcleanuplist?? } @@ -178,8 +179,8 @@ class dGroup : public ff::ff_farm { } // add the Square Box Left, just if we have a receiver! if (ir.hasReceiver) - firstSet.push_back(new SquareBoxLeft(localRightWorkers)); // ondemand?? - + firstSet.push_back(new SquareBoxLeft(localRightWorkers)); + std::transform(firstSet.begin(), firstSet.end(), firstSet.begin(), [](ff_node* n) -> ff_node* { if (!n->isPipe()) return new ff_Pipe(n); diff --git a/ff/node.hpp b/ff/node.hpp index 9be69d2f..bf878cab 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -685,6 +685,11 @@ class ff_node { * Example: \ref l1_ff_nodes_graph.cpp */ bool skipfirstpop() const { return skip1pop; } + +#ifdef DFF_ENABLED + bool skipallpop() {return _skipallpop;} +#endif + /** * \brief Creates the input channel @@ -1417,7 +1422,11 @@ class ff_node { } gettimeofday(&filter->wtstart,NULL); do { +#ifdef DFF_ENABLED + if (!filter->skipallpop() && inpresent){ +#else if (inpresent) { +#endif if (!skipfirstpop) pop(&task); else skipfirstpop=false; if ((task == FF_EOS) || (task == FF_EOSW) || diff --git a/tests/distributed/dwordcount/dwordcountH.cpp b/tests/distributed/dwordcount/dwordcountH.cpp new file mode 100644 index 00000000..10b699e1 --- /dev/null +++ b/tests/distributed/dwordcount/dwordcountH.cpp @@ -0,0 +1,411 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + +/* + * The Source produces a continuous stream of lines of a text file. + * The Splitter tokenizes each line producing a new output item for each + * word extracted. The Counter receives single words from the line + * splitter and counts how many occurrences of the same word appeared + * on the stream until that moment. The Sink node receives every result + * produced by the word counter and counts the total number of words. + * + * One possible FastFlow graph is the following: + * + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * | --> Counter --> Sink + * Source-->Splitter -->| + * + * /<---- pipe1 ---->/ /<--- pipe2 --->/ + * /<----------------- a2a ------------------>/ + * + * G1: pipe1 + * G2: pipe2 + * + */ + +#define FF_BOUNDED_BUFFER +//#define MAKE_VALGRIND_HAPPY +//#define MANUAL_SERIALIZATION +#define DEFAULT_BUFFER_CAPACITY 2048 +#define BYKEY true + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ff; + +const size_t qlen = DEFAULT_BUFFER_CAPACITY; +const int MAXLINE=128; // character per line (CPL), a typically value is 80 CPL +const int MAXWORD=32; + +struct tuple_t { + char text_line[MAXLINE]; // parsed line + size_t key; // line number + uint64_t id; // id set to zero + uint64_t ts; // timestamp +}; + +struct result_t { + char key[MAXWORD]; // key word + uint64_t id; // indicates the current number of occurrences of the word + uint64_t ts; // timestamp + + template + void serialize(Archive & archive) { + archive(key,id,ts); + } + +}; + +#if defined(MANUAL_SERIALIZATION) +template +void serialize(Buffer&b, result_t* input){ + b = {reinterpret_cast(input), sizeof(result_t)}; +} + +template +void deserialize(const Buffer&b, result_t*& strPtr){ + strPtr = reinterpret_cast(b.first); +} +#endif + +std::vector dataset; // contains all the input tuples in memory +std::atomic total_lines=0; // total number of lines processed by the system +std::atomic total_bytes=0; // total number of bytes processed by the system + + +std::atomic global_words = 0; +std::atomic global_unique = 0; + + +/// application run time (source generates the stream for app_run_time seconds, then sends out EOS) +unsigned long app_run_time = 15; // time in seconds + +static inline unsigned long current_time_usecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000L + (t.tv_nsec / 1000); +} +static inline unsigned long current_time_nsecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000000L + t.tv_nsec; +} + +struct Source: ff_node_t { + size_t next_tuple_idx = 0; // index of the next tuple to be sent + int generations = 0; // counts the times the file is generated + long generated_tuples = 0; // tuples counter + long generated_bytes = 0; // bytes counter + + // time variables + unsigned long app_start_time; // application start time + unsigned long current_time; + + Source(const unsigned long _app_start_time): + app_start_time(_app_start_time),current_time(_app_start_time) { + } + + tuple_t* svc(tuple_t*) { + while(1) { + tuple_t* t = new tuple_t; + assert(t); + + if (generated_tuples > 0) current_time = current_time_nsecs(); + if (next_tuple_idx == 0) generations++; // file generations counter + generated_tuples++; // tuples counter + // put a timestamp and send the tuple + *t = dataset.at(next_tuple_idx); + generated_bytes += sizeof(tuple_t); + t->ts = current_time - app_start_time; + ff_send_out(t); + ++next_tuple_idx; + next_tuple_idx %= dataset.size(); + // EOS reached + if (current_time - app_start_time >= (app_run_time*1000000000L) && next_tuple_idx == 0) + break; + } + total_lines.fetch_add(generated_tuples); + total_bytes.fetch_add(generated_bytes); + return EOS; + } +}; +struct Splitter: ff_monode_t { + int svc_init() { + noutch=get_num_outchannels(); // number of output channels + return 0; + } + + result_t* svc(tuple_t* in) { + char *tmpstr; + char *token = strtok_r(in->text_line, " ", &tmpstr); + while (token) { +#if defined(BYKEY) + int ch = std::hash()(std::string(token)) % noutch; +#else + int ch = ++idx % noutch; +#endif + result_t* r = new result_t; + assert(r); +#if defined(MAKE_VALGRIND_HAPPY) + bzero(r->key, MAXWORD); +#endif + strncpy(r->key, token, MAXWORD-1); + r->key[MAXWORD-1]='\0'; + r->ts = in->ts; + r->id = in->id; + + ff_send_out_to(r, ch); + token = strtok_r(NULL, " ", &tmpstr); + } + delete in; + return GO_ON; + } + long noutch=0; + long idx=-1; +}; + +struct Counter: ff_minode_t { + result_t* svc(result_t* in) { + ++M[std::string(in->key)]; + // number of occurrences of the word up to now + in->id = M[std::string(in->key)]; + return in; + } + void svc_end(){ + global_unique += M.size(); + } + std::map M; +}; + +struct Sink: ff_node_t { + result_t* svc(result_t* in) { + ++words; + delete in; + return GO_ON; + } + + void svc_end(){ + global_words += words; + } + + size_t words=0; // total number of words received +}; + +/** + * @brief Parse the input file and create all the tuples + * + * The created tuples are maintained in memory. The source node will generate the stream by + * reading all the tuples from main memory. + * + * @param file_path the path of the input dataset file + */ +int parse_dataset_and_create_tuples(const std::string& file_path) { + std::ifstream file(file_path); + if (file.is_open()) { + size_t all_records = 0; // counter of all records (dataset line) read + std::string line; + while (getline(file, line)) { + // process file line + if (!line.empty()) { + if (line.length() > MAXLINE) { + std::cerr << "ERROR INCREASE MAXLINE\n"; + exit(EXIT_FAILURE); + } + tuple_t t; + strncpy(t.text_line, line.c_str(), MAXLINE-1); + t.text_line[MAXLINE-1]='\0'; + t.key = all_records; + t.id = 0; + t.ts = 0; + all_records++; + dataset.push_back(t); + } + } + file.close(); + } else { + std::cerr << "Unable to open the file " << file_path << "\n"; + return -1; + } + return 0; +} + + +int main(int argc, char* argv[]) { + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + /// parse arguments from command line + std::string file_path(""); + size_t source_par_deg = 0; + size_t sink_par_deg = 0; + + if (argc >= 3 || argc == 1) { + int option = 0; + while ((option = getopt(argc, argv, "f:p:t:")) != -1) { + switch (option) { + case 'f': file_path=std::string(optarg); break; + case 'p': { + std::vector par_degs; + std::string pars(optarg); + std::stringstream ss(pars); + for (size_t i; ss >> i;) { + par_degs.push_back(i); + if (ss.peek() == ',') + ss.ignore(); + } + if (par_degs.size() != 2) { + std::cerr << "Error in parsing the input arguments -p, the format is n,m\n"; + return -1; + } else { + source_par_deg = par_degs[0]; + sink_par_deg = par_degs[1]; + } + } break; + case 't': { + long t = std::stol(optarg); + if (t<=0 || t > 100) { + std::cerr << "Wrong value for the '-t' option, it should be in the range [1,100]\n"; + return -1; + } + app_run_time = t; + } break; + default: { + std::cerr << "Error in parsing the input arguments\n"; + return -1; + } + } + } + } else { + std::cerr << "Parameters: -p -f \n"; + return -1; + } + if (file_path.length()==0) { + std::cerr << "The file path is empty, please use option -f \n"; + return -1; + } + if (source_par_deg == 0 || sink_par_deg==0) { + std::cerr << "Wrong values for the parallelism degree, please use option -p \n"; + return -1; + } + + //if (DFF_getMyGroup() == "G1") { + /// data pre-processing + if (parse_dataset_and_create_tuples(file_path)< 0) + return -1; + + ff::cout << "\n\n"; + ff::cout << "Executing WordCount with parameters:" << endl; + ff::cout << " * source/splitter : " << source_par_deg << endl; + ff::cout << " * counter/sink : " << sink_par_deg << endl; + ff::cout << " * running time : " << app_run_time << " (s)\n"; + //} + + /// application starting time + unsigned long app_start_time = current_time_nsecs(); + + std::vector L; // left and right workers of the A2A + std::vector R; + + + ff_a2a a2a(false, qlen, qlen, true); + + auto G1 = a2a.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + + for (size_t i=0;isetMappingList("0,1,2,3,4,5,6,7,8,9,10,11, 24,25,26,27,28,29,30,31,32,33,34,35"); + } else { + threadMapper::instance()->setMappingList("12,13,14,15,16,17,18,19,20,21,22,23, 36,37,38,39,40,41,42,43,44,45,46,47"); + } +#endif + + ff::cout << "Starting \n\n"; + /// evaluate topology execution time + volatile unsigned long start_time_main_usecs = current_time_usecs(); + if (a2a.run_and_wait_end()<0) { + error("running pipeMain\n"); + return -1; + } + volatile unsigned long end_time_main_usecs = current_time_usecs(); + ff::cout << "Exiting" << endl; + double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); + ff::cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; + + + ff::cout << "total_lines sent : " << total_lines << "\n"; + ff::cout << "total_bytes sent : " << std::setprecision(3) << total_bytes/1048576.0 << "(MB)\n"; + //double throughput = total_lines / elapsed_time_seconds; + //double mbs = (double)((total_bytes / 1048576) / elapsed_time_seconds); + //std::cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; + + + ff::cout << "words : " << global_words << "\n"; + ff::cout << "unique : " << global_unique<< "\n"; + + + return 0; +} diff --git a/tests/distributed/dwordcount/dwordcountH.json b/tests/distributed/dwordcount/dwordcountH.json new file mode 100644 index 00000000..79e1b1af --- /dev/null +++ b/tests/distributed/dwordcount/dwordcountH.json @@ -0,0 +1,12 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "G1" + }, + { + "name" : "G2", + "endpoint": "localhost:8005" + } + ] +} From 4847387310b832fd24e230b0f4f399c32e3f3cfc Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 8 Apr 2022 11:16:08 +0200 Subject: [PATCH 147/202] Fixed skipallpop for all BBs. Fixed dwordcountH test --- ff/all2all.hpp | 8 ++++ ff/farm.hpp | 8 ++++ ff/gt.hpp | 11 ++++- ff/lb.hpp | 8 +++- ff/multinode.hpp | 9 ++++- ff/pipeline.hpp | 6 +++ tests/distributed/dwordcount/dwordcountH.cpp | 42 +++++++++++++------- 7 files changed, 72 insertions(+), 20 deletions(-) diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 7cfbe2b5..23e227d4 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -395,6 +395,14 @@ class ff_a2a: public ff_node { skip1pop=sk; } +#ifdef DFF_ENABLED + void skipallpop(bool sk) { + for(size_t i=0;iskipallpop(sk); + ff_node::skipallpop(sk); + } +#endif + void blocking_mode(bool blk=true) { blocking_in = blocking_out = blk; } diff --git a/ff/farm.hpp b/ff/farm.hpp index d2379b05..ae7ebfcc 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -672,6 +672,14 @@ class ff_farm: public ff_node { skip1pop=sk; } + +#ifdef DFF_ENABLED + void skipallpop(bool sk) { + lb->skipallpop(sk); + ff_node::skipallpop(sk); + } +#endif + // consumer virtual inline bool init_input_blocking(pthread_mutex_t *&m, diff --git a/ff/gt.hpp b/ff/gt.hpp index 548c2339..3ff0667b 100644 --- a/ff/gt.hpp +++ b/ff/gt.hpp @@ -486,6 +486,10 @@ class ff_gatherer: public ff_thread { */ void skipfirstpop(bool sk=true) { skip1pop=sk; } +#ifdef DFF_ENABLED + void skipallpop(bool sk = true) { _skipallpop = sk;} +#endif + /** * \brief Gets the ouput buffer * @@ -567,7 +571,7 @@ class ff_gatherer: public ff_thread { // In this case we want to call notifyeos only when we have received EOS from all // input channel. bool notify_each_eos = filter ? (filter->neos==1): false; - + // TODO: inserire skipallpop! gettimeofday(&wtstart,NULL); do { task = NULL; @@ -870,8 +874,11 @@ class ff_gatherer: public ff_thread { ff_node * filter; svector workers; svector offline; - FFBUFFER * buffer; + FFBUFFER * buffer; bool skip1pop; +#ifdef DFF_ENABLED + bool _skipallpop; +#endif bool frominput; int (*ag_callback)(void *,void **, void*); void * ag_callback_arg; diff --git a/ff/lb.hpp b/ff/lb.hpp index 25919afa..2c362144 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -660,6 +660,10 @@ class ff_loadbalancer: public ff_thread { workers[i]->skipfirstpop(false); } +#ifdef DFF_ENABLED + void skipallpop(bool sk) { _skipallpop = sk; } +#endif + void blocking_mode(bool blk=true) { blocking_in = blocking_out = blk; @@ -903,7 +907,7 @@ class ff_loadbalancer: public ff_thread { do { #ifdef DFF_ENABLED - if (!skipallpop && inpresent){ + if (!_skipallpop && inpresent){ #else if (inpresent) { #endif @@ -1489,7 +1493,7 @@ class ff_loadbalancer: public ff_thread { bool blocking_out; #ifdef DFF_ENABLED - bool skipallpop = false; + bool _skipallpop = false; #endif #if defined(TRACE_FASTFLOW) diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 7b2e147c..d69489de 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -314,6 +314,13 @@ class ff_minode: public ff_node { ff_node::skipfirstpop(sk); } +#ifdef DFF_ENABLED + inline void skipallpop(bool sk) { + gt->skipallpop(sk); + ff_node::skipallpop(sk); + } +#endif + /** * \brief run * @@ -657,7 +664,7 @@ class ff_monode: public ff_node { #ifdef DFF_ENABLED inline void skipallpop(bool sk) { - lb->skipallpop = sk; + lb->skipallpop(sk); ff_node::skipallpop(sk); } #endif diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index d6210ec7..11f3eb05 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -1143,6 +1143,12 @@ class ff_pipeline: public ff_node { nodes_list[i]->skipfirstpop(false); } +#ifdef DFF_ENABLED + void skipallpop(bool sk) { + get_node(0)->skipallpop(sk); + } +#endif + /* WARNING: these methods must be called before the run() method */ void blocking_mode(bool blk=true) { diff --git a/tests/distributed/dwordcount/dwordcountH.cpp b/tests/distributed/dwordcount/dwordcountH.cpp index 10b699e1..b755f2b7 100644 --- a/tests/distributed/dwordcount/dwordcountH.cpp +++ b/tests/distributed/dwordcount/dwordcountH.cpp @@ -41,7 +41,7 @@ * | --> Counter --> Sink * Source-->Splitter -->| * - * /<---- pipe1 ---->/ /<--- pipe2 --->/ + * /<---- combine ---->/ /<--- combine --->/ * /<----------------- a2a ------------------>/ * * G1: pipe1 @@ -347,29 +347,41 @@ int main(int argc, char* argv[]) { auto G1 = a2a.createGroup("G1"); auto G2 = a2a.createGroup("G2"); - for (size_t i=0;iadd_stage(new Source(app_start_time)); + child->add_stage(new Splitter); + //auto* combine = new ff_comb(new Source(app_start_time), new Splitter, true, true); + L.push_back(child); + G1 << child; } for (size_t i=0;iadd_stage(new Counter); + child->add_stage(new Sink); + //auto* combine = new ff_comb(new Counter, new Sink, true, true); + R.push_back(child); + G1 << child; } for (size_t i=0;iadd_stage(new Source(app_start_time)); + child->add_stage(new Splitter); + //auto* combine = new ff_comb(new Source(app_start_time), new Splitter, true, true); + L.push_back(child); + G2 << child; } for (size_t i=0;iadd_stage(new Counter); + child->add_stage(new Sink); + //auto* combine = new ff_comb(new Counter, new Sink, true, true); + R.push_back(child); + G2 << child; } a2a.add_firstset(L, 0, true); @@ -382,7 +394,7 @@ int main(int argc, char* argv[]) { threadMapper::instance()->setMappingList("12,13,14,15,16,17,18,19,20,21,22,23, 36,37,38,39,40,41,42,43,44,45,46,47"); } #endif - + ff::cout << "Starting \n\n"; /// evaluate topology execution time volatile unsigned long start_time_main_usecs = current_time_usecs(); From d0b35b4cccbc725346adbf859d96c690f86b79c8 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Fri, 8 Apr 2022 14:32:13 +0200 Subject: [PATCH 148/202] Fixed issue related to EOS in ff_dsender --- ff/distributed/ff_dsender.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 41c2ce93..bd8e5e66 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -411,6 +411,7 @@ class ff_dsenderH : public ff_dsender { int last_rr_socket_Internal = -1; std::set internalGroupNames; int internalMessageOTF; + bool squareBoxEOS = false; int getNextReadyInternal(){ for(size_t i = 0; i < this->internalSockets.size(); i++){ @@ -483,6 +484,7 @@ class ff_dsenderH : public ff_dsender { } if (writevn(sck, v, size) < 0){ + perror("Writevn: "); error("Error sending the iovector inside the callback!\n"); return false; } @@ -525,7 +527,8 @@ class ff_dsenderH : public ff_dsender { void eosnotify(ssize_t id) { if (id == (ssize_t)(this->get_num_inchannels() - 1)){ // send the EOS to all the internal connections - message_t E_O_S(0,0); + if (squareBoxEOS) return; + squareBoxEOS = true; for(const auto& sck : internalSockets) { batchBuffers[sck].sendEOS(); shutdown(sck, SHUT_WR); From b1b571770e0d84cbdd0dc0af726fc3d26c6a3a0e Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Mon, 11 Apr 2022 16:20:28 +0200 Subject: [PATCH 149/202] Fixed bugs introduced in refactoring combine.hpp. Fixed bugs on ff_dgroup related to ondemand --- ff/combine.hpp | 2 +- ff/distributed/ff_dgroup.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ff/combine.hpp b/ff/combine.hpp index 219c74ed..4420e146 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -521,7 +521,7 @@ class ff_comb: public ff_minode { #ifdef DFF_ENABLED if (task || comp_nodes[0]->skipfirstpop() || comp_nodes[0]->skipallpop()) { #else - if (task || comp_nodes[0]->skipfirstpop()) + if (task || comp_nodes[0]->skipfirstpop()){ #endif r1= comp_nodes[0]->svc(task); if (!(r1 == FF_GO_ON || r1 == FF_GO_OUT || r1 == FF_EOS_NOFREEZE)) { diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index cd535fb0..30923a4b 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -186,7 +186,7 @@ class dGroup : public ff::ff_farm { return new ff_Pipe(n); return n; }); - innerA2A->add_firstset(firstSet, 1 /*reinterpret_cast(ir.parentBB)->ondemand_buffer()*/); // note the ondemand!! + innerA2A->add_firstset(firstSet, reinterpret_cast(ir.parentBB)->ondemand_buffer()); // note the ondemand!! int outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? 0 : f.second.first.size());}); From 7d29154577fba027632a5bef99c65dbc94c93d85 Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Tue, 12 Apr 2022 19:18:13 +0200 Subject: [PATCH 150/202] Added mpi feature to the new implmentation tested locally --- ff/distributed/ff_batchbuffer.hpp | 6 +++++- ff/distributed/ff_dgroup.hpp | 22 ++++++++++++++++++---- ff/distributed/ff_dgroups.hpp | 5 ++--- ff/distributed/ff_dintermediate.hpp | 1 + ff/distributed/ff_dreceiverMPI.hpp | 2 -- ff/distributed/ff_dsenderMPI.hpp | 17 +++++++++++------ ff/distributed/ff_network.hpp | 2 ++ 7 files changed, 39 insertions(+), 16 deletions(-) diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp index e15ce639..5346c2ce 100644 --- a/ff/distributed/ff_batchbuffer.hpp +++ b/ff/distributed/ff_batchbuffer.hpp @@ -1,3 +1,5 @@ +#ifndef FF_BATCHBUFFER_H +#define FF_BATCHBUFFER_H #include "ff_network.hpp" #include using namespace ff; @@ -66,4 +68,6 @@ class ff_batchBuffer { size = 0; } -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 30923a4b..1b76aa7e 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -127,11 +127,25 @@ class dGroup : public ff::ff_farm { } } - if (ir.hasReceiver) - this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); + if (ir.hasReceiver){ + if (ir.protocol == Proto::TCP) + this->add_emitter(new ff_dreceiver(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); + +#ifdef DFF_MPI + else + this->add_emitter(new ff_dreceiverMPI(ir.expectedEOS, vector2Map(ir.hasLeftChildren() ? ir.inputL : ir.inputR))); +#endif + } - if (ir.hasSender) - this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); + if (ir.hasSender){ + if(ir.protocol == Proto::TCP) + this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); + +#ifdef DFF_MPI + else + this->add_collector(new ff_dsenderMPI(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); +#endif + } if (ir.hasRightChildren() && ir.parentBB->isAll2All()) { ff_a2a *a2a = reinterpret_cast(ir.parentBB); diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 4a54096a..4ac51e56 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -52,9 +52,7 @@ #endif namespace ff { - -enum Proto {TCP , MPI}; - + class dGroups { public: friend struct GroupInterface; @@ -233,6 +231,7 @@ class dGroups { void prepareIR(ff_pipeline* parentPipe){ ff::ff_IR& runningGroup_IR = annotatedGroups[this->runningGroup]; + runningGroup_IR.protocol = this->usedProtocol; // set the protocol ff_node* previousStage = getPreviousStage(parentPipe, runningGroup_IR.parentBB); ff_node* nextStage = getNextStage(parentPipe, runningGroup_IR.parentBB); // TODO: check coverage all 1st level diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index e2c53956..f16b65a5 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -72,6 +72,7 @@ class ff_IR { bool coverageL = false, coverageR = false; bool isSource = false, isSink = false; bool hasReceiver = false, hasSender = false; + Proto protocol; ff_node* parentBB; diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 4bac3115..7b3f0e37 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -61,8 +61,6 @@ class ff_dreceiverMPI: public ff_monode_t { if (coreid!=-1) ff_mapThreadToCpu(coreid); - int r; - for(size_t i = 0; i < input_channels; i++) handshakeHandler(); diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 5a830511..d7949a34 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -16,6 +16,8 @@ #include #include +#include + using namespace ff; @@ -25,8 +27,11 @@ class ff_dsenderMPI: public ff_minode_t { int last_rr_rank = 0; //next destiation to send for round robin policy std::map dest2Rank; std::map rankCounters; + std::map batchBuffers; std::vector destRanks; std::string gName; + int batchSize; + int messageOTF; int coreid; static int receiveReachableDestinations(int rank, std::map& m){ @@ -93,13 +98,13 @@ class ff_dsenderMPI: public ff_minode_t { } public: - ff_dsenderMPI(ff_endpoint destRank, std::string gName = "", int coreid=-1) - : gName(gName), coreid(coreid) { + ff_dsenderMPI(ff_endpoint destRank, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) + : gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { this->destRanks.push_back(std::move(destRank)); } - ff_dsenderMPI( std::vector destRanks_, std::string gName = "", int coreid=-1) - : destRanks(std::move(destRanks_)), gName(gName), coreid(coreid) {} + ff_dsenderMPI( std::vector destRanks_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) + : destRanks(std::move(destRanks_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} int svc_init() { if (coreid!=-1) @@ -107,7 +112,7 @@ class ff_dsenderMPI: public ff_minode_t { for(ff_endpoint& ep: this->destRanks){ handshakeHandler(ep.getRank(), false); - rankCounters[ep.getRank()] = QUEUEDIM; + rankCounters[ep.getRank()] = messageOTF; } @@ -115,7 +120,7 @@ class ff_dsenderMPI: public ff_minode_t { } void svc_end(){ - long totalack = destRanks.size()*QUEUEDIM; + long totalack = destRanks.size() * messageOTF; long currack = 0; for(const auto& pair : rankCounters) currack += pair.second; diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 7d33aa21..f4f57cff 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -57,6 +57,8 @@ #define le64toh(x) OSSwapLittleToHostInt64(x) #endif +enum Proto {TCP , MPI}; + class dataBuffer: public std::stringbuf { public: dataBuffer() From 50df1102f639319e4fa1e6e99532fd166e85be8b Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 13 Apr 2022 10:43:05 +0200 Subject: [PATCH 151/202] Proposal: conversion of relative path to absolute path in loader --- ff/distributed/loader/Makefile | 3 +-- ff/distributed/loader/dff_run.cpp | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ff/distributed/loader/Makefile b/ff/distributed/loader/Makefile index e7623b9f..80d1346c 100644 --- a/ff/distributed/loader/Makefile +++ b/ff/distributed/loader/Makefile @@ -23,7 +23,7 @@ # # --------------------------------------------------------------------------- -CXXFLAGS += -std=c++17 +CXXFLAGS += -std=c++20 ifdef DEBUG OPTIMIZE_FLAGS += -g -fno-inline-functions else @@ -38,7 +38,6 @@ endif CXXFLAGS += -Wall -LIBS = -pthread -lstdc++fs INCLUDES = $(INCS) SOURCES = $(wildcard *.cpp) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 30e3d5ef..2c99b9a0 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -35,13 +35,9 @@ #include #include -#if(defined(_MSC_VER) or (defined(__GNUC__) and (7 <= __GNUC_MAJOR__))) - #include - namespace n_fs = std::filesystem; -#else - #include - namespace n_fs = std::experimental::filesystem; -#endif + +#include +namespace n_fs = std::filesystem; enum Proto {TCP = 1 , MPI}; @@ -184,7 +180,7 @@ int main(int argc, char** argv) { usage(argv[0]); exit(EXIT_FAILURE); } - configFile = std::string(argv[++i]); + configFile = n_fs::current_path().string() + "/" + std::string(argv[++i]); } break; case 'V': { seeAll=true; @@ -207,12 +203,17 @@ int main(int argc, char** argv) { usage(argv[0]); exit(EXIT_FAILURE); } - if (!n_fs::exists(std::string(argv[optind]))) { + + executable = n_fs::current_path().string() + "/" + std::string(argv[optind]); + + if (!n_fs::exists(executable)) { std::cerr << "ERROR: Unable to find the executable file (we found as executable \'" << argv[optind] << "\')\n"; exit(EXIT_FAILURE); - } + } + + executable += " "; - for (int index = optind; index < argc; index++) { + for (int index = optind+1 ; index < argc; index++) { executable += std::string(argv[index]) + " "; } From c293b82915f2954e6a47e4932ec3fe149df23fbb Mon Sep 17 00:00:00 2001 From: Nicolo Tonci Date: Wed, 13 Apr 2022 10:48:32 +0200 Subject: [PATCH 152/202] Proposal: conversion of relative path to absolute path in loader --- ff/distributed/loader/dff_run.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 2c99b9a0..85a0c2d9 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -180,7 +180,7 @@ int main(int argc, char** argv) { usage(argv[0]); exit(EXIT_FAILURE); } - configFile = n_fs::current_path().string() + "/" + std::string(argv[++i]); + configFile = n_fs::absolute(n_fs::path(argv[++i])).string(); } break; case 'V': { seeAll=true; @@ -204,7 +204,7 @@ int main(int argc, char** argv) { exit(EXIT_FAILURE); } - executable = n_fs::current_path().string() + "/" + std::string(argv[optind]); + executable = n_fs::absolute(n_fs::path(argv[optind])).string(); if (!n_fs::exists(executable)) { std::cerr << "ERROR: Unable to find the executable file (we found as executable \'" << argv[optind] << "\')\n"; From 2c43050473ca06168aa54f5c84072cfe2b8e2924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 13 Apr 2022 11:14:46 +0200 Subject: [PATCH 153/202] Merge remote-tracking branch 'origin/master' into DistributedFF --- ff/distributed/loader/dff_run.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 85a0c2d9..35aeef51 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -97,7 +97,7 @@ struct G { void run(){ char b[1024]; // ssh -t // trovare MAX ARGV - sprintf(b, " %s %s %s %s %s --DFF_Config=%s --DFF_GName=%s %s 2>&1 %s", (isRemote() ? "ssh -t " : ""), (isRemote() ? host.c_str() : ""), (isRemote() ? "'" : ""), this->preCmd.c_str(), executable.c_str(), configFile.c_str(), this->name.c_str(), toBePrinted(this->name) ? "" : "> /dev/null", (isRemote() ? "'" : "")); + sprintf(b, " %s %s %s %s %s --DFF_Config=%s --DFF_GName=%s %s 2>&1 %s", (isRemote() ? "ssh -T " : ""), (isRemote() ? host.c_str() : ""), (isRemote() ? "'" : ""), this->preCmd.c_str(), executable.c_str(), configFile.c_str(), this->name.c_str(), toBePrinted(this->name) ? "" : "> /dev/null", (isRemote() ? "'" : "")); std::cout << "Executing the following command: " << b << std::endl; file = popen(b, "r"); fd = fileno(file); From 10605c6b7c24f124c18d518d894c9c56dddfc1c8 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 14 Apr 2022 17:56:37 +0200 Subject: [PATCH 154/202] minor fixes --- ff/dff.hpp | 3 +++ ff/distributed/ff_batchbuffer.hpp | 28 ++++++++++++--------- ff/distributed/ff_dsender.hpp | 41 ++++++++++++++++++++----------- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/ff/dff.hpp b/ff/dff.hpp index 725eb443..6e434b11 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -37,9 +37,12 @@ #endif +// default size of the batching buffer #if !defined(DEFAULT_BATCH_SIZE) #define DEFAULT_BATCH_SIZE 1 #endif + +// default number of On-The-Fly messages #if !defined(DEFAULT_INTERNALMSG_OTF) #define DEFAULT_INTERNALMSG_OTF 10 #endif diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp index e15ce639..c8bba246 100644 --- a/ff/distributed/ff_batchbuffer.hpp +++ b/ff/distributed/ff_batchbuffer.hpp @@ -19,7 +19,7 @@ class ff_batchBuffer { iov[0].iov_len = sizeof(int); } - void push(message_t* m){ + int push(message_t* m){ m->sender = htonl(m->sender); m->chid = htonl(m->chid); size_t* sz = new size_t(htobe64(m->data.getLen())); @@ -38,32 +38,36 @@ class ff_batchBuffer { toCleanup.emplace_back(sz, m); if (++size == batchSize) - this->flush(); - + return this->flush(); + return 0; } - void sendEOS(){ - push(new message_t(0,0)); - flush(); + int sendEOS(){ + if (push(new message_t(0,0))<0) { + error("pushing EOS"); + } + return flush(); } - void flush(){ - if (size == 0) return; + int flush(){ + if (size == 0) return 0; int size_ = size; size = htonl(size); - if (!callback(iov, size_*4+1)) + if (!callback(iov, size_*4+1)) { error("Callback of the batchbuffer got something wrong!\n"); + return -1; + } - while (!toCleanup.empty()){ + while (!toCleanup.empty()){ delete toCleanup.back().first; delete toCleanup.back().second; toCleanup.pop_back(); } - size = 0; + return 0; } -}; \ No newline at end of file +}; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index bd8e5e66..97eab878 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -129,7 +129,7 @@ class ff_dsender: public ff_minode_t { int create_connect(const ff_endpoint& destination){ int socketFD; - #ifdef LOCAL +#ifdef LOCAL socketFD = socket(AF_LOCAL, SOCK_STREAM, 0); if (socketFD < 0){ error("\nError creating socket \n"); @@ -145,9 +145,9 @@ class ff_dsender: public ff_minode_t { close(socketFD); return -1; } - #endif +#endif - #ifdef REMOTE +#ifdef REMOTE struct addrinfo hints; struct addrinfo *result, *rp; @@ -175,13 +175,12 @@ class ff_dsender: public ff_minode_t { } free(result); - if (rp == NULL) /* No address succeeded */ + if (rp == NULL) { /* No address succeeded */ return -1; - #endif - + } +#endif // receive the reachable destination from this sockets - return socketFD; } @@ -337,7 +336,10 @@ class ff_dsender: public ff_minode_t { return true; })); // change with the correct size - if (handshakeHandler(sck, false) < 0) return -1; + if (handshakeHandler(sck, false) < 0) { + error("svc_init ff_dsender failed"); + return -1; + } FD_SET(sck, &set); if (sck > fdmax) fdmax = sck; @@ -365,7 +367,9 @@ class ff_dsender: public ff_minode_t { if (++neos >= this->get_num_inchannels()) { // all input EOS received, now sending the EOS to all connections for(const auto& sck : sockets) { - batchBuffers[sck].sendEOS(); + if (batchBuffers[sck].sendEOS()<0) { + error("sending EOS to external connections (ff_dsender)\n"); + } shutdown(sck, SHUT_WR); } } @@ -426,9 +430,13 @@ class ff_dsenderH : public ff_dsender { int sck; decltype(internalSockets)::iterator it; - do + do { sck = waitAckFromAny(); // FIX: error management! - while ((it = std::find(internalSockets.begin(), internalSockets.end(), sck)) != internalSockets.end()); + if (sck < 0) { + error("waitAckFromAny failed in getNextReadyInternal"); + return -1; + } + } while ((it = std::find(internalSockets.begin(), internalSockets.end(), sck)) != internalSockets.end()); last_rr_socket_Internal = it - internalSockets.begin(); return sck; @@ -484,8 +492,7 @@ class ff_dsenderH : public ff_dsender { } if (writevn(sck, v, size) < 0){ - perror("Writevn: "); - error("Error sending the iovector inside the callback!\n"); + error("Error sending the iovector inside the callback (errno=%d) %s\n", errno); return false; } @@ -530,7 +537,9 @@ class ff_dsenderH : public ff_dsender { if (squareBoxEOS) return; squareBoxEOS = true; for(const auto& sck : internalSockets) { - batchBuffers[sck].sendEOS(); + if (batchBuffers[sck].sendEOS()<0) { + error("sending EOS to internal connections\n"); + } shutdown(sck, SHUT_WR); } } @@ -538,7 +547,9 @@ class ff_dsenderH : public ff_dsender { // all input EOS received, now sending the EOS to all // others connections for(const auto& sck : sockets) { - batchBuffers[sck].sendEOS(); + if (batchBuffers[sck].sendEOS()<0) { + error("sending EOS to external connections (ff_dsenderH)\n"); + } shutdown(sck, SHUT_WR); } } From a7f4e197e985d49201d25c7cc4fbbd5b2af5f441 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 14 Apr 2022 19:38:29 +0200 Subject: [PATCH 155/202] fixed bug in the ff_dreceiver.hpp --- ff/distributed/ff_batchbuffer.hpp | 3 --- ff/distributed/ff_dreceiver.hpp | 28 +++++++------------- ff/distributed/ff_dsender.hpp | 8 ++++-- tests/distributed/dwordcount/dwordcountH.cpp | 4 +++ tests/distributed/runParametricPerf.sh | 8 +++--- tests/distributed/test_parametricPerf.json | 11 +++++--- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp index 81187cee..2b242022 100644 --- a/ff/distributed/ff_batchbuffer.hpp +++ b/ff/distributed/ff_batchbuffer.hpp @@ -73,8 +73,5 @@ class ff_batchBuffer { } }; -<<<<<<< HEAD -======= #endif ->>>>>>> 2c43050473ca06168aa54f5c84072cfe2b8e2924 diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 075d5b3d..0a5f8dd7 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -246,12 +246,9 @@ class ff_dreceiver: public ff_monode_t { case 0: continue; } - // iterate over the file descriptor to see which one is active - int fixed_last = (this->last_receive_fd + 1) % (fdmax +1); - for(int i=0; i <= fdmax; i++){ - int actualFD = (fixed_last + i) % (fdmax +1); - if (FD_ISSET(actualFD, &tmpset)){ - if (actualFD == this->listen_sck) { + for(int idx=0; idx <= fdmax; idx++){ + if (FD_ISSET(idx, &tmpset)){ + if (idx == this->listen_sck) { int connfd = accept(this->listen_sck, (struct sockaddr*)NULL ,NULL); if (connfd == -1){ error("Error accepting client\n"); @@ -264,26 +261,20 @@ class ff_dreceiver: public ff_monode_t { continue; } - // it is not a new connection, call receive and handle possible errors - // save the last socket i - this->last_receive_fd = actualFD; - - - if (this->handleBatch(actualFD) < 0){ - close(actualFD); - FD_CLR(actualFD, &set); + if (this->handleBatch(idx) < 0){ + close(idx); + FD_CLR(idx, &set); // update the maximum file descriptor - if (actualFD == fdmax) + if (idx == fdmax) for(int ii=(fdmax-1);ii>=0;--ii) if (FD_ISSET(ii, &set)){ fdmax = ii; - this->last_receive_fd = -1; break; } - + } - + } } } @@ -297,7 +288,6 @@ class ff_dreceiver: public ff_monode_t { int listen_sck; ff_endpoint acceptAddr; std::map routingTable; - int last_receive_fd = -1; int coreid; ack_t ACK; }; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 97eab878..4beb6eea 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -358,7 +358,9 @@ class ff_dsender: public ff_minode_t { else sck = getMostFilledBufferSck(); // get the most filled buffer socket or a rr socket - batchBuffers[sck].push(task); + if (batchBuffers[sck].push(task) == -1) { + return EOS; + } return this->GO_ON; } @@ -523,7 +525,9 @@ class ff_dsenderH : public ff_dsender { sck = getMostFilledInternalBufferSck(); - batchBuffers[sck].push(task); + if (batchBuffers[sck].push(task) == -1) { + return EOS; + } return this->GO_ON; } diff --git a/tests/distributed/dwordcount/dwordcountH.cpp b/tests/distributed/dwordcount/dwordcountH.cpp index b755f2b7..0445051f 100644 --- a/tests/distributed/dwordcount/dwordcountH.cpp +++ b/tests/distributed/dwordcount/dwordcountH.cpp @@ -152,11 +152,15 @@ struct Source: ff_node_t { t->ts = current_time - app_start_time; ff_send_out(t); ++next_tuple_idx; + next_tuple_idx %= dataset.size(); // EOS reached if (current_time - app_start_time >= (app_run_time*1000000000L) && next_tuple_idx == 0) break; } + + std::cout << "Time expired\n"; + total_lines.fetch_add(generated_tuples); total_bytes.fetch_add(generated_bytes); return EOS; diff --git a/tests/distributed/runParametricPerf.sh b/tests/distributed/runParametricPerf.sh index 9d15dc51..a73047cd 100755 --- a/tests/distributed/runParametricPerf.sh +++ b/tests/distributed/runParametricPerf.sh @@ -2,11 +2,11 @@ ## Configure the parametric perf ITEMS=1000 -BYTExITEM=1000 -EXECTIMESOURCE=10 -EXECTIMESINK=10 +BYTExITEM=100 # 0 +EXECTIMESOURCE=0 #10 +EXECTIMESINK=0 #10 WORKERSXPROCESSSX=1 -WORKERSXPROCESSDX=20 +WORKERSXPROCESSDX= 1 #20 ## this scripts allow to run a solution in the cluster automatically assigning addresses and ports ## diff --git a/tests/distributed/test_parametricPerf.json b/tests/distributed/test_parametricPerf.json index 1671aed5..0979a1f9 100644 --- a/tests/distributed/test_parametricPerf.json +++ b/tests/distributed/test_parametricPerf.json @@ -12,12 +12,17 @@ , { "endpoint" : "localhost:8002", - "name" : "D0" + "name" : "S2" } - , + , { "endpoint" : "localhost:8003", - "name" : "D1" + "name" : "S3" + } + , + { + "endpoint" : "localhost:8004", + "name" : "D0" } ] } From 28339009c91f0edfd5f47fa739ab4a4a409ef856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Fri, 15 Apr 2022 10:57:35 +0200 Subject: [PATCH 156/202] Working version of MPI implementation: both vertical and horizontal divisions --- ff/distributed/ff_dgroup.hpp | 22 ++- ff/distributed/ff_dreceiverMPI.hpp | 3 +- ff/distributed/ff_dsenderMPI.hpp | 207 +++++++++++++++++++++++++++-- ff/distributed/ff_network.hpp | 2 +- 4 files changed, 213 insertions(+), 21 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 1b76aa7e..5cd02171 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -245,11 +245,23 @@ class dGroup : public ff::ff_farm { workers.push_back(innerA2A); - if (ir.hasReceiver) - this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); - - if (ir.hasSender) - this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF) , true); + if (ir.hasReceiver){ + if (ir.protocol == Proto::TCP) + this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); +#ifdef DFF_MPI + else + this->add_emitter(new ff_dreceiverHMPI(ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); +#endif + } + + if (ir.hasSender){ + if(ir.protocol == Proto::TCP) + this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF) , true); +#ifdef DFF_MPI + else + this->add_collector(new ff_dsenderHMPI(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF), true); +#endif + } } if (this->getNWorkers() == 0){ diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 7b3f0e37..0ebf0401 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -61,6 +61,7 @@ class ff_dreceiverMPI: public ff_monode_t { if (coreid!=-1) ff_mapThreadToCpu(coreid); + for(size_t i = 0; i < input_channels; i++) handshakeHandler(); @@ -163,7 +164,7 @@ class ff_dreceiverHMPI : public ff_dreceiverMPI { else if (task->chid != -1) ff_send_out_to(task, this->routingTable[task->chid]); else { ff_send_out_to(task, next_rr_destination); - next_rr_destination = ++next_rr_destination % (this->get_num_outchannels()-1); + next_rr_destination = ((next_rr_destination + 1) % (this->get_num_outchannels()-1)); } } diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index d7949a34..a4bfe837 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -28,6 +28,7 @@ class ff_dsenderMPI: public ff_minode_t { std::map dest2Rank; std::map rankCounters; std::map batchBuffers; + std::vector ranks; std::vector destRanks; std::string gName; int batchSize; @@ -59,9 +60,13 @@ class ff_dsenderMPI: public ff_minode_t { return 0; } - virtual int handshakeHandler(const int rank, bool){ + int sendGroupName(const int rank){ MPI_Send(gName.c_str(), gName.size(), MPI_BYTE, rank, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD); + return 0; + } + virtual int handshakeHandler(const int rank, bool){ + sendGroupName(rank); return receiveReachableDestinations(rank, dest2Rank); } @@ -86,9 +91,9 @@ class ff_dsenderMPI: public ff_minode_t { } int getNextReady(){ - for(size_t i = 0; i < this->destRanks.size(); i++){ - int rankIndex = (last_rr_rank + 1 + i) % this->destRanks.size(); - int rank = destRanks[rankIndex].getRank(); + for(size_t i = 0; i < this->ranks.size(); i++){ + int rankIndex = (last_rr_rank + 1 + i) % this->ranks.size(); + int rank = ranks[rankIndex]; if (rankCounters[rank] > 0) { last_rr_rank = rankIndex; return rank; @@ -97,6 +102,31 @@ class ff_dsenderMPI: public ff_minode_t { return waitAckFromAny(); } + int sendToRank(const int rank, const message_t* task){ + size_t sz = task->data.getLen(); + + long header[3] = {(long)sz, task->sender, task->chid}; + + MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + + MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); + + return 0; + + } + + int getMostFilledBufferRank(){ + int rankMax = 0; + int sizeMax = 0; + for(auto& [rank, buffer] : batchBuffers){ + if (buffer.size > sizeMax) rankMax = rank; + } + if (rankMax > 0) return rankMax; + + last_rr_rank = (last_rr_rank + 1) % this->destRanks.size(); + return this->destRanks[last_rr_rank].getRank(); + } + public: ff_dsenderMPI(ff_endpoint destRank, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { @@ -113,14 +143,17 @@ class ff_dsenderMPI: public ff_minode_t { for(ff_endpoint& ep: this->destRanks){ handshakeHandler(ep.getRank(), false); rankCounters[ep.getRank()] = messageOTF; + ranks.push_back(ep.getRank()); } + this->destRanks.clear(); + return 0; } void svc_end(){ - long totalack = destRanks.size() * messageOTF; + long totalack = ranks.size() * messageOTF; long currack = 0; for(const auto& pair : rankCounters) currack += pair.second; @@ -141,14 +174,8 @@ class ff_dsenderMPI: public ff_minode_t { } } else rank = getNextReady(); - - size_t sz = task->data.getLen(); - long header[3] = {(long)sz, task->sender, task->chid}; - - MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); - - MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); + sendToRank(rank, task); rankCounters[rank]--; @@ -160,11 +187,163 @@ class ff_dsenderMPI: public ff_minode_t { if (++neos >= this->get_num_inchannels()){ long header[3] = {0,0,0}; - for(auto& ep : destRanks) - MPI_Send(header, 3, MPI_LONG, ep.getRank(), DFF_HEADER_TAG, MPI_COMM_WORLD); + for(auto& rank : ranks) + MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); } } }; + +class ff_dsenderHMPI : public ff_dsenderMPI { + + std::map internalDest2Rank; + std::vector internalRanks; + int last_rr_rank_Internal = -1; + std::set internalGroupNames; + int internalMessageOTF; + bool squareBoxEOS = false; + + int getNextReadyInternal(){ + for(size_t i = 0; i < this->internalRanks.size(); i++){ + int actualRankIndex = (last_rr_rank_Internal + 1 + i) % this->internalRanks.size(); + int sck = internalRanks[actualRankIndex]; + if (rankCounters[sck] > 0) { + last_rr_rank_Internal = actualRankIndex; + return sck; + } + } + + int rank; + decltype(internalRanks)::iterator it; + + do + rank = waitAckFromAny(); // FIX: error management! + while ((it = std::find(internalRanks.begin(), internalRanks.end(), rank)) != internalRanks.end()); + + last_rr_rank_Internal = it - internalRanks.begin(); + return rank; + } + + int getMostFilledInternalBufferRank(){ + int rankMax = 0; + int sizeMax = 0; + for(int rank : internalRanks){ + auto& b = batchBuffers[rank]; + if (b.size > sizeMax) { + rankMax = rank; + sizeMax = b.size; + } + } + if (rankMax > 0) return rankMax; + + last_rr_rank_Internal = (last_rr_rank_Internal + 1) % this->internalRanks.size(); + return internalRanks[last_rr_rank_Internal]; + } + +public: + + ff_dsenderHMPI(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderHMPI(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + + int handshakeHandler(const int rank, bool isInternal){ + sendGroupName(rank); + + return receiveReachableDestinations(rank, isInternal ? internalDest2Rank : dest2Rank); + } + + int svc_init() { + + if (coreid!=-1) + ff_mapThreadToCpu(coreid); + + for(auto& endpoint : this->destRanks){ + int rank = endpoint.getRank(); + bool isInternal = internalGroupNames.contains(endpoint.groupName); + if (isInternal) + internalRanks.push_back(rank); + else + ranks.push_back(rank); + rankCounters[rank] = isInternal ? internalMessageOTF: messageOTF; + /*batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { + + if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ + error("Errore waiting ack from socket inside the callback\n"); + return false; + } + + if (writevn(sck, v, size) < 0){ + perror("Writevn: "); + error("Error sending the iovector inside the callback!\n"); + return false; + } + + this->socketsCounters[sck]--; + + return true; + })); // change with the correct size*/ + if (handshakeHandler(rank, isInternal) < 0) return -1; + + } + + this->destRanks.clear(); + + return 0; + } + + message_t *svc(message_t* task) { + if (this->get_channel_id() == (ssize_t)(this->get_num_inchannels() - 1)){ + int rank; + + // pick destination from the list of internal connections! + if (task->chid != -1){ // roundrobin over the destinations + rank = internalDest2Rank[task->chid]; + } else + rank = getMostFilledInternalBufferRank(); + + + // boh!! + //batchBuffers[rank].push(task); + sendToRank(rank, task); + return this->GO_ON; + } + + return ff_dsenderMPI::svc(task); + } + + void eosnotify(ssize_t id) { + if (id == (ssize_t)(this->get_num_inchannels() - 1)){ + // send the EOS to all the internal connections + if (squareBoxEOS) return; + squareBoxEOS = true; + long header[3] = {0,0,0}; + for(const auto&rank : internalRanks) + MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + + } + if (++neos >= this->get_num_inchannels()) { + // all input EOS received, now sending the EOS to all + // others connections + long header[3] = {0,0,0}; + for(const auto& rank : ranks) + MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + + } + } + + void svc_end() { + // here we wait all acks from all connections + long totalack = ranks.size() * messageOTF + internalRanks.size() * internalMessageOTF; + long currack = 0; + + for(const auto& pair : rankCounters) currack += pair.second; + + while(currack Date: Tue, 19 Apr 2022 19:38:18 +0200 Subject: [PATCH 157/202] RISV is now supported --- ff/cycle.h | 31 +++++++++++++++++++++++++++++++ ff/sysdep.h | 29 +++++++++++++++++++++++++++++ tests/Makefile | 14 ++++++++++---- tests/runtests.sh | 34 +++++++++++++++++++++++++++------- tests/test1.cpp | 4 ++-- 5 files changed, 99 insertions(+), 13 deletions(-) diff --git a/ff/cycle.h b/ff/cycle.h index 4c687893..d2b829a7 100644 --- a/ff/cycle.h +++ b/ff/cycle.h @@ -81,6 +81,34 @@ #ifndef FF_CYCLE_H #define FF_CYCLE_H +// Marco Aldinucci: RISCV Apr 2022 + +#if defined(__linux__) && defined(__riscv) && !defined(HAVE_TICK_COUNTER) +#pragma message "RISCV detected - experimental" +extern __inline + unsigned long + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +rdcycle(void) { + unsigned long dst; + // output into any register, likely a0 + // regular instruction: + asm volatile ("csrrs %0, 0xc00, x0" : "=r" (dst) ); + // regular instruction with symbolic csr and register names + // asm volatile ("csrrs %0, cycle, zero" : "=r" (dst) ); + // pseudo-instruction: + // asm volatile ("csrr %0, cycle" : "=r" (dst) ); + // pseudo-instruction: + //asm volatile ("rdcycle %0" : "=r" (dst) ); + return dst; +} +typedef unsigned long ticks; + +static __inline ticks getticks(void) { + return (rdcycle()); +} +#define HAVE_TICK_COUNTER +#endif + // Mauro Mulatero: ARM #if defined(__linux__) && (defined(__arm__) || defined(__aarch64__) ) #define TIME_WITH_SYS_TIME 1 @@ -259,6 +287,9 @@ INLINE_ELAPSED(__inline__) #define TIME_MIN 5000.0 #endif +// RISCV Linux + + /*---------------------------------------------------------------- generic Windows platform March 2011, Marco Aldinucci, aldinuc@di.unito.it diff --git a/ff/sysdep.h b/ff/sysdep.h index 4dc802fa..a753f5cc 100644 --- a/ff/sysdep.h +++ b/ff/sysdep.h @@ -46,6 +46,35 @@ * Various types of memory barriers and atomic operations \***********************************************************/ +/* RISCV + Marco Aldinucci + 10/04/2022 02:08 + RISC-V-Linux/linux/arch/riscv/include/asm/barrier.h +*/ + +#if defined(__riscv) +#pragma message "RISCV detected - experimental" + +#define nop() __asm__ __volatile__ ("nop") + +#define RISCV_FENCE(p, s) \ + __asm__ __volatile__ ("fence " #p "," #s : : : "memory") + +/* These barriers need to enforce ordering on both devices or memory. */ +#define mb() RISCV_FENCE(iorw,iorw) +#define rmb() RISCV_FENCE(ir,ir) +#define wmb() RISCV_FENCE(ow,ow) + +/* These barriers do not need to enforce ordering on devices, just memory. */ +#define __smp_mb() RISCV_FENCE(rw,rw) +#define __smp_rmb() RISCV_FENCE(r,r) +#define __smp_wmb() RISCV_FENCE(w,w) + +#define WMB() __smp_wmb() +#define PAUSE() + +#endif + /*------------------------ POWERPC ------------------------*/ diff --git a/tests/Makefile b/tests/Makefile index 00d156e0..fcea60f2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,3 +1,4 @@ + # --------------------------------------------------------------------------- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -70,7 +71,8 @@ endif #LINK_OPT = #VERSION = #CFLAGS = -#LDFLAGS = +#LDFLAGS = +TRACE_FASTFLOW =1 CXXFLAGS += -std=c++17 ifdef DEBUG OPTIMIZE_FLAGS += -g -fno-inline-functions @@ -157,9 +159,13 @@ tests: test test: all @echo "*****************************************************" @echo "* *" - @echo "* make test(s) is rather naive at moment, it just *" - @echo "* checks compilation and execution with default *" - @echo "* argument parameters *" +# @echo "* make test(s) is rather naive at moment, it just *" +# @echo "* checks compilation and execution with default *" +# @echo "* argument parameters *" + @echo "* FastFlow - efficient parallel streaming runtime *" + @echo "* https://github.com/fastflow *" + @echo "* uname -m: riscv, nproc: 4 *" + @echo "* running test suite with 2-8 threads *" @echo "* *" @echo "*****************************************************" @./runtests.sh diff --git a/tests/runtests.sh b/tests/runtests.sh index 496b2caa..ada82de7 100755 --- a/tests/runtests.sh +++ b/tests/runtests.sh @@ -7,19 +7,39 @@ # contained in the current directory and then checks the exit status # ( hoping that the program terminates ;) ) # -# - -execfiles="$(find . -maxdepth 1 -type f -executable| grep -v runtests.sh| grep -v mytime.h)" +# +system=`uname -mo` +arch=`uname -o` +if [ $arch = "Darwin" ]; then + execfiles="$(find . -maxdepth 1 -type f -perm -a=x| grep -v runtests.sh| grep -v mytime.h)" +else + execfiles="$(find . -maxdepth 1 -type f -executable| grep -v runtests.sh| grep -v mytime.h)" +fi +echo "*****************************************************************************" +echo "*" +echo "* FastFlow: high-performance parallel patterns and building blocks in C++" +echo "* https://github.com/fastflow/fastflow" +echo "* http://dx.doi.org/10.1002/9781119332015.ch13" +echo "* Running $(wc -w <<< $execfiles) tests on: $system " +test -f /proc/cpuinfo && echo "* CPU cores: $(grep -c processor /proc/cpuinfo)" +test -f /proc/cpuinfo && echo "* $(grep uarch /proc/cpuinfo | head -n1)" +echo "*" +echo "*****************************************************************************" +count=0 +success=0 +failure=0 for file in $execfiles -do - echo -n "$file: " +do + ((count=count+1)) + echo -n "[$count] $file: " $($file &> /dev/null) if [ $? -eq 0 ]; then echo "OK" + ((success=success+1)) else echo "FAILED" + ((failure=failure+1)) fi done -echo -echo "DONE" +echo "Done. $count tests: $success completed with success, $failure failed." diff --git a/tests/test1.cpp b/tests/test1.cpp index 1aa39d38..217c661b 100644 --- a/tests/test1.cpp +++ b/tests/test1.cpp @@ -79,7 +79,7 @@ class Emitter: public ff_node { int main(int argc, char * argv[]) { int nworkers = 1; int streamlen = 10; - + if (argc>1) { if (argc!=3) { std::cerr << "use: " @@ -90,7 +90,7 @@ int main(int argc, char * argv[]) { nworkers=atoi(argv[1]); streamlen=atoi(argv[2]); } - + if (!nworkers || !streamlen) { std::cerr << "Wrong parameters values\n"; return -1; From 7657a507f396431dd07d9904a08d31f60f5d1990 Mon Sep 17 00:00:00 2001 From: Aldinucci Marco Date: Tue, 19 Apr 2022 19:41:59 +0200 Subject: [PATCH 158/202] RISC-V is now supported --- ff/cycle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/cycle.h b/ff/cycle.h index d2b829a7..107b8a4f 100644 --- a/ff/cycle.h +++ b/ff/cycle.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2003, 2007 Matteo Frigo * Copyright (c) 2003, 2007 Massachusetts Institute of Technology - * + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including From 13f65163a7373b84235267c6901c1fbbb5e07d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 28 Apr 2022 12:36:58 +0200 Subject: [PATCH 159/202] Fixed issue related to mpirun invokation in the loader. Added -np parameter --- ff/distributed/loader/dff_run.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 35aeef51..f81b7d2b 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -298,7 +298,7 @@ int main(int argc, char** argv) { char command[350]; - sprintf(command, "mpirun --hostfile %s %s --DFF_Config=%s", hostFile.c_str(), executable.c_str(), configFile.c_str()); + sprintf(command, "mpirun -np %lu --hostfile %s %s --DFF_Config=%s", parsedGroups.size(), hostFile.c_str(), executable.c_str(), configFile.c_str()); FILE *fp; char buff[1024]; From b1e4e3cb424788f99a8fef971f5cd2e9dbf30605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 28 Apr 2022 15:34:36 +0200 Subject: [PATCH 160/202] Added the possibility to specify a custom thread mapping per group in the configuration file through the key << threadMapping >> --- ff/distributed/ff_dgroups.hpp | 40 +++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 4ac51e56..d92aa060 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -52,7 +52,7 @@ #endif namespace ff { - + class dGroups { public: friend struct GroupInterface; @@ -118,25 +118,35 @@ class dGroups { this->runningGroup = parsedGroups[rank].name; } + /* + * Set the thread mapping if specified in the configuration file. Otherwise use the default mapping specified in the legacy FastFlow config.hpp file. + * In config file the mapping can be specified for each group through the key "threadMapping" + */ + void setThreadMapping(){ + auto g = std::find_if(parsedGroups.begin(), parsedGroups.end(), [this](auto& g){return g.name == this->runningGroup;}); + if (g != parsedGroups.end() && !g->threadMapping.empty()) + threadMapper::instance()->setMappingList(g->threadMapping.c_str()); + } + const std::string& getRunningGroup() const { return runningGroup; } void forceProtocol(Proto p){this->usedProtocol = p;} int run_and_wait_end(ff_pipeline* parent){ if (annotatedGroups.find(runningGroup) == annotatedGroups.end()){ - ff::error("The group specified is not found nor implemented!\n"); + ff::error("The group %s is not found nor implemented!\n", runningGroup.c_str()); return -1; } - bool allDeriveFromParent = true; - for(auto& [name, ir]: annotatedGroups) - if (ir.parentBB != parent) { allDeriveFromParent = false; break; } + bool allDeriveFromParent = true; + for(auto& [name, ir]: annotatedGroups) + if (ir.parentBB != parent) { allDeriveFromParent = false; break; } - if (allDeriveFromParent) { - ff_pipeline *mypipe = new ff_pipeline; - mypipe->add_stage(parent); - parent = mypipe; - } + if (allDeriveFromParent) { + ff_pipeline *mypipe = new ff_pipeline; + mypipe->add_stage(parent); + parent = mypipe; + } // qui dovrei creare la rappresentazione intermedia di tutti this->prepareIR(parent); @@ -185,6 +195,7 @@ class dGroups { struct G { std::string name; std::string address; + std::string threadMapping; int port; int batchSize = DEFAULT_BATCH_SIZE; int internalMessageOTF = DEFAULT_INTERNALMSG_OTF; @@ -212,6 +223,10 @@ class dGroups { ar(cereal::make_nvp("messageOTF", messageOTF)); } catch (cereal::Exception&) {ar.setNextName(nullptr);} + try { + ar(cereal::make_nvp("threadMapping", threadMapping)); + } catch (cereal::Exception&) {ar.setNextName(nullptr);} + } }; @@ -559,7 +574,10 @@ static inline int DFF_Init(int& argc, char**& argv){ std::cout << "Running group: " << dGroups::Instance()->getRunningGroup() << " on rank: " << myrank << "\n"; } - #endif + #endif + + // set the mapping if specified + dGroups::Instance()->setThreadMapping(); // set the name for the printer ff::cout.setPrefix(dGroups::Instance()->getRunningGroup()); From 0b2ea3317fed03a4f18e8538d4ee13a3ac275ae4 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Sat, 14 May 2022 08:21:00 +0200 Subject: [PATCH 161/202] fixed problem of invalid id in the eosnotify --- ff/gt.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ff/gt.hpp b/ff/gt.hpp index 3ff0667b..f051d3e4 100644 --- a/ff/gt.hpp +++ b/ff/gt.hpp @@ -580,30 +580,30 @@ class ff_gatherer: public ff_thread { else skipfirstpop=false; if (task == FF_GO_ON) continue; + channelid = (nextr-feedbackid); + frominput=true; + if (feedbackid>0) { // there are feedback channels + if (nextreosnotify(workers[nextr]->get_my_id()); + if (filter && notify_each_eos) + filter->eosnotify(channelid); //workers[nextr]->get_my_id()); offline[nextr]=true; ++neos; ret=task; } else if (task == FF_EOS_NOFREEZE) { if (filter && notify_each_eos) - filter->eosnotify(workers[nextr]->get_my_id()); + filter->eosnotify(channelid); //workers[nextr]->get_my_id()); offline[nextr]=true; ++neosnofreeze; ret = task; } else { FFTRACE(++taskcnt); - if (filter) { - channelid = (nextr-feedbackid); - frominput=true; - if (feedbackid>0) { // there are feedback channels - if (nextr Date: Thu, 19 May 2022 21:39:01 +0200 Subject: [PATCH 162/202] - Improved the on-demand communication protocol - Improved task allocation/deallocation control for data serialization (i.e., serializefreetask and deserializealloctask) --- ff/combine.hpp | 5 +- ff/distributed/ff_batchbuffer.hpp | 9 +- ff/distributed/ff_dadapters.hpp | 53 ++- ff/distributed/ff_dgroup.hpp | 27 +- ff/distributed/ff_dsender.hpp | 2 +- ff/distributed/ff_network.hpp | 10 +- ff/distributed/ff_typetraits.hpp | 53 ++- ff/distributed/ff_wrappers.hpp | 34 +- ff/multinode.hpp | 142 ++++-- ff/node.hpp | 86 +++- tests/distributed/dwordcount/dwordcount.cpp | 20 +- .../dwordcount/dwordcountbench.cpp | 421 ++++++++++++++++++ tests/distributed/test_a2a_h2.cpp | 4 +- tests/distributed/test_a2a_h3.cpp | 4 +- tests/distributed/test_a2a_h4.cpp | 4 +- tests/distributed/test_parametricPerf.cpp | 43 +- tests/distributed/test_parametricPerfH.cpp | 80 ++-- 17 files changed, 837 insertions(+), 160 deletions(-) create mode 100644 tests/distributed/dwordcount/dwordcountbench.cpp diff --git a/ff/combine.hpp b/ff/combine.hpp index 4420e146..e784615f 100644 --- a/ff/combine.hpp +++ b/ff/combine.hpp @@ -316,8 +316,9 @@ class ff_comb: public ff_minode { #ifdef DFF_ENABLED virtual bool isSerializable(){ return comp_nodes[1]->isSerializable(); } virtual bool isDeserializable(){ return comp_nodes[0]->isDeserializable(); } - virtual decltype(serializeF) getSerializationFunction(){ return comp_nodes[1]->getSerializationFunction(); } - virtual decltype(deserializeF) getDeserializationFunction(){ return comp_nodes[0]->getDeserializationFunction(); } + virtual std::pair getSerializationFunction(){ return comp_nodes[1]->getSerializationFunction(); } + virtual std::pair getDeserializationFunction(){ return comp_nodes[0]->getDeserializationFunction(); } + #endif protected: diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp index 2b242022..82af4679 100644 --- a/ff/distributed/ff_batchbuffer.hpp +++ b/ff/distributed/ff_batchbuffer.hpp @@ -4,16 +4,17 @@ #include using namespace ff; -class ff_batchBuffer { +class ff_batchBuffer { std::function callback; int batchSize; - struct iovec iov[1024]; + struct iovec iov[UIO_MAXIOV]; std::vector> toCleanup; public: int size = 0; - ff_batchBuffer() {} + ff_batchBuffer() { + } ff_batchBuffer(int _size, std::function cbk) : callback(cbk), batchSize(_size) { - if (_size*4+1 > 1024){ + if (_size*4+1 > UIO_MAXIOV){ error("Size too big!\n"); abort(); } diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index 9097d9e3..4d873064 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -104,17 +104,33 @@ class EmitterAdapter: public internal_mo_transformer { bool forward(void* task, int destination){ - if (destination == -1){ - for(size_t i = 0; i < localWorkersMap.size(); i++){ - long actualDestination = (nextDestination + 1 + i) % localWorkersMap.size(); - if (ff_send_out_to(task, actualDestination, 1)){ // non blocking ff_send_out_to, we try just once - nextDestination = actualDestination; + if (destination == -1) { + message_t* msg = nullptr; + bool datacopied = true; + do { + for(size_t i = 0; i < localWorkersMap.size(); i++){ + long actualDestination = (nextDestination + 1 + i) % localWorkersMap.size(); + if (ff_send_out_to(task, actualDestination, 1)){ // non blcking ff_send_out_to, we try just once + nextDestination = actualDestination; + if (msg) { + if (!datacopied) msg->data.doNotCleanup(); + delete msg; + } + return true; + } + } + if (!msg) { + msg = new message_t(index, destination); + datacopied = this->n->serializeF(task, msg->data); + if (!datacopied) { + msg->data.freetaskF = this->n->freetaskF; + } + } + if (ff_send_out_to(msg, localWorkersMap.size(), 1)) { + if (datacopied) this->n->freetaskF(task); return true; } - } - message_t* msg = new message_t(index, destination); - this->n->serializeF(task, msg->data); - return ff_send_out_to(msg, localWorkersMap.size()); + } while(1); } auto pyshicalDestination = localWorkersMap.find(destination); @@ -122,8 +138,13 @@ class EmitterAdapter: public internal_mo_transformer { return ff_send_out_to(task, pyshicalDestination->second); } else { message_t* msg = new message_t(index, destination); - this->n->serializeF(task, msg->data); - return ff_send_out_to(msg, localWorkersMap.size()); + bool datacopied = this->n->serializeF(task, msg->data); + if (!datacopied) { + msg->data.freetaskF = this->n->freetaskF; + } + ff_send_out_to(msg, localWorkersMap.size()); + if (datacopied) this->n->freetaskF(task); + return true; } } @@ -173,10 +194,12 @@ class CollectorAdapter: public internal_mi_transformer { // if the results come from the "square box", it is a result from a remote workers so i have to read from which worker it come from if ((size_t)get_channel_id() == localWorkers.size()){ - message_t * m = reinterpret_cast(in); - channel = m->sender; - in = this->n->deserializeF(m->data); - delete m; + message_t * msg = reinterpret_cast(in); + channel = msg->sender; + bool datacopied = true; + in = this->n->deserializeF(msg->data, datacopied); + if (!datacopied) msg->data.doNotCleanup(); + delete msg; } else { // the result come from a local worker, just pass it to collector and compute the right worker id channel = localWorkers.at(get_channel_id()); } diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 5cd02171..b502ceb4 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -51,7 +51,7 @@ T getBackAndPop(std::vector& v){ namespace ff{ class dGroup : public ff::ff_farm { - + static inline std::unordered_map vector2UMap(const std::vector v){ std::unordered_map output; for(size_t i = 0; i < v.size(); i++) output[v[i]] = i; @@ -65,22 +65,27 @@ class dGroup : public ff::ff_farm { } struct ForwarderNode : ff_node { - ForwarderNode(std::function f){ + ForwarderNode(std::function f, + std::function d) { this->serializeF = f; + this->freetaskF = d; } - ForwarderNode(std::function f){ + ForwarderNode(std::function f, + std::function a) { + this->alloctaskF = a; this->deserializeF = f; } - void* svc(void* input){return input;} + void* svc(void* input){ return input;} }; static ff_node* buildWrapperIN(ff_node* n){ - if (n->isMultiOutput()) return new ff_comb(new WrapperIN(new ForwarderNode(n->deserializeF)), n, true, false); + if (n->isMultiOutput()) + return new ff_comb(new WrapperIN(new ForwarderNode(n->deserializeF, n->alloctaskF)), n, true, false); return new WrapperIN(n); } static ff_node* buildWrapperOUT(ff_node* n, int id, int outputChannels){ - if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF), id, 1, true), false, true); + if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF, n->freetaskF), id, 1, true), false, true); return new WrapperOUT(n, id, outputChannels); } @@ -169,7 +174,8 @@ class dGroup : public ff::ff_farm { wrapped->skipallpop(true); firstSet.push_back(wrapped); } else { - firstSet.push_back(new ff_comb(new WrapperIN(new ForwarderNode(child->getDeserializationFunction()), 1, true), new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers), true, true)); + auto d = child->getDeserializationFunction(); + firstSet.push_back(new ff_comb(new WrapperIN(new ForwarderNode(d.first,d.second), 1, true), new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers), true, true)); } else { @@ -207,12 +213,13 @@ class dGroup : public ff::ff_farm { std::vector reverseRightOutputIndexes(ir.outputR.rbegin(), ir.outputR.rend()); std::vector secondSet; for(ff_node* child : ir.R){ - if (isSeq(child)) + if (isSeq(child)) { + auto s = child->getSerializationFunction(); secondSet.push_back( (ir.isSink) ? (ff_node*)new CollectorAdapter(child, ir.outputL) - : (ff_node*)new ff_comb(new CollectorAdapter(child, ir.outputL), new WrapperOUT(new ForwarderNode(child->getSerializationFunction()), getBackAndPop(reverseRightOutputIndexes), 1, true), true, true) + : (ff_node*)new ff_comb(new CollectorAdapter(child, ir.outputL), new WrapperOUT(new ForwarderNode(s.first, s.second), getBackAndPop(reverseRightOutputIndexes), 1, true), true, true) ); - else { + } else { ff::svector inputs; child->get_in_nodes(inputs); for(ff_node* input : inputs){ ff_node* inputParent = getBB(child, input); diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 4beb6eea..433bac4f 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -53,7 +53,7 @@ class ff_dsender: public ff_minode_t { std::vector dest_endpoints; std::map dest2Socket; std::vector sockets; - int last_rr_socket; + int last_rr_socket = -1; std::map socketsCounters; std::map batchBuffers; std::string gName; diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 58f1d372..4258ec46 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -72,7 +72,13 @@ class dataBuffer: public std::stringbuf { } ~dataBuffer() { - if (cleanup) delete [] getPtr(); + if (cleanup) { + cleanup = false; + if (freetaskF) { + freetaskF(getPtr()); + } else + delete [] getPtr(); + } } void setBuffer(char p[], size_t len, bool cleanup=true){ @@ -93,6 +99,8 @@ class dataBuffer: public std::stringbuf { cleanup = false; } + std::function freetaskF; + protected: ssize_t len=-1; bool cleanup = false; diff --git a/ff/distributed/ff_typetraits.hpp b/ff/distributed/ff_typetraits.hpp index eb0ab73e..f67ae36e 100644 --- a/ff/distributed/ff_typetraits.hpp +++ b/ff/distributed/ff_typetraits.hpp @@ -18,19 +18,33 @@ struct exists{ static constexpr bool value = decltype(check(0))::value; }; -template>(std::declval&>(), std::declval()))>>> +template>(std::declval&>(), std::declval()))>>> struct user_serialize_test{}; -template>(std::declval&>(), std::declval()))>>> +template>(std::declval&>(), std::declval()))>>> struct user_deserialize_test{}; -template, decltype(serializeWrapper(std::declval()))>>> +template(std::declval(), std::declval()))>>> +struct user_freetask_test{}; + +template>(std::declval&>(), std::declval()))>>> +struct user_alloctask_test{}; + + +template, decltype(serializeWrapper(std::declval(), std::declval()))>>> struct serialize_test{}; -template(std::declval(), std::declval(), std::declval()))>>> +template(std::declval(), std::declval(), std::declval()))>>> struct deserialize_test{}; +template(std::declval()))>>> +struct freetask_test{}; +template(std::declval(), std::declval(), std::declval()))>>> +struct alloctask_test{}; + + + /* High level traits to use @@ -51,6 +65,19 @@ using is_deserializable = exists; template inline constexpr bool is_deserializable_v = is_deserializable::value; +template +using has_freetask = exists; + +template +inline constexpr bool has_freetask_v = has_freetask::value; + +template +using has_alloctask = exists; + +template +inline constexpr bool has_alloctask_v = has_alloctask::value; + + } @@ -58,15 +85,25 @@ inline constexpr bool is_deserializable_v = is_deserializable::value; Wrapper to user defined serialize and de-serialize functions, in order to exploits user defined functions in other translation units. */ template::value>> -std::pair serializeWrapper(T*in){ +std::pair serializeWrapper(T*in, bool& datacopied){ std::pair p; - serialize>(p, in); + datacopied = serialize>(p, in); return p; } template::value>> -void deserializeWrapper(char* c, size_t s, T*& obj){ - deserialize>(std::make_pair(c, s),obj); +bool deserializeWrapper(char* c, size_t s, T* obj){ + return deserialize>(std::make_pair(c, s),obj); +} + +template::value>> +void freetaskWrapper(T*in){ + serializefreetask((char*)in, in); +} + +template::value>> +void alloctaskWrapper(char* c, size_t s, T*& p){ + deserializealloctask>(std::make_pair(c,s), p); } diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 6b07b9cb..238c3956 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -64,10 +64,8 @@ class WrapperIN: public internal_mi_transformer { } return n->svc_init(); } - - void svc_end(){this->n->svc_end();} - void * svc(void* in) { + void * svc(void* in) { message_t* msg = (message_t*)in; if (this->n->isMultiInput()) { @@ -75,12 +73,15 @@ class WrapperIN: public internal_mi_transformer { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); } - void* inputData = this->n->deserializeF(msg->data); - + bool datacopied=true; + void* inputData = this->n->deserializeF(msg->data, datacopied); + if (!datacopied) msg->data.doNotCleanup(); delete msg; return n->svc(inputData); } + void svc_end(){this->n->svc_end();} + ff_node* getOriginal(){ return this->n; } }; @@ -107,12 +108,13 @@ class WrapperOUT: public internal_mo_transformer { message_t* msg = new message_t; - - this->n->serializeF(in, msg->data); + bool datacopied = this->n->serializeF(in, msg->data); msg->sender = myID; // da cambiare con qualcosa di reale! msg->chid = id; - - return this->ff_send_out(msg); + if (!datacopied) msg->data.freetaskF = this->n->freetaskF; + this->ff_send_out(msg); + if (datacopied) this->n->freetaskF(in); + return true; } void * svc(void* in) { @@ -172,11 +174,13 @@ class WrapperINOUT: public internal_mi_transformer { message_t* msg = new message_t; - this->n->serializeF(in, msg->data); - msg->sender = myID; // da cambiare con qualcosa di reale! + bool datacopied= this->n->serializeF(in, msg->data); + msg->sender = myID; // FIX! msg->chid = id; - - return ff_node::ff_send_out(msg); + if (!datacopied) msg->data.freetaskF = this->n->freetaskF; + ff_node::ff_send_out(msg); + if (datacopied) this->n->freetaskF(in); + return true; } int svc_init() { @@ -197,7 +201,9 @@ class WrapperINOUT: public internal_mi_transformer { ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); } - void* out = n->svc(this->n->deserializeF(msg->data)); + bool datacopied=true; + void* out = n->svc(this->n->deserializeF(msg->data, datacopied)); + if (!datacopied) msg->data.doNotCleanup(); delete msg; serialize(out, defaultDestination); diff --git a/ff/multinode.hpp b/ff/multinode.hpp index d69489de..1ad6f765 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -837,25 +837,72 @@ struct ff_minode_t: ff_minode { EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { #ifdef DFF_ENABLED - // check on Serialization capabilities on the OUTPUT type! - if constexpr (traits::is_serializable_v){ - this->serializeF = [](void* o, dataBuffer& b) {std::pair p = serializeWrapper(reinterpret_cast(o)); b.setBuffer(p.first, p.second);}; - } else if constexpr (cereal::traits::is_output_serializable::value){ - this->serializeF = [](void* o, dataBuffer& b) -> void { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); delete reinterpret_cast(o);}; + /* WARNING: + * the definition of functions alloctaskF, freetaskF, serializeF, deserializeF + * IS DUPLICATED for the ff_node_t (see file node.hpp). + * + */ + if constexpr (traits::has_alloctask_v) { + this->alloctaskF = [](char* ptr, size_t sz) -> void* { + IN_t* p = nullptr; + alloctaskWrapper(ptr, sz, p); + assert(p); + return p; + }; + } else { + this->alloctaskF = [](char*, size_t ) -> void* { + IN_t* o = new IN_t; + assert(o); + return o; + }; + } + + if constexpr (traits::has_freetask_v) { + this->freetaskF = [](void* o) { + freetaskWrapper(reinterpret_cast(o)); + }; + + } else { + this->freetaskF = [](void* o) { delete reinterpret_cast(o); }; + } + + + // check on Serialization capabilities on the OUTPUT type! + if constexpr (traits::is_serializable_v){ + this->serializeF = [](void* o, dataBuffer& b) -> bool { + bool datacopied=true; + std::pair p = serializeWrapper(reinterpret_cast(o), datacopied); + b.setBuffer(p.first, p.second); + return datacopied; + }; + } else if constexpr (cereal::traits::is_output_serializable::value) { + this->serializeF = [](void* o, dataBuffer& b) -> bool { + std::ostream oss(&b); + cereal::PortableBinaryOutputArchive ar(oss); + ar << *reinterpret_cast(o); + return true; + }; } // check on Serialization capabilities on the INPUT type! if constexpr (traits::is_deserializable_v){ - this->deserializeF = [](dataBuffer& b) -> void* { - IN_t* ptr = nullptr; - b.doNotCleanup(); - deserializeWrapper(b.getPtr(), b.getLen(), ptr); - return ptr; - }; - } else if constexpr(cereal::traits::is_input_serializable::value){ - this->deserializeF = [](dataBuffer& b) -> void* {std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); IN_t* o = new IN_t; ar >> *o; return o;}; + this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { + IN_t* ptr=(IN_t*)this->alloctaskF(b.getPtr(), b.getLen()); + datacopied = deserializeWrapper(b.getPtr(), b.getLen(), ptr); + assert(ptr); + return ptr; + }; + } else if constexpr(cereal::traits::is_input_serializable::value) { + this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { + std::istream iss(&b); + cereal::PortableBinaryInputArchive ar(iss); + IN_t* o = (IN_t*)this->alloctaskF(nullptr,0); + assert(o); + ar >> *o; + datacopied = true; + return o; + }; } - #endif } OUT_t * const GO_ON, *const EOS, *const EOSW, *const GO_OUT, *const EOS_NOFREEZE; @@ -897,25 +944,66 @@ struct ff_monode_t: ff_monode { EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { #ifdef DFF_ENABLED + if constexpr (traits::has_alloctask_v) { + this->alloctaskF = [](char* ptr, size_t sz) -> void* { + IN_t* p = nullptr; + alloctaskWrapper(ptr, sz, p); + assert(p); + return p; + }; + } else { + this->alloctaskF = [](char*, size_t) -> void* { + IN_t* o = new IN_t; + assert(o); + return o; + }; + } + + if constexpr (traits::has_freetask_v) { + this->freetaskF = [](void* o) { + freetaskWrapper(reinterpret_cast(o)); + }; + + } else { + this->freetaskF = [](void* o) { delete reinterpret_cast(o); }; + } + + // check on Serialization capabilities on the OUTPUT type! if constexpr (traits::is_serializable_v){ - this->serializeF = [](void* o, dataBuffer& b) {std::pair p = serializeWrapper(reinterpret_cast(o)); b.setBuffer(p.first, p.second);}; - } else if constexpr (cereal::traits::is_output_serializable::value){ - this->serializeF = [](void* o, dataBuffer& b) -> void { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); delete reinterpret_cast(o);}; - } + this->serializeF = [](void* o, dataBuffer& b) -> bool { + bool datacopied=true; + std::pair p = serializeWrapper(reinterpret_cast(o),datacopied); + b.setBuffer(p.first, p.second); + return datacopied; + }; + } else if constexpr (cereal::traits::is_output_serializable::value) { + this->serializeF = [](void* o, dataBuffer& b) -> bool { + std::ostream oss(&b); + cereal::PortableBinaryOutputArchive ar(oss); + ar << *reinterpret_cast(o); + return true; + }; + } // check on Serialization capabilities on the INPUT type! if constexpr (traits::is_deserializable_v){ - this->deserializeF = [](dataBuffer& b) -> void* { - IN_t* ptr = nullptr; - b.doNotCleanup(); - deserializeWrapper(b.getPtr(), b.getLen(), ptr); - return ptr; - }; + this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { + IN_t* ptr=(IN_t*)this->alloctaskF(b.getPtr(), b.getLen()); + datacopied = deserializeWrapper(b.getPtr(), b.getLen(), ptr); + assert(ptr); + return ptr; + }; } else if constexpr(cereal::traits::is_input_serializable::value){ - this->deserializeF = [](dataBuffer& b) -> void* {std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); IN_t* o = new IN_t; ar >> *o; return o;}; + this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { + std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); + IN_t* o = (IN_t*)this->alloctaskF(nullptr,0); + assert(o); + ar >> *o; + datacopied = true; + return o; + }; } - #endif } OUT_t * const GO_ON, *const EOS, *const EOSW, *const GO_OUT, *const EOS_NOFREEZE; @@ -1127,4 +1215,4 @@ struct internal_mi_transformer: ff_minode { } // namespace -#endif /* FF_MULTINODE_HPP */ \ No newline at end of file +#endif /* FF_MULTINODE_HPP */ diff --git a/ff/node.hpp b/ff/node.hpp index bf878cab..639029f6 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1290,13 +1290,16 @@ class ff_node { virtual void propagateEOS(void* task=FF_EOS) { (void)task; } #ifdef DFF_ENABLED - std::function serializeF; - std::function deserializeF; + std::function serializeF; + std::function freetaskF; + std::function deserializeF; + std::function alloctaskF; + virtual bool isSerializable(){ return (bool)serializeF; } virtual bool isDeserializable(){ return (bool)deserializeF; } - virtual decltype(serializeF) getSerializationFunction(){return serializeF;} - virtual decltype(deserializeF) getDeserializationFunction(){return deserializeF;} + virtual std::pair getSerializationFunction(){return std::make_pair(serializeF,freetaskF);} + virtual std::pair getDeserializationFunction(){ return std::make_pair(deserializeF,alloctaskF);} GroupInterface createGroup(std::string); #endif @@ -1596,25 +1599,76 @@ struct ff_node_t: ff_node { EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { #ifdef DFF_ENABLED + /* WARNING: + * the definition of functions alloctaskF, freetaskF, serializeF, deserializeF + * IS DUPLICATED for the ff_minode_t and ff_monode_t (see file multinode.hpp). + * + */ + if constexpr (traits::has_alloctask_v) { + this->alloctaskF = [](char* ptr, size_t sz) -> void* { + IN_t* p = nullptr; + alloctaskWrapper(ptr, sz, p); + assert(p); + return p; + }; + } else { + this->alloctaskF = [](char*, size_t ) -> void* { + IN_t* o = new IN_t; + assert(o); + return o; + }; + } + + if constexpr (traits::has_freetask_v) { + this->freetaskF = [](void* o) { + freetaskWrapper(reinterpret_cast(o)); + }; + + } else { + this->freetaskF = [](void* o) { + if constexpr (!std::is_void_v) { + OUT_t* obj = reinterpret_cast(o); + delete obj; + } + }; + } + // check on Serialization capabilities on the OUTPUT type! if constexpr (traits::is_serializable_v){ - this->serializeF = [](void* o, dataBuffer& b) {std::pair p = serializeWrapper(reinterpret_cast(o)); b.setBuffer(p.first, p.second);}; + this->serializeF = [](void* o, dataBuffer& b) -> bool { + bool datacopied = true; + std::pair p = serializeWrapper(reinterpret_cast(o,datacopied)); + b.setBuffer(p.first, p.second); + return datacopied; + }; } else if constexpr (cereal::traits::is_output_serializable::value){ - this->serializeF = [](void* o, dataBuffer& b) -> void { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); delete reinterpret_cast(o);}; + this->serializeF = [](void* o, dataBuffer& b) -> bool { + std::ostream oss(&b); + cereal::PortableBinaryOutputArchive ar(oss); + ar << *reinterpret_cast(o); + return true; + }; } // check on Serialization capabilities on the INPUT type! - if constexpr (traits::is_deserializable_v){ - this->deserializeF = [](dataBuffer& b) -> void* { - IN_t* ptr = nullptr; - b.doNotCleanup(); - deserializeWrapper(b.getPtr(), b.getLen(), ptr); - return ptr; - }; + if constexpr (traits::is_deserializable_v) { + this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { + IN_t* ptr=(IN_t*)this->alloctaskF(b.getPtr(), b.getLen()); + datacopied = deserializeWrapper(b.getPtr(), b.getLen(), ptr); + assert(ptr); + return ptr; + }; } else if constexpr(cereal::traits::is_input_serializable::value){ - this->deserializeF = [](dataBuffer& b) -> void* {std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); IN_t* o = new IN_t; ar >> *o; return o;}; - } - + this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { + std::istream iss(&b); + cereal::PortableBinaryInputArchive ar(iss); + IN_t* o = (IN_t*)this->alloctaskF(nullptr,0); + assert(o); + ar >> *o; + datacopied = true; + return o; + }; + } #endif } diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index bf1732e1..191c5499 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -44,8 +44,9 @@ * /<---- pipe1 ---->/ /<--- pipe2 --->/ * /<----------------- a2a ------------------>/ * - * G1: pipe1 - * G2: pipe2 + * If -g is N then the groups G1...GN will be created. Each group will have n Source-Splitter and m Counter-Sink where n and m are the + * values set with -p (i.e, -p n,m). This means that the FastFlow graph will have: + * n*N Source-Sink replicas and m*N Counter-Sink replicas. * */ @@ -267,10 +268,11 @@ int main(int argc, char* argv[]) { std::string file_path(""); size_t source_par_deg = 0; size_t sink_par_deg = 0; + size_t ngroups = 0; if (argc >= 3 || argc == 1) { int option = 0; - while ((option = getopt(argc, argv, "f:p:t:")) != -1) { + while ((option = getopt(argc, argv, "f:p:g:t:")) != -1) { switch (option) { case 'f': file_path=std::string(optarg); break; case 'p': { @@ -316,6 +318,10 @@ int main(int argc, char* argv[]) { std::cerr << "Wrong values for the parallelism degree, please use option -p \n"; return -1; } + if (ngroups <=0 ) { + std::cerr << "Wrong values for the ngroups, please use option -g \n"; + return -1; + } if (DFF_getMyGroup() == "G1") { /// data pre-processing @@ -369,13 +375,7 @@ int main(int argc, char* argv[]) { ff_pipeline pipeMain(false, qlen, qlen, true); pipeMain.add_stage(&a2a); -#if 0 - if (DFF_getMyGroup() == "G1") { - threadMapper::instance()->setMappingList("0,1,2,3,4,5,6,7,8,9,10,11, 24,25,26,27,28,29,30,31,32,33,34,35"); - } else { - threadMapper::instance()->setMappingList("12,13,14,15,16,17,18,19,20,21,22,23, 36,37,38,39,40,41,42,43,44,45,46,47"); - } -#endif + std::cout << "Starting " << pipeMain.numThreads() << " threads\n\n"; /// evaluate topology execution time volatile unsigned long start_time_main_usecs = current_time_usecs(); diff --git a/tests/distributed/dwordcount/dwordcountbench.cpp b/tests/distributed/dwordcount/dwordcountbench.cpp new file mode 100644 index 00000000..3ca762c7 --- /dev/null +++ b/tests/distributed/dwordcount/dwordcountbench.cpp @@ -0,0 +1,421 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + */ + + +#define FF_BOUNDED_BUFFER +#define DEFAULT_BUFFER_CAPACITY 2048 + +#include +#include +#include +#include +#include +#include +#include + + +#include + + +using namespace ff; + +const size_t qlen = DEFAULT_BUFFER_CAPACITY; +const int MAXLINE=280; // character per line (CPL), a typically value is 80 CPL + + +struct tuple_t { + char text_line[MAXLINE]; // parsed line + size_t key; // line number + uint64_t id; // id set to zero + uint64_t ts; // timestamp +}; + +struct result_t { + std::string key; + uint64_t id; // indicates the current number of occurrences of the word + uint64_t ts; // timestamp + + template + void serialize(Archive & archive) { + archive(key,id,ts); + } +}; +struct Result_t { + std::vector keys; + + template + void serialize(Archive & archive) { + archive(keys); + } +}; + + +std::vector dataset; // contains all the input tuples in memory +std::atomic total_lines=0; // total number of lines processed by the system +std::atomic total_bytes=0; // total number of bytes processed by the system + +/// application run time (source generates the stream for app_run_time seconds, then sends out EOS) +unsigned long app_run_time = 15; // time in seconds + +static inline unsigned long current_time_usecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000L + (t.tv_nsec / 1000); +} +static inline unsigned long current_time_nsecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000000L + t.tv_nsec; +} + +struct Source: ff_monode_t { + size_t next_tuple_idx = 0; // index of the next tuple to be sent + int generations = 0; // counts the times the file is generated + long generated_tuples = 0; // tuples counter + long generated_bytes = 0; // bytes counter + + // time variables + unsigned long app_start_time; // application start time + unsigned long current_time; + + Source(const unsigned long _app_start_time): + app_start_time(_app_start_time),current_time(_app_start_time) { + } + + tuple_t* svc(tuple_t*) { + while(1) { + tuple_t* t = new tuple_t; + assert(t); + + if (generated_tuples > 0) { + current_time = current_time_nsecs(); + } + if (next_tuple_idx == 0) generations++; // file generations counter + generated_tuples++; // tuples counter + // put a timestamp and send the tuple + *t = dataset.at(next_tuple_idx); + generated_bytes += sizeof(tuple_t); + t->ts = current_time - app_start_time; + ff_send_out(t); + + ++next_tuple_idx; + next_tuple_idx %= dataset.size(); + // EOS reached + if (current_time - app_start_time >= (app_run_time*1000000000L) && next_tuple_idx == 0) { + break; + } + } + total_lines.fetch_add(generated_tuples); + total_bytes.fetch_add(generated_bytes); + + return EOS; + } +}; +struct Splitter: ff_monode_t { + Splitter(long buffered_lines):buffered_lines(buffered_lines) { } + + int svc_init() { + noutch=get_num_outchannels(); // number of output channels + outV.resize(noutch,nullptr); + return 0; + } + + Result_t* svc(tuple_t* in) { + char *tmpstr; + char *token = strtok_r(in->text_line, " ", &tmpstr); + while (token) { + int ch = std::hash()(std::string(token)) % noutch; + + if (outV[ch] == nullptr) { + Result_t* r = new Result_t; + assert(r); + outV[ch] = r; + } + result_t r; + r.key = std::string(token); + r.ts = in->ts; + r.id = in->id; + + outV[ch]->keys.push_back(r); + + token = strtok_r(NULL, " ", &tmpstr); + } + ++nmsgs; + if (nmsgs>=buffered_lines) { + for(long i=0;i outV; +}; + +struct Counter: ff_minode_t { + Result_t* svc(Result_t* in) { + for(size_t i=0;ikeys.size();++i) { + ++M[in->keys[i].key]; + // number of occurrences of the string word up to now + in->keys[i].id = M[in->keys[i].key]; + } + + return in; + } + + size_t unique() { + // std::cout << "Counter:\n"; + // for (const auto& kv : M) { + // std::cout << kv.first << " --> " << kv.second << "\n"; + // } + return M.size(); + } + std::map M; +}; + + +struct Sink: ff_node_t { + Result_t* svc(Result_t* in) { + words+= in->keys.size(); + delete in; + return GO_ON; + } + + size_t words=0; // total number of words received +}; + + +/** + * @brief Parse the input file and create all the tuples + * + * The created tuples are maintained in memory. The source node will generate the stream by + * reading all the tuples from main memory. + * + * @param file_path the path of the input dataset file + */ +int parse_dataset_and_create_tuples(const std::string& file_path) { + std::ifstream file(file_path); + if (file.is_open()) { + size_t all_records = 0; // counter of all records (dataset line) read + std::string line; + while (getline(file, line)) { + // process file line + if (!line.empty()) { + if (line.length() > MAXLINE) { + std::cerr << "ERROR INCREASE MAXLINE\n"; + std::cerr << line << "\n"; + //exit(EXIT_FAILURE); + } + tuple_t t; + strncpy(t.text_line, line.c_str(), MAXLINE-1); + t.text_line[MAXLINE-1]='\0'; + t.key = all_records; + t.id = 0; + t.ts = 0; + all_records++; + dataset.push_back(t); + } + } + file.close(); + } else { + std::cerr << "Unable to open the file " << file_path << "\n"; + return -1; + } + return 0; +} + + + +int main(int argc, char* argv[]) { + if (DFF_Init(argc, argv) != 0) { + error("DFF_Init\n"); + return -1; + } + + /// parse arguments from command line + std::string file_path(""); + size_t source_par_deg = 0; + size_t sink_par_deg = 0; + size_t ngroups = 0; + size_t buffered_lines = 1; + + if (argc >= 3 || argc == 1) { + int option = 0; + while ((option = getopt(argc, argv, "f:p:g:t:b:")) != -1) { + switch (option) { + case 'f': file_path=std::string(optarg); break; + case 'p': { + std::vector par_degs; + std::string pars(optarg); + std::stringstream ss(pars); + for (size_t i; ss >> i;) { + par_degs.push_back(i); + if (ss.peek() == ',') + ss.ignore(); + } + if (par_degs.size() != 2) { + std::cerr << "Error in parsing the input arguments -p, the format is n,m\n"; + return -1; + } else { + source_par_deg = par_degs[0]; + sink_par_deg = par_degs[1]; + } + } break; + case 'g': { + ngroups = std::stol(optarg); + if (ngroups<=0) { + std::cerr << "Wrong value for the '-g' option\n"; + return -1; + } + } break; + case 't': { + long t = std::stol(optarg); + if (t<=0 || t > 100) { + std::cerr << "Wrong value for the '-t' option, it should be in the range [1,100]\n"; + return -1; + } + app_run_time = t; + } break; + case 'b': { + buffered_lines = std::stol(optarg); + if (buffered_lines<=0 || buffered_lines>100000) { + std::cerr << "Wrong value for the '-b' option\n"; + return -1; + } + } break; + default: { + std::cerr << "Error in parsing the input arguments\n"; + return -1; + } + } + } + } else { + std::cerr << "Parameters: -p -f -t -b \n"; + return -1; + } + if (file_path.length()==0) { + std::cerr << "The file path is empty, please use option -f \n"; + return -1; + } + if (source_par_deg == 0 || sink_par_deg==0) { + std::cerr << "Wrong values for the parallelism degree, please use option -p \n"; + return -1; + } + + /// data pre-processing + if (parse_dataset_and_create_tuples(file_path)< 0) + return -1; + + + if (DFF_getMyGroup() == "G0") + { + std::cout << "\n\n"; + std::cout << "Executing WordCount with parameters:" << std::endl; + std::cout << " * source/splitter : " << source_par_deg << std::endl; + std::cout << " * counter/sink : " << sink_par_deg << std::endl; + std::cout << " * buffered lines : " << buffered_lines << std::endl; + std::cout << " * n. of groups : " << ngroups << std::endl; + std::cout << " * running time : " << app_run_time << " (s)\n"; + } + + /// application starting time + unsigned long app_start_time = current_time_nsecs(); + + std::vector C(sink_par_deg); + std::vector S(sink_par_deg); + std::vector L; // left and right workers of the A2A + std::vector R; + + ff_a2a a2a(false, qlen, qlen, true); + + for(size_t i=0; i < ngroups; ++i) { + + auto G = a2a.createGroup("G"+std::to_string(i)); + + for (size_t i=0;iadd_stage(new Source(app_start_time)); + pipe0->add_stage(new Splitter(buffered_lines)); + + L.push_back(pipe0); + + G << pipe0; + } + for (size_t i=0;iadd_stage(C[i]); + pipe1->add_stage(S[i]); + + R.push_back(pipe1); + + G << pipe1; + } + } + + + a2a.add_firstset(L, 1, true); // 1 for ondemand + a2a.add_secondset(R); + ff_pipeline pipeMain(false, qlen, qlen, true); + pipeMain.add_stage(&a2a); + + std::cout << "Starting " << pipeMain.numThreads() << " threads\n\n"; + /// evaluate topology execution time + volatile unsigned long start_time_main_usecs = current_time_usecs(); + if (pipeMain.run_and_wait_end()<0) { + error("running pipeMain\n"); + return -1; + } + volatile unsigned long end_time_main_usecs = current_time_usecs(); + std::cout << "Exiting" << std::endl; + double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); + std::cout << "elapsed time : " << elapsed_time_seconds << "(s)\n"; + + std::cout << "total_lines sent : " << total_lines << "\n"; + std::cout << "total_bytes sent : " << std::setprecision(5) << total_bytes/1048576.0 << "(MB)\n"; + //double throughput = total_lines / elapsed_time_seconds; + //double mbs = (double)((total_bytes / 1048576) / elapsed_time_seconds); + //std::cout << "Measured throughput: " << (int) throughput << " lines/second, " << mbs << " MB/s" << endl; + + return 0; +} diff --git a/tests/distributed/test_a2a_h2.cpp b/tests/distributed/test_a2a_h2.cpp index 7d4f4e7a..4ee9571e 100644 --- a/tests/distributed/test_a2a_h2.cpp +++ b/tests/distributed/test_a2a_h2.cpp @@ -57,10 +57,10 @@ struct Sink : ff_minode_t{ struct ForwarderNode : ff_node{ - ForwarderNode(std::function f){ + ForwarderNode(std::function f){ this->serializeF = f; } - ForwarderNode(std::function f){ + ForwarderNode(std::function f){ this->deserializeF = f; } void* svc(void* input){return input;} diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp index a3c9ce2f..67bf221d 100644 --- a/tests/distributed/test_a2a_h3.cpp +++ b/tests/distributed/test_a2a_h3.cpp @@ -73,10 +73,10 @@ struct StringPrinter : ff_node_t{ struct ForwarderNode : ff_node{ - ForwarderNode(std::function f){ + ForwarderNode(std::function f){ this->serializeF = f; } - ForwarderNode(std::function f){ + ForwarderNode(std::function f){ this->deserializeF = f; } void* svc(void* input){return input;} diff --git a/tests/distributed/test_a2a_h4.cpp b/tests/distributed/test_a2a_h4.cpp index 7afe22b5..9b6e4378 100644 --- a/tests/distributed/test_a2a_h4.cpp +++ b/tests/distributed/test_a2a_h4.cpp @@ -74,10 +74,10 @@ struct StringPrinter : ff_node_t{ struct ForwarderNode : ff_node{ - ForwarderNode(std::function f){ + ForwarderNode(std::function f){ this->serializeF = f; } - ForwarderNode(std::function f){ + ForwarderNode(std::function f){ this->deserializeF = f; } void* svc(void* input){return input;} diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 4527633b..8c0e9de1 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -9,8 +9,9 @@ * * /<------- a2a ------>/ * - * S: all left-hand side nodes - * D: all righ-hand side nodes + * distributed group names: + * S*: all left-hand side nodes + * D*: all righ-hand side nodes * */ @@ -24,7 +25,8 @@ #include #include -//#define MANUAL_SERIALIZATION 1 +// to test serialization without using Cereal +#define MANUAL_SERIALIZATION 1 // ------------------------------------------------------ std::mutex mtx; // used only for pretty printing @@ -72,33 +74,43 @@ struct ExcType { } archive(cereal::binary_data(C, clen)); } - + }; +template +void serializefreetask(T *o, ExcType* input) { + input->~ExcType(); + free(o); +} + #ifdef MANUAL_SERIALIZATION template -void serialize(Buffer&b, ExcType* input){ +bool serialize(Buffer&b, ExcType* input){ b = {(char*)input, input->clen+sizeof(ExcType)}; + return false; } template -void deserialize(const Buffer&b, ExcType*& Ptr){ - ExcType* p = new (b.first) ExcType(true); +void deserializealloctask(const Buffer& b, ExcType*& p) { + p = new (b.first) ExcType(true); +}; + +template +bool deserialize(const Buffer&b, ExcType* p){ p->clen = b.second - sizeof(ExcType); p->C = (char*)p + sizeof(ExcType); - Ptr = p; + return false; } #endif static ExcType* allocateExcType(size_t size, bool setdata=false) { - char* _p = (char*)malloc(size+sizeof(ExcType)); + char* _p = (char*)calloc(size+sizeof(ExcType), 1); // to make valgrind happy ! ExcType* p = new (_p) ExcType(true); // contiguous allocation p->clen = size; p->C = (char*)p+sizeof(ExcType); if (setdata) { - bzero(p->C, p->clen); p->C[0] = 'c'; if (size>10) p->C[10] = 'i'; @@ -130,7 +142,7 @@ struct MoNode : ff::ff_monode_t{ void svc_end(){ const std::lock_guard lock(mtx); ff::cout << "[MoNode" << this->get_my_id() << "] Generated Items: " << items << ff::endl; - } + } }; struct MiNode : ff::ff_minode_t{ @@ -150,13 +162,16 @@ struct MiNode : ff::ff_minode_t{ myassert(in->C[100] == 'a'); if (in->clen>500) myassert(in->C[500] == 'o'); - //ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; } if (in->C[in->clen-1] != 'F') { ff::cout << "ERROR: " << in->C[in->clen-1] << " != 'F'\n"; myassert(in->C[in->clen-1] == 'F'); } +#ifdef MANUAL_SERIALIZATION + in->~ExcType(); free(in); +#else delete in; +#endif return this->GO_ON; } @@ -201,8 +216,8 @@ int main(int argc, char*argv[]){ for(int i = 0; i < (numProcDx*numWorkerXProcessDx); i++) dxWorkers.push_back(new MiNode(execTimeSink, check)); - a2a.add_firstset(sxWorkers); - a2a.add_secondset(dxWorkers); + a2a.add_firstset(sxWorkers, 0, true); + a2a.add_secondset(dxWorkers, true); for(int i = 0; i < numProcSx; i++){ auto g = a2a.createGroup(std::string("S")+std::to_string(i)); diff --git a/tests/distributed/test_parametricPerfH.cpp b/tests/distributed/test_parametricPerfH.cpp index affa0abb..c85f8078 100644 --- a/tests/distributed/test_parametricPerfH.cpp +++ b/tests/distributed/test_parametricPerfH.cpp @@ -9,8 +9,8 @@ * * /<------- a2a ------>/ * - * S: all left-hand side nodes - * D: all righ-hand side nodes + * distributed group names: + * * */ @@ -20,7 +20,8 @@ #include #include -//#define MANUAL_SERIALIZATION 1 +// to test serialization without using Cereal +#define MANUAL_SERIALIZATION 1 // ------------------------------------------------------ std::mutex mtx; // used only for pretty printing @@ -39,11 +40,11 @@ static inline float active_delay(int msecs) { return x; } // this assert will not be removed by -DNDEBUG -#define myassert(c) { \ +#define myassert(c) { \ if (!(c)) { \ - std::cerr << "ERROR: assert at line " << __LINE__ << " failed\n"; \ - abort(); \ - } \ + std::cerr << "ERROR: myassert at line " << __LINE__ << " failed\n"; \ + abort(); \ + } \ } // ----------------------------------------------------- struct ExcType { @@ -70,31 +71,40 @@ struct ExcType { } }; +template +void serializefreetask(T *o, ExcType* input) { + input->~ExcType(); + free(o); +} #ifdef MANUAL_SERIALIZATION template -void serialize(Buffer&b, ExcType* input){ +bool serialize(Buffer&b, ExcType* input){ b = {(char*)input, input->clen+sizeof(ExcType)}; + return false; // 'false' means no data copy } template -void deserialize(const Buffer&b, ExcType*& Ptr){ - ExcType* p = new (b.first) ExcType(true); - p->C = (char*)p + sizeof(ExcType); +void deserializealloctask(const Buffer& b, ExcType*& p) { + p = new (b.first) ExcType(true); +}; + +template +bool deserialize(const Buffer&b, ExcType* p){ p->clen = b.second - sizeof(ExcType); - Ptr = p; + p->C = (char*)p + sizeof(ExcType); + return false; // 'false' means no data copy } #endif static ExcType* allocateExcType(size_t size, bool setdata=false) { - char* _p = (char*)malloc(size+sizeof(ExcType)); + char* _p = (char*)calloc(size+sizeof(ExcType), 1 ); // to make valgrind happy ! ExcType* p = new (_p) ExcType(true); // contiguous allocation p->clen = size; p->C = (char*)p+sizeof(ExcType); if (setdata) { - bzero(p->C, p->clen); p->C[0] = 'c'; if (size>10) p->C[10] = 'i'; @@ -136,22 +146,28 @@ struct MiNode : ff::ff_minode_t{ MiNode(int execTime, bool checkdata=false): execTime(execTime),checkdata(checkdata) {} ExcType* svc(ExcType* in){ - if (execTime) active_delay(this->execTime); - - ++processedItems; - if (checkdata) { - myassert(in->C[0] == 'c'); - if (in->clen>10) - myassert(in->C[10] == 'i'); - if (in->clen>100) - myassert(in->C[100] == 'a'); - if (in->clen>500) - myassert(in->C[500] == 'o'); - ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; - } - myassert(in->C[in->clen-1] == 'F'); - delete in; - return this->GO_ON; + if (execTime) active_delay(this->execTime); + ++processedItems; + if (checkdata) { + myassert(in->C[0] == 'c'); + if (in->clen>10) + myassert(in->C[10] == 'i'); + if (in->clen>100) + myassert(in->C[100] == 'a'); + if (in->clen>500) + myassert(in->C[500] == 'o'); + //ff::cout << "MiNode" << get_my_id() << " input data " << processedItems << " OK\n"; + } + if (in->C[in->clen-1] != 'F') { + ff::cout << "ERROR: " << in->C[in->clen-1] << " != 'F'\n"; + myassert(in->C[in->clen-1] == 'F'); + } +#ifdef MANUAL_SERIALIZATION + in->~ExcType(); free(in); +#else + delete in; +#endif + return this->GO_ON; } void svc_end(){ @@ -192,8 +208,8 @@ int main(int argc, char*argv[]){ for(int i = 0; i < ((numProcDx+1)*numWorkerXProcessDx); i++) dxWorkers.push_back(new MiNode(execTimeSink, check)); - a2a.add_firstset(sxWorkers, 10); //ondemand on!! - a2a.add_secondset(dxWorkers); + a2a.add_firstset(sxWorkers, 1, true); //ondemand on!! + a2a.add_secondset(dxWorkers, true); auto master = a2a.createGroup("M"); master << sxWorkers.front(); From b060b653a5a693714426819e168e37a0d1af02da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 23 May 2022 12:21:46 +0200 Subject: [PATCH 163/202] Added new MPI implementation with multiple buffering and non-blocking primitives. Added DISABLE_FF_DISTRIBUTED compilation option in order to disable the distributed runtime at compile time, without rewriting the application --- ff/dff.hpp | 10 ++ ff/distributed/ff_dinterface.hpp | 18 ++- ff/distributed/ff_dprinter.hpp | 2 - ff/distributed/ff_dreceiverMPI.hpp | 68 +++++---- ff/distributed/ff_dsenderMPI.hpp | 229 ++++++++++++++++++----------- tests/distributed/Makefile | 3 + 6 files changed, 214 insertions(+), 116 deletions(-) diff --git a/ff/dff.hpp b/ff/dff.hpp index 6e434b11..ec7b1ae5 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -20,6 +20,7 @@ * **************************************************************************** */ +#ifndef DISABLE_FF_DISTRIBUTED #ifndef FF_DFF_HPP #define FF_DFF_HPP @@ -58,3 +59,12 @@ #include #endif /* FF_DFF_HPP */ +#else /* DISABLE_FF_DISTRIBUTED */ +#include +#include +#include +namespace ff { + std::ostream& cout = std::cout; +} +static inline int DFF_Init(int& argc, char**& argv){ return 0; } +#endif /* DISABLE_FF_DISTRIBUTED */ diff --git a/ff/distributed/ff_dinterface.hpp b/ff/distributed/ff_dinterface.hpp index 87b59113..0dd37e5e 100644 --- a/ff/distributed/ff_dinterface.hpp +++ b/ff/distributed/ff_dinterface.hpp @@ -1,7 +1,15 @@ +#ifndef FF_DINTERFACE_H +#define FF_DINTERFACE_H + #include -#include #include + +#ifdef DFF_ENABLED +#include +#endif + + namespace ff { struct GroupInterface { @@ -9,6 +17,7 @@ struct GroupInterface { GroupInterface(std::string name) : name(name){} GroupInterface& operator<<(ff_node* node){ +#ifdef DFF_ENABLED auto& annotated = dGroups::Instance()->annotated; auto handler = annotated.find(node); if (handler == annotated.end()) @@ -17,14 +26,19 @@ struct GroupInterface { std::cerr << "Node has been annotated in group " << name << " and in group " << handler->second << "! Aborting\n"; abort(); } +#endif return *this; } }; GroupInterface ff_node::createGroup(std::string name){ +#ifdef DFF_ENABLED dGroups::Instance()->annotateGroup(name, this); +#endif return GroupInterface(name); } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/ff/distributed/ff_dprinter.hpp b/ff/distributed/ff_dprinter.hpp index 0114ab10..2df88a2f 100644 --- a/ff/distributed/ff_dprinter.hpp +++ b/ff/distributed/ff_dprinter.hpp @@ -2,8 +2,6 @@ #define FF_DPRINTER_H #include -#include - namespace ff { class prefixbuf : public std::streambuf { diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 0ebf0401..4f2d09be 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -73,37 +73,54 @@ class ff_dreceiverMPI: public ff_monode_t { Everything will be handled inside a while true in the body of this node where data is pulled from network */ message_t *svc(message_t* task) { - MPI_Request tmpAckReq; MPI_Status status; - long header[3]; while(neos < input_channels){ + + int headersLen; + MPI_Probe(MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status); + MPI_Get_count(&status, MPI_LONG, &headersLen); + long headers[headersLen]; - if (MPI_Recv(header, 3, MPI_LONG, MPI_ANY_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) + if (MPI_Recv(headers, headersLen, MPI_LONG, status.MPI_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) error("Error on Recv Receiver primo in alto\n"); - - size_t sz = header[0]; - - if (sz == 0){ - registerEOS(status.MPI_SOURCE); - continue; + assert(headers[0]*3+1 == headersLen); + if (headers[0] == 1){ + size_t sz = headers[3]; + + if (sz == 0){ + registerEOS(status.MPI_SOURCE); + continue; + } + char* buff = new char[sz]; + if (MPI_Recv(buff,sz,MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE) != MPI_SUCCESS) + error("Error on Recv Receiver Payload\n"); + + message_t* out = new message_t(buff, sz, true); + out->sender = headers[1]; + out->chid = headers[2]; + + this->forward(out, status.MPI_SOURCE); + } else { + int size; + MPI_Probe(status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, &status); + MPI_Get_count(&status, MPI_BYTE, &size); + char* buff = new char[size]; // this can be reused!! + MPI_Recv(buff, size, MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + size_t head = 0; + for (size_t i = 0; i < headers[0]; i++){ + size_t sz = headers[3*i+3]; + char* outBuff = new char[sz]; + memcpy(outBuff, buff+head, sz); + head += sz; + message_t* out = new message_t(outBuff, sz, true); + out->sender = headers[3*i+1]; + out->chid = headers[3*i+2]; + + this->forward(out, status.MPI_SOURCE); + } + delete [] buff; } - - char* buff = new char[sz]; - assert(buff); - - if (MPI_Recv(buff,sz,MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE) != MPI_SUCCESS) - error("Error on Recv Receiver Payload\n"); - - message_t* out = new message_t(buff, sz, true); - assert(out); - out->sender = header[1]; - out->chid = header[2]; - - this->forward(out, status.MPI_SOURCE); - - MPI_Isend(&ACK, sizeof(ack_t), MPI_BYTE, status.MPI_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &tmpAckReq); - MPI_Request_free(&tmpAckReq); } @@ -115,7 +132,6 @@ class ff_dreceiverMPI: public ff_monode_t { size_t input_channels; std::map routingTable; int coreid; - ack_t ACK; }; diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index a4bfe837..97bc84a8 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -16,18 +16,100 @@ #include #include -#include - - using namespace ff; class ff_dsenderMPI: public ff_minode_t { protected: + class batchBuffer { + protected: + int rank; + bool blocked = false; + size_t size_, actualSize = 0; + std::vector buffer; + std::vector headers; + MPI_Request headersR, datasR; + public: + batchBuffer(size_t size_, int rank) : rank(rank), size_(size_){ + headers.reserve(size_*3+1); + } + virtual void waitCompletion(){ + if (blocked){ + MPI_Wait(&headersR, MPI_STATUS_IGNORE); + MPI_Wait(&datasR, MPI_STATUS_IGNORE); + headers.clear(); + buffer.clear(); + blocked = false; + } + } + + virtual size_t size() {return actualSize;} + virtual int push(message_t* m){ + waitCompletion(); + int idx = 3*actualSize++; + headers[idx+1] = m->sender; + headers[idx+2] = m->chid; + headers[idx+3] = m->data.getLen(); + + buffer.insert(buffer.end(), m->data.getPtr(), m->data.getPtr() + m->data.getLen()); + + delete m; + if (actualSize == size_) { + this->flush(); + return 1; + } + return 0; + } + + virtual void flush(){ + headers[0] = actualSize; + MPI_Isend(headers.data(), actualSize*3+1, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD, &headersR); + MPI_Isend(buffer.data(), buffer.size(), MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD, &datasR); + blocked = true; + actualSize = 0; + } + + virtual void pushEOS(){ + int idx = 3*actualSize++; + headers[idx+1] = 0; headers[idx+2] = 0; headers[idx+3] = 0; + + this->flush(); + } + }; + + class directBatchBuffer : public batchBuffer { + message_t* currData = NULL; + long currHeader[4] = {1, 0, 0, 0}; + public: + directBatchBuffer(int rank) : batchBuffer(0, rank){} + void pushEOS(){ + waitCompletion(); + currHeader[1] = 0; currHeader[2] = 0; currHeader[3] = 0; + MPI_Send(currHeader, 4, MPI_LONG, this->rank, DFF_HEADER_TAG, MPI_COMM_WORLD); + } + void flush() {} + void waitCompletion(){ + if (blocked){ + MPI_Wait(&headersR, MPI_STATUS_IGNORE); + MPI_Wait(&datasR, MPI_STATUS_IGNORE); + if (currData) delete currData; + blocked = false; + } + } + int push(message_t* m){ + waitCompletion(); + currHeader[1] = m->sender; currHeader[2] = m->chid; currHeader[3] = m->data.getLen(); + MPI_Isend(currHeader, 4, MPI_LONG, this->rank, DFF_HEADER_TAG, MPI_COMM_WORLD, &this->headersR); + MPI_Isend(m->data.getPtr(), m->data.getLen(), MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD, &datasR); + currData = m; + blocked = true; + return 1; + } + }; size_t neos=0; int last_rr_rank = 0; //next destiation to send for round robin policy std::map dest2Rank; std::map rankCounters; - std::map batchBuffers; + std::map>> buffers; std::vector ranks; std::vector destRanks; std::string gName; @@ -116,15 +198,20 @@ class ff_dsenderMPI: public ff_minode_t { } int getMostFilledBufferRank(){ - int rankMax = 0; + int rankMax = -1; int sizeMax = 0; - for(auto& [rank, buffer] : batchBuffers){ - if (buffer.size > sizeMax) rankMax = rank; + for(int rank : ranks){ + auto& batchBB = buffers[rank]; + size_t sz = batchBB.second[batchBB.first]->size(); + if (sz > sizeMax) { + rankMax = rank; + sizeMax = sz; + } } - if (rankMax > 0) return rankMax; + if (rankMax >= 0) return rankMax; - last_rr_rank = (last_rr_rank + 1) % this->destRanks.size(); - return this->destRanks[last_rr_rank].getRank(); + last_rr_rank = (last_rr_rank + 1) % this->ranks.size(); + return this->ranks[last_rr_rank]; } public: @@ -142,9 +229,11 @@ class ff_dsenderMPI: public ff_minode_t { for(ff_endpoint& ep: this->destRanks){ handshakeHandler(ep.getRank(), false); - rankCounters[ep.getRank()] = messageOTF; + //rankCounters[ep.getRank()] = messageOTF; ranks.push_back(ep.getRank()); - + std::vector appo; + for(int i = 0; i < messageOTF; i++) appo.push_back(batchSize == 1 ? new directBatchBuffer(ep.getRank()) : new batchBuffer(batchSize, ep.getRank())); + buffers.emplace(std::make_pair(ep.getRank(), std::make_pair(0, std::move(appo)))); } this->destRanks.clear(); @@ -153,44 +242,31 @@ class ff_dsenderMPI: public ff_minode_t { } void svc_end(){ - long totalack = ranks.size() * messageOTF; - long currack = 0; - - for(const auto& pair : rankCounters) currack += pair.second; - - while(currackwaitCompletion(); } message_t *svc(message_t* task) { int rank; - if (task->chid != -1){ - rank = dest2Rank[task->chid]; - if (rankCounters[rank] == 0 && waitAckFrom(rank) == -1){ - error("Error waiting ACK\n"); - delete task; return this->GO_ON; - } - } else - rank = getNextReady(); + if (task->chid != -1) + rank = dest2Rank[task->chid]; + else + rank = getMostFilledBufferRank(); - sendToRank(rank, task); - - rankCounters[rank]--; - - delete task; + auto& buffs = buffers[rank]; + assert(buffs.second.size() > 0); + if (buffs.second[buffs.first]->push(task)) // the push triggered a flush, so we must go ion the next buffer + buffs.first = (buffs.first + 1) % buffs.second.size(); // increment the used buffer of 1 + return this->GO_ON; } void eosnotify(ssize_t) { - if (++neos >= this->get_num_inchannels()){ - long header[3] = {0,0,0}; - - for(auto& rank : ranks) - MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); - - } + if (++neos >= this->get_num_inchannels()) + for(auto& rank : ranks){ + auto& buffs = buffers[rank]; + buffs.second[buffs.first]->pushEOS(); + } } }; @@ -226,16 +302,17 @@ class ff_dsenderHMPI : public ff_dsenderMPI { } int getMostFilledInternalBufferRank(){ - int rankMax = 0; + int rankMax = -1; int sizeMax = 0; for(int rank : internalRanks){ - auto& b = batchBuffers[rank]; - if (b.size > sizeMax) { + auto& batchBB = buffers[rank]; + size_t sz = batchBB.second[batchBB.first]->size(); + if (sz > sizeMax) { rankMax = rank; - sizeMax = b.size; + sizeMax = sz; } } - if (rankMax > 0) return rankMax; + if (rankMax >= 0) return rankMax; last_rr_rank_Internal = (last_rr_rank_Internal + 1) % this->internalRanks.size(); return internalRanks[last_rr_rank_Internal]; @@ -264,24 +341,11 @@ class ff_dsenderHMPI : public ff_dsenderMPI { internalRanks.push_back(rank); else ranks.push_back(rank); - rankCounters[rank] = isInternal ? internalMessageOTF: messageOTF; - /*batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { - - if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ - error("Errore waiting ack from socket inside the callback\n"); - return false; - } - if (writevn(sck, v, size) < 0){ - perror("Writevn: "); - error("Error sending the iovector inside the callback!\n"); - return false; - } - - this->socketsCounters[sck]--; - - return true; - })); // change with the correct size*/ + std::vector appo; + for(int i = 0; i < (isInternal ? internalMessageOTF : messageOTF); i++) appo.push_back(batchSize == 1 ? new directBatchBuffer(rank) : new batchBuffer(batchSize, rank)); + buffers.emplace(std::make_pair(rank, std::make_pair(0, std::move(appo)))); + if (handshakeHandler(rank, isInternal) < 0) return -1; } @@ -302,9 +366,10 @@ class ff_dsenderHMPI : public ff_dsenderMPI { rank = getMostFilledInternalBufferRank(); - // boh!! - //batchBuffers[rank].push(task); - sendToRank(rank, task); + auto& buffs = buffers[rank]; + if (buffs.second[buffs.first]->push(task)) // the push triggered a flush, so we must go ion the next buffer + buffs.first = (buffs.first + 1) % buffs.second.size(); // increment the used buffer of 1 + return this->GO_ON; } @@ -316,33 +381,25 @@ class ff_dsenderHMPI : public ff_dsenderMPI { // send the EOS to all the internal connections if (squareBoxEOS) return; squareBoxEOS = true; - long header[3] = {0,0,0}; - for(const auto&rank : internalRanks) - MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); - + for(const auto&rank : internalRanks){ + auto& buffs = buffers[rank]; + buffs.second[buffs.first]->pushEOS(); + } } if (++neos >= this->get_num_inchannels()) { // all input EOS received, now sending the EOS to all // others connections - long header[3] = {0,0,0}; - for(const auto& rank : ranks) - MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); - + for(const auto& rank : ranks){ + auto& buffs = buffers[rank]; + buffs.second[buffs.first]->pushEOS(); + } } } - void svc_end() { - // here we wait all acks from all connections - long totalack = ranks.size() * messageOTF + internalRanks.size() * internalMessageOTF; - long currack = 0; - - for(const auto& pair : rankCounters) currack += pair.second; - - while(currackwaitCompletion(); + } }; diff --git a/tests/distributed/Makefile b/tests/distributed/Makefile index 485976f8..28c773f5 100644 --- a/tests/distributed/Makefile +++ b/tests/distributed/Makefile @@ -29,6 +29,9 @@ ifdef DEBUG else OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG endif +ifdef DISABLE_FF_DISTRIBUTED + CXXFLAGS += -DDISABLE_FF_DISTRIBUTED +endif ifdef EXCLUDE_BLOCKING CXXFLAGS += -DDFF_EXCLUDE_BLOCKING endif From 80c77a6d04db66dc172d21691e6758ec352dca36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 23 May 2022 12:22:28 +0200 Subject: [PATCH 164/202] Added missing file node.hpp to the previous commit --- ff/node.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ff/node.hpp b/ff/node.hpp index bf878cab..6dabfc33 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -61,9 +61,9 @@ namespace ff { -#ifdef DFF_ENABLED +// distributed rts related type, but always defined struct GroupInterface; -#endif + static void* FF_EOS = (void*)(ULLONG_MAX); /// automatically propagated static void* FF_EOS_NOFREEZE = (void*)(ULLONG_MAX-1); /// not automatically propagated @@ -1298,8 +1298,9 @@ class ff_node { virtual decltype(serializeF) getSerializationFunction(){return serializeF;} virtual decltype(deserializeF) getDeserializationFunction(){return deserializeF;} - GroupInterface createGroup(std::string); #endif + // always defined, the body will implement a no-op if the distributed runtime is disabled + GroupInterface createGroup(std::string); protected: From 87ccf0b53e26ab93828903c06b5c21b7a75aebb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 23 May 2022 13:06:24 +0200 Subject: [PATCH 165/202] Fixed warning introduced in the last commit --- ff/distributed/ff_dreceiverMPI.hpp | 2 +- ff/distributed/ff_dsenderMPI.hpp | 6 +++--- ff/distributed/ff_network.hpp | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 4f2d09be..4e0030d2 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -108,7 +108,7 @@ class ff_dreceiverMPI: public ff_monode_t { char* buff = new char[size]; // this can be reused!! MPI_Recv(buff, size, MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); size_t head = 0; - for (size_t i = 0; i < headers[0]; i++){ + for (size_t i = 0; i < (size_t)headers[0]; i++){ size_t sz = headers[3*i+3]; char* outBuff = new char[sz]; memcpy(outBuff, buff+head, sz); diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 97bc84a8..ebba8449 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -199,7 +199,7 @@ class ff_dsenderMPI: public ff_minode_t { int getMostFilledBufferRank(){ int rankMax = -1; - int sizeMax = 0; + size_t sizeMax = 0; for(int rank : ranks){ auto& batchBB = buffers[rank]; size_t sz = batchBB.second[batchBB.first]->size(); @@ -302,8 +302,8 @@ class ff_dsenderHMPI : public ff_dsenderMPI { } int getMostFilledInternalBufferRank(){ - int rankMax = -1; - int sizeMax = 0; + int rankMax = -1; + size_t sizeMax = 0; for(int rank : internalRanks){ auto& batchBB = buffers[rank]; size_t sz = batchBB.second[batchBB.first]->size(); diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 4258ec46..7f80bb5b 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -55,6 +55,10 @@ #define htole64(x) OSSwapHostToLittleInt64(x) #define be64toh(x) OSSwapBigToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x) + + #ifndef UIO_MAXIOV + #define UIO_MAXIOV 1023 + #endif #endif enum Proto {TCP , MPI}; From 635e1fe530f4d66b477f1be862c67549c2166c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 1 Jun 2022 16:28:19 +0200 Subject: [PATCH 166/202] Deleted a print for debugging purpose on wrappers --- ff/distributed/ff_wrappers.hpp | 28 +++- tests/distributed/test_group13_feedback.cpp | 159 ++++++++++++++++++++ 2 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 tests/distributed/test_group13_feedback.cpp diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 238c3956..c5815c8a 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -48,31 +48,35 @@ class WrapperIN: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have + int feedbackChannels = 0; public: - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false): internal_mi_transformer(this, false), inchannels(inchannels) {this->n = n; this->cleanup= cleanup;} + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false): internal_mi_transformer(this, false), inchannels(inchannels){this->n = n; this->cleanup= cleanup;} void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } int svc_init() { - if (this->n->isMultiOutput()) { + if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_running(inchannels); + this->feedbackChannels = this->get_num_feedbackchannels(); } return n->svc_init(); } void * svc(void* in) { - message_t* msg = (message_t*)in; + message_t* msg = (message_t*)in; if (this->n->isMultiInput()) { + if (this->get_channel_id() < feedbackChannels) return in; int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, true); } + bool datacopied=true; void* inputData = this->n->deserializeF(msg->data, datacopied); if (!datacopied) msg->data.doNotCleanup(); @@ -95,9 +99,11 @@ class WrapperOUT: public internal_mo_transformer { int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; int myID; + int feedbackChannels; + int senderChannel = 0; public: - WrapperOUT(ff_node* n, int id, int outchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id) { + WrapperOUT(ff_node* n, int id, int outchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id){ this->n = n; this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -105,14 +111,20 @@ class WrapperOUT: public internal_mo_transformer { bool serialize(void* in, int id) { if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); + + if (feedbackChannels){ + // TODO: se id==-1 round robin sui canali di feedback + if (id < feedbackChannels) return this->ff_send_out(in, id); + else id -= feedbackChannels; + } message_t* msg = new message_t; bool datacopied = this->n->serializeF(in, msg->data); - msg->sender = myID; // da cambiare con qualcosa di reale! + msg->sender = myID; msg->chid = id; if (!datacopied) msg->data.freetaskF = this->n->freetaskF; - this->ff_send_out(msg); + this->ff_send_out_to(msg, senderChannel); if (datacopied) this->n->freetaskF(in); return true; } @@ -124,6 +136,10 @@ class WrapperOUT: public internal_mo_transformer { } int svc_init() { + // save the channel id fo the sender, useful for when there are feedbacks in the application + this->feedbackChannels = this->get_num_feedbackchannels(); + senderChannel = this->get_num_outchannels() - 1; + if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); mo->set_running(outchannels); diff --git a/tests/distributed/test_group13_feedback.cpp b/tests/distributed/test_group13_feedback.cpp new file mode 100644 index 00000000..44a71c66 --- /dev/null +++ b/tests/distributed/test_group13_feedback.cpp @@ -0,0 +1,159 @@ +/* + * |-> Node31 -->| + * |-> Node21 -->| | + * Node1---->| |-> Node32 -->| ----> Node4 + * |-> Node22 -->| | + * |-> Node33 -->| + * + * /<--------- a2a0 ---------->/ + * /<--------------------- pipe --------------------->/ + * + * G1: pipe0 + * G2: a2a0 + * G3: pipe1 + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + myTask_t() {} + myTask_t(myTask_t* t){ + str = std::string(t->str); + S.t = t->S.t; + S.f = t->S.f; + } + + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct FdbInput : ff_minode_t{ + int feedbacks, feedbackTasks = 0; + + FdbInput(int feedbacks) : feedbacks(feedbacks) {} + + myTask_t * svc(myTask_t* t){ + if (fromInput()) return t; + feedbackTasks++; + ff::cout << "Received from feeback channel!\n"; + ff_send_out(t); + return (feedbackTasks == feedbacks ? this->EOS : this->GO_ON); + } +}; + + +struct Node2: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + return t; + } +}; + +struct FdbOutput : ff_monode_t{ + int feedbacks; + FdbOutput(int feedbacks) : feedbacks(feedbacks) {} + myTask_t* svc(myTask_t* t){ + if (t->str == "Hello World"){ + t->str = "Feedback!"; + for(int i = 0; i < feedbacks; i++) ff_send_out_to(new myTask_t(t), i); + delete t; + return this->GO_ON; + } + ff_send_out_to(t, feedbacks); + return this->GO_ON; + } +}; + +struct Node3: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + return; + //abort(); + } + std::cout << "RESULT OK\n"; + } + long ntasks; + long processed=0; +}; + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 1000; + if (argc>1) { + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n21, n22; + Node3 n31, n32, n33; + Node4 n4(ntasks*2); + ff_a2a a2a; + a2a.add_firstset({new ff_comb(new FdbInput(3), &n21),new ff_comb(new FdbInput(3), &n22)}); + a2a.add_secondset({new ff_comb(&n31,new FdbOutput(2)), new ff_comb( &n32, new FdbOutput(2)), new ff_comb(&n33, new FdbOutput(2))}); + a2a.wrap_around(); + + pipe.add_stage(&n1); + pipe.add_stage(&a2a); + pipe.add_stage(&n4); + + //----- defining the distributed groups ------ + + auto G1 = n1.createGroup("G1"); + auto G2 = a2a.createGroup("G2"); + auto G3 = n4.createGroup("G3"); + + // ------------------------------------------- + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} From 6a58c9f1c7219cb87f6d5752df417c2c19550133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 1 Jun 2022 17:58:35 +0200 Subject: [PATCH 167/202] Fixed MPI bug --- ff/distributed/ff_dreceiverMPI.hpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 4e0030d2..95f7586e 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -103,13 +103,19 @@ class ff_dreceiverMPI: public ff_monode_t { this->forward(out, status.MPI_SOURCE); } else { int size; - MPI_Probe(status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, &status); - MPI_Get_count(&status, MPI_BYTE, &size); + MPI_Status localStatus; + MPI_Probe(status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, &localStatus); + MPI_Get_count(&localStatus, MPI_BYTE, &size); char* buff = new char[size]; // this can be reused!! - MPI_Recv(buff, size, MPI_BYTE, status.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + MPI_Recv(buff, size, MPI_BYTE, localStatus.MPI_SOURCE, DFF_TASK_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); size_t head = 0; for (size_t i = 0; i < (size_t)headers[0]; i++){ size_t sz = headers[3*i+3]; + if (sz == 0){ + registerEOS(status.MPI_SOURCE); + assert(i+1 == (size_t)headers[0]); + break; + } char* outBuff = new char[sz]; memcpy(outBuff, buff+head, sz); head += sz; From 120013f0f7c3e33dc0cacadb65e78037e7b496ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 1 Jun 2022 18:39:17 +0200 Subject: [PATCH 168/202] New version of test_group13_feedback --- ff/distributed/ff_wrappers.hpp | 16 ++++++++-------- tests/distributed/test_group13_feedback.cpp | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index c5815c8a..7e423830 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -71,10 +71,10 @@ class WrapperIN: public internal_mi_transformer { message_t* msg = (message_t*)in; if (this->n->isMultiInput()) { - if (this->get_channel_id() < feedbackChannels) return in; int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); - mi->set_input_channelid(channelid, true); + mi->set_input_channelid(channelid, fromInput()); + if (!this->fromInput()) return n->svc(in); } bool datacopied=true; @@ -129,12 +129,6 @@ class WrapperOUT: public internal_mo_transformer { return true; } - void * svc(void* in) { - void* out = n->svc(in); - serialize(out, defaultDestination); - return GO_ON; - } - int svc_init() { // save the channel id fo the sender, useful for when there are feedbacks in the application this->feedbackChannels = this->get_num_feedbackchannels(); @@ -148,6 +142,12 @@ class WrapperOUT: public internal_mo_transformer { return n->svc_init(); } + void * svc(void* in) { + void* out = n->svc(in); + serialize(out, defaultDestination); + return GO_ON; + } + void svc_end(){n->svc_end();} int run(bool skip_init=false) { diff --git a/tests/distributed/test_group13_feedback.cpp b/tests/distributed/test_group13_feedback.cpp index 44a71c66..8f795023 100644 --- a/tests/distributed/test_group13_feedback.cpp +++ b/tests/distributed/test_group13_feedback.cpp @@ -1,9 +1,9 @@ /* - * |-> Node31 -->| - * |-> Node21 -->| | - * Node1---->| |-> Node32 -->| ----> Node4 - * |-> Node22 -->| | - * |-> Node33 -->| + * |-> Node31 FdbOutput -->| + * |-> FdbInput Node21 -->| | + * Node1---->| |-> Node32 FdbOutput -->| ----> Node4 + * |-> FdbInput Node22 -->| | + * |-> Node33 FdbOutput -->| * * /<--------- a2a0 ---------->/ * /<--------------------- pipe --------------------->/ @@ -124,7 +124,7 @@ int main(int argc, char*argv[]){ error("DFF_Init\n"); return -1; } - long ntasks = 1000; + long ntasks = 1; if (argc>1) { ntasks = std::stol(argv[1]); } @@ -135,8 +135,8 @@ int main(int argc, char*argv[]){ Node3 n31, n32, n33; Node4 n4(ntasks*2); ff_a2a a2a; - a2a.add_firstset({new ff_comb(new FdbInput(3), &n21),new ff_comb(new FdbInput(3), &n22)}); - a2a.add_secondset({new ff_comb(&n31,new FdbOutput(2)), new ff_comb( &n32, new FdbOutput(2)), new ff_comb(&n33, new FdbOutput(2))}); + a2a.add_firstset({new ff_comb(new FdbInput(1), &n21)}); + a2a.add_secondset({new ff_comb(&n31,new FdbOutput(1))}); a2a.wrap_around(); pipe.add_stage(&n1); From 70b6fbb7b8855161abfc3663a3038c5bce6a8e85 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 6 Jun 2022 11:58:22 +0200 Subject: [PATCH 169/202] sorted out some bugs related to (local) feedback channels management for the distributed version (as well as shared-memory one) --- ff/all2all.hpp | 169 ++--- ff/distributed/ff_batchbuffer.hpp | 1 + ff/distributed/ff_dadapters.hpp | 2 +- ff/distributed/ff_dgroup.hpp | 34 +- ff/distributed/ff_dsenderMPI.hpp | 4 + ff/distributed/ff_wrappers.hpp | 46 +- ff/distributed/loader/dff_run.cpp | 30 +- ff/gt.hpp | 14 +- ff/lb.hpp | 21 +- ff/multinode.hpp | 75 +- ff/node.hpp | 10 +- ff/pipeline.hpp | 812 +++++++++++----------- tests/distributed/test_group13.cpp | 6 +- tests/distributed/test_group16.cpp | 6 +- tests/distributed/test_group17.cpp | 59 +- tests/distributed/test_group17.json | 8 +- tests/distributed/test_group21.cpp | 206 ++++++ tests/distributed/test_group21.json | 16 + tests/distributed/test_group22.cpp | 207 ++++++ tests/distributed/test_group3.cpp | 2 +- tests/distributed/test_parametricPerf.cpp | 2 +- tests/test_all-to-all11.cpp | 20 +- tests/test_changesize2.cpp | 2 +- tests/test_multi_input11.cpp | 2 +- 24 files changed, 1169 insertions(+), 585 deletions(-) create mode 100644 tests/distributed/test_group21.cpp create mode 100644 tests/distributed/test_group21.json create mode 100644 tests/distributed/test_group22.cpp diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 23e227d4..84bd32a6 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -57,6 +57,88 @@ class ff_a2a: public ff_node { } inline int prepare() { + /* ----------------------- */ + if (wraparound) { + if (workers2[0]->isMultiOutput()) { // NOTE: we suppose that all others are the same + if (workers1[0]->isMultiInput()) { // NOTE: we suppose that all others are the same + for(size_t i=0;iset_id(i); + internalSupportNodes.push_back(t); + workers2[i]->set_output_feedback(t); + workers1[j]->set_input_feedback(t); + } + } + } else { + // the cardinatlity of the first and second set of workers must be the same + if (workers1.size() != workers2.size()) { + error("A2A, wrap_around, the workers of the second set are not multi-output nodes so the cardinatlity of the first and second set must be the same\n"); + return -1; + } + + if (create_input_buffer(in_buffer_entries, false) <0) { + error("A2A, error creating input buffers\n"); + return -1; + } + + for(size_t i=0;iset_output_feedback(workers1[i]); + + } + } else { + // the cardinatlity of the first and second set of workers must be the same + if (workers1.size() != workers2.size()) { + error("A2A, wrap_around, the workers of the second set are not multi-output nodes so the cardinatlity of the first and second set must be the same\n"); + return -1; + } + if (!workers1[0]->isMultiInput()) { // we suppose that all others are the same + if (create_input_buffer(in_buffer_entries, false) <0) { + error("A2A, error creating input buffers\n"); + return -1; + } + + for(size_t i=0;iset_output_buffer(workers1[i]->get_in_buffer()); + + } else { + if (create_output_buffer(out_buffer_entries, false) <0) { + error("A2A, error creating output buffers\n"); + return -1; + } + + for(size_t i=0;iset_input_feedback(workers2[i])<0) { + error("A2A, wrap_around, the nodes of the first set are not multi-input\n"); + return -1; + } + } + } + + // blocking stuff -------------------------------------------- + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + for(size_t i=0;iinit_output_blocking(m,c)) return -1; + } + if (!workers2[0]->isMultiOutput()) { + assert(workers1.size() == workers2.size()); + } + if (workers1[0]->isMultiInput()) { + for(size_t i=0;iinit_input_blocking(m,c)) return -1; + } + } else { + assert(workers1.size() == workers2.size()); + for(size_t i=0;iinit_input_blocking(m,c)) return -1; + workers2[i]->set_output_blocking(m,c); + } + } + // ----------------------------------------------------------- + } // wraparound + + if (workers1[0]->isFarm() || workers1[0]->isAll2All()) { error("A2A, nodes of the first set cannot be farm or all-to-all\n"); return -1; @@ -562,89 +644,9 @@ class ff_a2a: public ff_node { * The last stage output stream will be connected to the first stage * input stream in a cycle (feedback channel) */ - int wrap_around() { - - if (workers2[0]->isMultiOutput()) { // NOTE: we suppose that all others are the same - if (workers1[0]->isMultiInput()) { // NOTE: we suppose that all others are the same - for(size_t i=0;iset_id(i); - internalSupportNodes.push_back(t); - workers2[i]->set_output_feedback(t); - workers1[j]->set_input_feedback(t); - } - } - } else { - // the cardinatlity of the first and second set of workers must be the same - if (workers1.size() != workers2.size()) { - error("A2A, wrap_around, the workers of the second set are not multi-output nodes so the cardinatlity of the first and second set must be the same\n"); - return -1; - } - - if (create_input_buffer(in_buffer_entries, false) <0) { - error("A2A, error creating input buffers\n"); - return -1; - } - - for(size_t i=0;iset_output_feedback(workers1[i]); - - } - skipfirstpop(true); - } else { - // the cardinatlity of the first and second set of workers must be the same - if (workers1.size() != workers2.size()) { - error("A2A, wrap_around, the workers of the second set are not multi-output nodes so the cardinatlity of the first and second set must be the same\n"); - return -1; - } - if (!workers1[0]->isMultiInput()) { // we suppose that all others are the same - if (create_input_buffer(in_buffer_entries, false) <0) { - error("A2A, error creating input buffers\n"); - return -1; - } - - for(size_t i=0;iset_output_buffer(workers1[i]->get_in_buffer()); - - } else { - if (create_output_buffer(out_buffer_entries, false) <0) { - error("A2A, error creating output buffers\n"); - return -1; - } - - for(size_t i=0;iset_input_feedback(workers2[i])<0) { - error("A2A, wrap_around, the nodes of the first set are not multi-input\n"); - return -1; - } - } - } - - // blocking stuff -------------------------------------------- - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - for(size_t i=0;iinit_output_blocking(m,c)) return -1; - } - if (!workers2[0]->isMultiOutput()) { - assert(workers1.size() == workers2.size()); - } - if (workers1[0]->isMultiInput()) { - for(size_t i=0;iinit_input_blocking(m,c)) return -1; - } - } else { - assert(workers1.size() == workers2.size()); - for(size_t i=0;iinit_input_blocking(m,c)) return -1; - workers2[i]->set_output_blocking(m,c); - } - } - // ----------------------------------------------------------- - return 0; - } - + int wrap_around() { wraparound=true; return 0;} + bool isset_wraparound() { return wraparound; } + /* WARNING: if these methods are called after prepare (i.e. after having called * run_and_wait_end/run_then_freeze/run/....) they have no effect. * @@ -810,6 +812,7 @@ class ff_a2a: public ff_node { bool workers1_cleanup=false; bool workers2_cleanup=false; bool prepared, fixedsizeIN, fixedsizeOUT; + bool wraparound=false; int in_buffer_entries, out_buffer_entries; int ondemand_chunk=0; svector workers1; // first set, nodes must be multi-output diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp index 82af4679..c77d5b96 100644 --- a/ff/distributed/ff_batchbuffer.hpp +++ b/ff/distributed/ff_batchbuffer.hpp @@ -53,6 +53,7 @@ class ff_batchBuffer { } int flush(){ + if (size == 0) return 0; int size_ = size; diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index 4d873064..5cfe5f3c 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -77,7 +77,7 @@ class EmitterAdapter: public internal_mo_transformer { if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); //mo->set_running(localWorkersMap.size() + 1); // the last worker is the forwarder to the remote workers - mo->set_running(totalWorkers); + mo->set_virtual_outchannels(totalWorkers); } // change the size of the queue to the SquareBoxRight (if present), diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index b502ceb4..9201e19f 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -107,14 +107,27 @@ class dGroup : public ff::ff_farm { child = inputs[0]; if (isSeq(child)){ - if (ir.hasReceiver && ir.hasSender) - workers.push_back(new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes))); - else if (ir.hasReceiver) - workers.push_back(buildWrapperIN(child)); - else workers.push_back(buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels)); - + ff_node* wrapper = nullptr; + if (ir.hasReceiver && ir.hasSender) { + wrapper = new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes)); + workers.push_back(wrapper); + } else if (ir.hasReceiver) { + wrapper = buildWrapperIN(child); + workers.push_back(wrapper); + } else { + wrapper = buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels); + workers.push_back(wrapper); + } + // TODO: in case there are feedback channels we cannot skip all pops! + if (ir.isSource) + wrapper->skipallpop(true); } else { - if (ir.hasReceiver){ + + // TODO: in case there are feedback channels we cannot skip all pops! + if (ir.isSource) + child->skipallpop(true); + + if (ir.hasReceiver){ for(ff_node* input : inputs){ ff_node* inputParent = getBB(child, input); if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); //cleanup?? removefromcleanuplist?? @@ -170,8 +183,8 @@ class dGroup : public ff::ff_farm { if (isSeq(child)) if (ir.isSource){ ff_node* wrapped = new EmitterAdapter(child, ir.rightTotalInputs, getBackAndPop(reverseLeftOutputIndexes) , localRightWorkers); - if (ir.hasReceiver) - wrapped->skipallpop(true); + //if (ir.hasReceiver) + wrapped->skipallpop(true); firstSet.push_back(wrapped); } else { auto d = child->getDeserializationFunction(); @@ -180,7 +193,8 @@ class dGroup : public ff::ff_farm { else { if (ir.isSource){ - if (ir.hasReceiver) child->skipallpop(true); + //if (ir.hasReceiver) + child->skipallpop(true); } else { ff::svector inputs; child->get_in_nodes(inputs); for(ff_node* input : inputs){ diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index ebba8449..72b8f528 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -96,6 +96,10 @@ class ff_dsenderMPI: public ff_minode_t { } } int push(message_t* m){ + + std::cerr << "directBatchBuffer PUSH\n"; + + waitCompletion(); currHeader[1] = m->sender; currHeader[2] = m->chid; currHeader[3] = m->data.getLen(); MPI_Isend(currHeader, 4, MPI_LONG, this->rank, DFF_HEADER_TAG, MPI_COMM_WORLD, &this->headersR); diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 7e423830..1179f039 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -52,7 +52,10 @@ class WrapperIN: public internal_mi_transformer { public: - WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false): internal_mi_transformer(this, false), inchannels(inchannels){this->n = n; this->cleanup= cleanup;} + WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false): internal_mi_transformer(this, false), inchannels(inchannels){ + this->n = n; + this->cleanup= cleanup; + } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); @@ -61,8 +64,7 @@ class WrapperIN: public internal_mi_transformer { int svc_init() { if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); - mi->set_running(inchannels); - this->feedbackChannels = this->get_num_feedbackchannels(); + mi->set_running(get_num_feedbackchannels()+1); } return n->svc_init(); } @@ -100,22 +102,23 @@ class WrapperOUT: public internal_mo_transformer { int defaultDestination; int myID; int feedbackChannels; - int senderChannel = 0; public: - WrapperOUT(ff_node* n, int id, int outchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id){ + WrapperOUT(ff_node* n, int id, int outchannels=-1, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id){ this->n = n; this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); } bool serialize(void* in, int id) { - if ((void*)in > FF_TAG_MIN) return this->ff_send_out(in); - if (feedbackChannels){ - // TODO: se id==-1 round robin sui canali di feedback - if (id < feedbackChannels) return this->ff_send_out(in, id); - else id -= feedbackChannels; + if (id < feedbackChannels) { + if (id == -1) return ff_send_out(in); + return ff_send_out_to(in, id); + } + // from 0 to feedbackChannels-1 are feedback channels + // from feedbackChannels to outchannels-1 are forward channels + id -= feedbackChannels; } message_t* msg = new message_t; @@ -124,26 +127,33 @@ class WrapperOUT: public internal_mo_transformer { msg->sender = myID; msg->chid = id; if (!datacopied) msg->data.freetaskF = this->n->freetaskF; - this->ff_send_out_to(msg, senderChannel); + if (feedbackChannels) { + // all forward channels are multiplexed in feedbackChannels + ff_send_out_to(msg, feedbackChannels); + } else + ff_send_out(msg); if (datacopied) this->n->freetaskF(in); return true; } int svc_init() { // save the channel id fo the sender, useful for when there are feedbacks in the application - this->feedbackChannels = this->get_num_feedbackchannels(); - senderChannel = this->get_num_outchannels() - 1; + + // these are local feedback channels + feedbackChannels = internal_mo_transformer::get_num_feedbackchannels(); if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); - mo->set_running(outchannels); + mo->set_virtual_outchannels(outchannels); + mo->set_virtual_feedbackchannels(-1); } return n->svc_init(); } void * svc(void* in) { - void* out = n->svc(in); + void* out = n->svc(in); + if (out > FF_TAG_MIN) return out; serialize(out, defaultDestination); return GO_ON; } @@ -153,6 +163,12 @@ class WrapperOUT: public internal_mo_transformer { int run(bool skip_init=false) { return internal_mo_transformer::run(skip_init); } + + + /** returns the total number of output channels */ + size_t get_num_outchannels() const { return outchannels; } + size_t get_num_feedbackchannels() const { return 0; } // TODO <--------- + ff::ff_node* getOriginal(){return this->n;} diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index f81b7d2b..cde238c4 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -28,8 +28,10 @@ #include #include #include +#include #include + #include #include #include @@ -41,19 +43,20 @@ namespace n_fs = std::filesystem; enum Proto {TCP = 1 , MPI}; -static inline unsigned long getusec() { - struct timeval tv; - gettimeofday(&tv,NULL); - return (unsigned long)(tv.tv_sec*1e6+tv.tv_usec); -} - Proto usedProtocol; bool seeAll = false; std::vector viewGroups; -char hostname[100]; +char hostname[HOST_NAME_MAX]; std::string configFile(""); std::string executable; + +static inline unsigned long getusec() { + struct timeval tv; + gettimeofday(&tv,NULL); + return (unsigned long)(tv.tv_sec*1e6+tv.tv_usec); +} + bool toBePrinted(std::string gName){ return (seeAll || (find(viewGroups.begin(), viewGroups.end(), gName) != viewGroups.end())); } @@ -130,6 +133,7 @@ static inline void usage(char* progname) { << "\t -v ,..., \t Prints the output of the specified groups\n" << "\t -V \t Print the output of all groups\n" << "\t -p \"TCP|MPI\" \t Force communication protocol\n"; + std::cout << "\n"; } @@ -148,13 +152,17 @@ std::string generateHostFile(std::vector& parsedGroups){ int main(int argc, char** argv) { - if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-help") == 0 || strcmp(argv[0], "-h") == 0){ + if (argc == 1 || + strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "-h") == 0){ usage(argv[0]); exit(EXIT_SUCCESS); } // get the hostname - gethostname(hostname, 100); + if (gethostname(hostname, HOST_NAME_MAX) != 0) { + perror("gethostname"); + exit(EXIT_FAILURE); + } int optind=0; for(int i=1;iget_num_outchannels(); + return (buffer?1:0); + } + + size_t get_num_feedbackchannels() const { + return feedbackid; + } + /** * \brief Gets the number of worker threads currently running. * @@ -556,8 +567,7 @@ class ff_gatherer: public ff_thread { bool outpresent = (buffer != NULL); bool skipfirstpop = skip1pop; - // the following case is possible when the collector is a dnode or there is a - // filter that is a composition + // the following case is possible when the there is a filter that is a composition if ( filter && ( (filter->get_out_buffer()!=NULL) || filter->isMultiOutput() ) ) { diff --git a/ff/lb.hpp b/ff/lb.hpp index 2c362144..6f33a913 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -483,7 +483,7 @@ class ff_loadbalancer: public ff_thread { * \param max_num_workers The max number of workers allowed */ ff_loadbalancer(size_t max_num_workers): - running(-1),max_nworkers(max_num_workers),nextw(-1),feedbackid(-1), + running(-1),max_nworkers(max_num_workers),nextw(-1),feedbackid(0), channelid(-2),input_channelid(-1), filter(NULL),workers(max_num_workers), buffer(NULL),skip1pop(false),master_worker(false),parallel_workers(false), @@ -609,6 +609,17 @@ class ff_loadbalancer: public ff_thread { */ ssize_t get_channel_id() const { return channelid;} + size_t get_num_inchannels() const { + size_t nw=multi_input.size(); + if (manager) nw +=1; + if (multi_input.size()==0 && (get_in_buffer()!=NULL)) nw+=1; + return nw; + } + size_t get_num_outchannels() const { return workers.size(); } + size_t get_num_feedbackchannels() const { + return feedbackid; + } + /** * \brief Resets the channel id * @@ -890,12 +901,10 @@ class ff_loadbalancer: public ff_thread { bool inpresent = (get_in_buffer() != NULL); bool skipfirstpop = skip1pop; - // the following case is possible when the emitter is a dnode if (!inpresent && filter && (filter->get_in_buffer()!=NULL)) { inpresent = true; set_in_buffer(filter->get_in_buffer()); } - gettimeofday(&wtstart,NULL); if (!master_worker && (multi_input.size()==0) && (inputNodesFeedback.size()==0)) { @@ -1135,10 +1144,14 @@ class ff_loadbalancer: public ff_thread { } int dryrun() { + // if there are feedback channels, we want ff_send_out will do + // a round-robin on those channels, whereas ff_send_out_to could be + // used to target forward channels + // by setting running=feedbackid, then selectworkers will skip forward channels if (feedbackid>0) running = feedbackid; else - running=workers.size(); + running = workers.size(); if (filter) { // WARNING: If the last node of a composition is a multi-output node, then the diff --git a/ff/multinode.hpp b/ff/multinode.hpp index 1ad6f765..deb110f6 100644 --- a/ff/multinode.hpp +++ b/ff/multinode.hpp @@ -380,14 +380,16 @@ class ff_minode: public ff_node { */ ssize_t get_channel_id() const { return gt->get_channel_id();} - - /** - * \brief Gets the number of input channels - */ - - size_t get_num_inchannels() const { return gt->getrunning(); } - - size_t get_num_feedbackchannels() const { return inputNodesFeedback.size(); } + size_t get_num_inchannels() const { return gt->get_num_inchannels(); } + size_t get_num_outchannels() const { + if (gt->get_filter() == (ff_node*)this) + return (gt->get_out_buffer()?1:0); + + return gt->get_num_outchannels(); + } + size_t get_num_feedbackchannels() const { + return gt->get_num_feedbackchannels(); + } /** * For a multi-input node the number of EOS to receive before terminating is equal to @@ -663,10 +665,17 @@ class ff_monode: public ff_node { } #ifdef DFF_ENABLED - inline void skipallpop(bool sk) { + void skipallpop(bool sk) { lb->skipallpop(sk); ff_node::skipallpop(sk); } + void set_virtual_outchannels(int n){ + noutchannels=n; + } + void set_virtual_feedbackchannels(int n) { + nfeedbackchannels=n; + } + #endif /** @@ -776,9 +785,20 @@ class ff_monode: public ff_node { */ ssize_t get_channel_id() const { return lb->get_channel_id();} - size_t get_num_feedbackchannels() const { return outputNodesFeedback.size(); } - size_t get_num_outchannels() const { return lb->getnworkers(); } - + size_t get_num_feedbackchannels() const { +#ifdef DFF_ENABLED + if (nfeedbackchannels!=-1) return nfeedbackchannels; +#endif + return lb->get_num_feedbackchannels(); + } + size_t get_num_outchannels() const { +#ifdef DFF_ENABLED + if (noutchannels!=-1) return noutchannels; +#endif + return lb->get_num_outchannels(); + } + size_t get_num_inchannels() const { return lb->get_num_inchannels(); } + const struct timeval getstarttime() const { return lb->getstarttime();} const struct timeval getstoptime() const { return lb->getstoptime();} const struct timeval getwstartime() const { return lb->getwstartime();} @@ -806,6 +826,10 @@ class ff_monode: public ff_node { ff_loadbalancer* lb; bool myownlb; int ondemand=0; +#ifdef DFF_ENABLED + int noutchannels=-1; + int nfeedbackchannels=-1; +#endif svector outputNodes; svector outputNodesFeedback; svector internalSupportNodes; @@ -1044,8 +1068,10 @@ struct internal_mo_transformer: ff_monode { n=nullptr; } } + + inline int svc_init() { return n->svc_init(); } inline void* svc(void* task) { return n->svc(task);} - + inline void svc_end() { return n->svc_end(); } inline void eosnotify(ssize_t id) { n->eosnotify(id); } int create_input_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { @@ -1054,7 +1080,6 @@ struct internal_mo_transformer: ff_monode { ff_monode::getlb()->get_filter()->set_input_buffer(ff_monode::get_in_buffer()); return 0; } - void set_id(ssize_t id) { if (n) n->set_id(id); @@ -1070,6 +1095,10 @@ struct internal_mo_transformer: ff_monode { if (n) { if (!n->callback) n->registerCallback(ff_send_out_motransformer, this); + if (n->isMultiOutput()) { + assert(!n->isComp()); + n->setlb(ff_monode::getlb(), false); + } } return ff_monode::dryrun(); } @@ -1089,7 +1118,6 @@ struct internal_mo_transformer: ff_monode { return ff_monode::run(skip_init); } - static inline bool ff_send_out_motransformer(void * task, int id, unsigned long retry, unsigned long ticks, void *obj) { @@ -1134,8 +1162,9 @@ struct internal_mi_transformer: ff_minode { if (cleanup && n) delete n; } + inline int svc_init() { return n->svc_init(); } inline void* svc(void*task) { return n->svc(task); } - + inline void svc_end() { return n->svc_end(); } int set_input(const svector & w) { n->neos += w.size(); return ff_minode::set_input(w); @@ -1145,9 +1174,11 @@ struct internal_mi_transformer: ff_minode { n->neos+=1; return ff_minode::set_input(node); } +#if 0 int set_output(ff_node *node) { return ff_minode::set_output(node); } +#endif int create_output_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { if (ff_minode::getgt()->get_out_buffer()) return -1; @@ -1192,6 +1223,17 @@ struct internal_mi_transformer: ff_minode { ff_minode::set_id(id); } + int dryrun() { + if (prepared) return 0; + if (n) { + if (n->isMultiInput()) { + assert(!n->isComp()); + n->setgt(ff_minode::getgt(), false); + } + } + return ff_minode::dryrun(); + } + int run(bool skip_init=false) { assert(n); if (!prepared) { @@ -1208,6 +1250,7 @@ struct internal_mi_transformer: ff_minode { ff_minode::getgt()->get_filter()->set_id(get_my_id()); return ff_minode::run(skip_init); } + bool cleanup; ff_node *n=nullptr; }; diff --git a/ff/node.hpp b/ff/node.hpp index f0409086..2d831fde 100644 --- a/ff/node.hpp +++ b/ff/node.hpp @@ -1067,6 +1067,7 @@ class ff_node { virtual inline void get_out_nodes_feedback(svector&) {} virtual inline void get_in_nodes(svector&w) { w.push_back(this); } virtual inline void get_in_nodes_feedback(svector&) {} + /** * \brief Force ff_node-to-core pinning @@ -1286,7 +1287,14 @@ class ff_node { abort(); // to be removed, just for debugging purposes } - + + virtual inline ssize_t get_channel_id() const { return -1; } + /** returns the total number of output channels */ + virtual inline size_t get_num_outchannels() const { return 0; } + /** returns the total number of input channels */ + virtual inline size_t get_num_inchannels() const { return 0; } //(in?1:0); } + virtual inline size_t get_num_feedbackchannels() const { return 0; } //(out?1:0);} + virtual void propagateEOS(void* task=FF_EOS) { (void)task; } #ifdef DFF_ENABLED diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 11f3eb05..50620544 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -75,107 +75,423 @@ class ff_pipeline: public ff_node { friend inline int combine_with_laststage(ff_pipeline&,T*,bool); protected: - inline int prepare() { - const int nstages=static_cast(nodes_list.size()); + int prepare_wraparound() { + const int last = static_cast(nodes_list.size())-1; + + bool isa2a_first = get_node(0)->isAll2All(); + bool isa2a_last = get_lastnode()->isAll2All(); + bool isfarm_last = get_lastnode()->isFarm(); // possible cases: [captured by] // - // the current stage is a standard node (the previous stage can also be multi-output) [curr_single_standard] + // the last stage is a standard node (the first stage can also be a multi-input node) [last_single_standard] + // the first stage is a standard node (the last stage can also be a multi-output node) [first_single_standard] // - // the current stage is multi-input - // - it's a farm (the emettitore is multi-input by default) [curr_single_multiinput] - // - it's a single multi-input node [curr_single_multiinput] - // - it's all2all with standard nodes [curr_multi_standard] - // - it's all2all with multi-input nodes [curr_multi_multiinput] // - // the previous stage is a standard node [prev_single_standard] + // the last stage is a multi-output node: + // - it's a farm with a multi-output collector [last_single_multioutput] + // - it's a farm without collector and workers are standard nodes [last_multi_standard] + // - it's a farm without collector with multi-output workers [last_multi_multioutput] + // - it's a all2all with standard nodes [last_multi_standard] + // - it's a all2all with multi-output nodes [last_multi_multioutput] + // - it's a singolo nodo (comp or pipeline) with the last stage multi-output [last_single_multioutput] // - // the previous stage is a multi-output node: - // - it's a farm with a multi-output collector [prev_single_multioutput] - // - it's a farm without collector and workers are standard node [prev_multi_standard] - // - it's a farm without collector with multi-output workers [prev_multi_multioutput] - // - it's a all2all with standard nodes [prev_multi_standard] - // - it's a all2all with multi-output nodes [prev_multi_multioutput] - // - it's a single node (comp or pipeline) with the last stage multi-output [prev_single_multioutput] // + // the first stage is multi-input + // - it's a farm (the emettitore is multi-input by default) [first_single_multiinput] + // - it's a single multi-input node [first_single_multiinput] + // - it's all2all with standard nodes [first_multi_standard] + // - its' all2all with multi-input nodes [first_multi_multiinput] // - for(int i=1;iisAll2All(); - const bool curr_single_standard = (!nodes_list[i]->isMultiInput()); - // the farm is considered single_multiinput - const bool curr_single_multiinput = (!isa2a_curr && nodes_list[i]->isMultiInput()); - const bool curr_multi_multiinput = [&]() { - if (!isa2a_curr) return false; - const svector& w1=isa2a_getfirstset(get_node(i)); + + bool last_isfarm_nocollector = get_lastnode()->isFarm() && !isfarm_withcollector(get_lastnode()); + bool last_isfarm_withcollector = get_lastnode()->isFarm() && !last_isfarm_nocollector; + + bool first_single_standard = (!nodes_list[0]->isMultiInput()); + // the farm is considered single_multiinput + bool first_single_multiinput = (nodes_list[0]->isMultiInput() && !isa2a_first); + bool first_multi_standard = [&]() { + if (!isa2a_first) return false; + const svector& w1=isa2a_getfirstset(get_node(0)); + assert(w1.size()>0); + if (w1[0]->isMultiInput()) return false; // NOTE: we suppose homogeneous first set + return true; + }(); + bool first_multi_multiinput = [&]() { + if (!isa2a_first) return false; + const svector& w1=isa2a_getfirstset(get_node(0)); + assert(w1.size()>0); + if (w1[0]->isMultiInput()) return true; // NOTE: we suppose homogeneous first set + return false; + } (); + + bool last_single_standard = (!nodes_list[last]->isMultiOutput()); + bool last_single_multioutput = ((nodes_list[last]->isMultiOutput() && !isa2a_last && !isfarm_last) || + (last_isfarm_withcollector && nodes_list[last]->isMultiOutput())); + bool last_multi_standard = [&]() { + if (last_isfarm_nocollector) { + return !isfarm_multimultioutput(get_lastnode()); + } if (isa2a_last) { + const svector& w1=isa2a_getsecondset(nodes_list[last]); assert(w1.size()>0); - for(size_t k=0;k w2(1); - w1[k]->get_in_nodes(w2); - for(size_t j=0;jisMultiInput()) return true; - } - return false; - } (); - const bool curr_multi_standard = [&]() { - if (!isa2a_curr) return false; - return !curr_multi_multiinput; - }(); - const bool isa2a_prev = get_node_last(i-1)->isAll2All(); - const bool isfarm_prev = get_node_last(i-1)->isFarm(); - const bool prev_isfarm_nocollector = (isfarm_prev && !get_node_last(i-1)->isOFarm() && !(isfarm_withcollector(get_node_last(i-1)))); - const bool prev_isfarm_withcollector = isfarm_withcollector(get_node_last(i-1)); - - - const bool prev_single_standard = (!get_node_last(i-1)->isMultiOutput()); - const bool prev_single_multioutput = ((get_node_last(i-1)->isMultiOutput() && !isa2a_prev && !isfarm_prev) || - (prev_isfarm_withcollector && get_node_last(i-1)->isMultiOutput())); - const bool prev_multi_standard = [&]() { - if (prev_isfarm_nocollector) { - svector w1; - nodes_list[i-1]->get_out_nodes(w1); - if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers - } if (isa2a_prev) { - const svector& w1=isa2a_getsecondset(get_node_last(i-1)); - assert(w1.size()>0); - if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers - } - return false; - } (); - const bool prev_multi_multioutput = [&]() { - if (prev_isfarm_nocollector) { - svector w1; - nodes_list[i-1]->get_out_nodes(w1); - if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers - } if (isa2a_prev) { - const svector& w1=isa2a_getsecondset(get_node_last(i-1)); - assert(w1.size()>0); - if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers + if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous second set + } + return false; + } (); + bool last_multi_multioutput = [&]() { + if (last_isfarm_nocollector) { + return isfarm_multimultioutput(get_lastnode()); + } if (isa2a_last) { + const svector& w1=isa2a_getsecondset(nodes_list[last]); + assert(w1.size()>0); + if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous second set + } + return false; + } (); + + // first stage: standard node + if (first_single_standard) { + if (create_input_buffer(out_buffer_entries, false)<0) return -1; + if (last_single_standard) { + if (set_output_buffer(get_in_buffer())<0) return -1; + + // blocking stuff ------ + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!nodes_list[last]->init_output_blocking(m,c)) return -1; + if (!nodes_list[0]->init_input_blocking(m,c)) return -1; + nodes_list[last]->set_output_blocking(m,c); + // --------------------- + } else { + if (last_single_multioutput) { + ff_node *t = new ff_buffernode(last, get_in_buffer(), get_in_buffer()); + internalSupportNodes.push_back(t); + assert(t); + nodes_list[last]->set_output_feedback(t); + + // blocking stuff ------ + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!nodes_list[last]->init_output_blocking(m,c)) return -1; + if (!nodes_list[0]->init_input_blocking(m,c)) return -1; + // NOTE: if the node0 is multi-input, then the init_input_blocking + // method already calls set_output_blocking + t->set_output_blocking(m,c); + // --------------------- + + } else { + error("PIPE, cannot connect stage %d with node %d\n", last, 0); + return -1; } - return false; - } (); - - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (curr_single_standard) { - bool skip_set_output_blocking = false; - if (nodes_list[i]->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; - if (prev_single_standard) { - if (nodes_list[i-1]->set_output_buffer(nodes_list[i]->get_in_buffer())<0) return -1; + } + nodes_list[0]->skipfirstpop(true); + } + // first stage: multi-input + if (first_single_multiinput) { + + if (last_single_standard) { + if (nodes_list[last]->create_output_buffer(out_buffer_entries,false)<0) return -1; + nodes_list[0]->set_input_feedback(nodes_list[last]); + + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!nodes_list[last]->init_output_blocking(m,c)) return -1; + // multi-input nodes execute set_output_blocking + if (!nodes_list[0]->init_input_blocking(m,c)) return -1; + // --------------------- + } else { + if (last_single_multioutput) { + ff_node *t = new ff_buffernode(out_buffer_entries,false, last); + assert(t); + internalSupportNodes.push_back(t); + nodes_list[last]->set_output_feedback(t); + nodes_list[0]->set_input_feedback(t); + + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!nodes_list[last]->init_output_blocking(m,c)) return -1; + // multi-input nodes execute set_output_blocking + if (!nodes_list[0]->init_input_blocking(m,c)) return -1; + // --------------------- } else { - skip_set_output_blocking = true; - // WARNING: here we add as output node of the previous stage the - // current node and not a buffer-node. - if (prev_multi_standard || prev_multi_multioutput) { - svector w(1); - nodes_list[i-1]->get_out_nodes(w); - if (w.size()>1) { - error("PIPE, cannot connect stage %d with stage %d\n", i-1, i); + if (last_multi_standard) { + if (nodes_list[last]->create_output_buffer(out_buffer_entries, false) <0) return -1; - } - nodes_list[i-1]->set_output(nodes_list[i]); + svector w(MAX_NUM_THREADS); + nodes_list[last]->get_out_nodes(w); + assert(w.size()); + for(size_t j=0;jset_input_feedback(w[j]); + + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!nodes_list[last]->init_output_blocking(m,c)) return -1; + // multi-input nodes execute set_output_blocking + if (!nodes_list[0]->init_input_blocking(m,c)) return -1; + // --------------------- } else { - assert(prev_single_multioutput); - nodes_list[i-1]->set_output(nodes_list[i]); + if (last_multi_multioutput) { + svector w(MAX_NUM_THREADS); + nodes_list[last]->get_out_nodes(w); + assert(w.size()); + + for(size_t j=0;jset_input_feedback(t); + w[j]->set_output_feedback(t); + } + + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!nodes_list[last]->init_output_blocking(m,c)) return -1; + // multi-input nodes execute set_output_blocking + if (!nodes_list[0]->init_input_blocking(m,c)) return -1; + // --------------------- + } else { + error("PIPE, wrap_around invalid stage\n"); + return -1; + } + } + } + } + nodes_list[0]->skipfirstpop(true); + } + // first stage: multi standard + if (first_multi_standard) { // all-to-all + assert(get_node(0)->isAll2All()); + ff_node* a2a = nodes_list[0]; + const svector& firstSet=isa2a_getfirstset(a2a); + assert(firstSet.size()>0); + + if (last_single_standard) { + error("PIPE, wrap_around, cannot connect last with first stage\n"); + return -1; + } + if (last_single_multioutput) { + for(size_t j=0;jset_input(t); + nodes_list[last]->set_output_feedback(t); + + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!firstSet[j]->init_input_blocking(m,c)) return -1; + t->set_output_blocking(m,c); + // --------------------- + } + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + nodes_list[last]->init_output_blocking(m,c); + // --------------------- + } + if (last_multi_standard) { + svector w(MAX_NUM_THREADS); + nodes_list[last]->get_out_nodes(w); + assert(w.size()); + assert(w.size() == firstSet.size()); + + if (a2a->create_input_buffer(in_buffer_entries, false)<0) return -1; + for(size_t j=0;jset_output_buffer(firstSet[j]->get_in_buffer()); + + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!a2a->init_output_blocking(m,c)) return -1; + for(size_t j=0;jinit_input_blocking(m,c)) return -1; + w[j]->set_output_blocking(m,c); + } + // --------------------- + } + if (last_multi_multioutput) { + error("PIPE, wrap_around, cannot connect last stage with first stage\n"); + return -1; + } + nodes_list[0]->skipfirstpop(true); + } + // first stage: multi multi-input + if (first_multi_multiinput) { + assert(get_node(0)->isAll2All()); + const svector& firstSet=isa2a_getfirstset(nodes_list[0]); + assert(firstSet.size()>0); + + if (last_single_standard) { + error("PIPE, wrap_around, cannot connect last with first stage\n"); + return -1; + } + if (last_single_multioutput) { + for(size_t j=0;jset_input_feedback(t); + nodes_list[last]->set_output_feedback(t); + } + } + if (last_multi_standard) { + svector w(MAX_NUM_THREADS); + nodes_list[last]->get_out_nodes(w); + assert(w.size()); + assert(w.size() == firstSet.size()); + + for(size_t i=0;iset_input_feedback(t); + w[i]->set_output(t); + } + } + if (last_multi_multioutput) { + svector w(MAX_NUM_THREADS); + nodes_list[last]->get_out_nodes(w); + assert(w.size()); + + // here we have to create all connections + for(size_t i=0;iset_input_feedback(t); + w[j]->set_output_feedback(t); + } + } + } + // blocking stuff ...... + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (!nodes_list[last]->init_output_blocking(m,c)) return -1; + if (!nodes_list[0]->init_input_blocking(m,c)) return -1; + // --------------------- + nodes_list[0]->skipfirstpop(true); + } + return 0; + } + inline int prepare() { + + if (wraparound) { + if (nodes_list.size()<2) { + error("PIPE, too few pipeline nodes\n"); + return -1; + } + if (prepare_wraparound()<0) { + error("PIPE, prepare_wraparound failed\n"); + return -1; + } + } + + const int nstages=static_cast(nodes_list.size()); + + // possible cases: [captured by] + // + // the current stage is a standard node (the previous stage can also be multi-output) [curr_single_standard] + // + // the current stage is multi-input + // - it's a farm (the emettitore is multi-input by default) [curr_single_multiinput] + // - it's a single multi-input node [curr_single_multiinput] + // - it's all2all with standard nodes [curr_multi_standard] + // - it's all2all with multi-input nodes [curr_multi_multiinput] + // + // the previous stage is a standard node [prev_single_standard] + // + // the previous stage is a multi-output node: + // - it's a farm with a multi-output collector [prev_single_multioutput] + // - it's a farm without collector and workers are standard node [prev_multi_standard] + // - it's a farm without collector with multi-output workers [prev_multi_multioutput] + // - it's a all2all with standard nodes [prev_multi_standard] + // - it's a all2all with multi-output nodes [prev_multi_multioutput] + // - it's a single node (comp or pipeline) with the last stage multi-output [prev_single_multioutput] + // + // + for(int i=1;iisAll2All(); + const bool curr_single_standard = (!nodes_list[i]->isMultiInput()); + // the farm is considered single_multiinput + const bool curr_single_multiinput = (!isa2a_curr && nodes_list[i]->isMultiInput()); + const bool curr_multi_multiinput = [&]() { + if (!isa2a_curr) return false; + const svector& w1=isa2a_getfirstset(get_node(i)); + assert(w1.size()>0); + for(size_t k=0;k w2(1); + w1[k]->get_in_nodes(w2); + for(size_t j=0;jisMultiInput()) return true; + } + return false; + } (); + const bool curr_multi_standard = [&]() { + if (!isa2a_curr) return false; + return !curr_multi_multiinput; + }(); + const bool isa2a_prev = get_node_last(i-1)->isAll2All(); + const bool isfarm_prev = get_node_last(i-1)->isFarm(); + const bool prev_isfarm_nocollector = (isfarm_prev && !get_node_last(i-1)->isOFarm() && !(isfarm_withcollector(get_node_last(i-1)))); + const bool prev_isfarm_withcollector = isfarm_withcollector(get_node_last(i-1)); + + + const bool prev_single_standard = (!get_node_last(i-1)->isMultiOutput()); + const bool prev_single_multioutput = ((get_node_last(i-1)->isMultiOutput() && !isa2a_prev && !isfarm_prev) || + (prev_isfarm_withcollector && get_node_last(i-1)->isMultiOutput())); + const bool prev_multi_standard = [&]() { + if (prev_isfarm_nocollector) { + svector w1; + nodes_list[i-1]->get_out_nodes(w1); + if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers + } if (isa2a_prev) { + const svector& w1=isa2a_getsecondset(get_node_last(i-1)); + assert(w1.size()>0); + if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers + } + return false; + } (); + const bool prev_multi_multioutput = [&]() { + if (prev_isfarm_nocollector) { + svector w1; + nodes_list[i-1]->get_out_nodes(w1); + if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers + } if (isa2a_prev) { + const svector& w1=isa2a_getsecondset(get_node_last(i-1)); + assert(w1.size()>0); + if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers + } + return false; + } (); + + pthread_mutex_t *m = NULL; + pthread_cond_t *c = NULL; + if (curr_single_standard) { + bool skip_set_output_blocking = false; + if (nodes_list[i]->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; + if (prev_single_standard) { + if (nodes_list[i-1]->set_output_buffer(nodes_list[i]->get_in_buffer())<0) return -1; + } else { + skip_set_output_blocking = true; + // WARNING: here we add as output node of the previous stage the + // current node and not a buffer-node. + if (prev_multi_standard || prev_multi_multioutput) { + svector w(1); + nodes_list[i-1]->get_out_nodes(w); + if (w.size()>1) { + error("PIPE, cannot connect stage %d with stage %d\n", i-1, i); + return -1; + } + nodes_list[i-1]->set_output(nodes_list[i]); + } else { + assert(prev_single_multioutput); + nodes_list[i-1]->set_output(nodes_list[i]); } } // blocking stuff -------------------------------------------- @@ -332,8 +648,10 @@ class ff_pipeline: public ff_node { } } } - if (curr_multi_multiinput) { - ff_node* a2a = nodes_list[i]; + // it could be an all-to-all or a pipeline containing as first stage an all-to-all + if (curr_multi_multiinput) { + ff_node* a2a = get_node(i); + assert(a2a->isAll2All()); const svector& W1 = isa2a_getfirstset(a2a); assert(W1.size()>0); @@ -715,314 +1033,8 @@ class ff_pipeline: public ff_node { * The last stage output stream will be connected to the first stage * input stream in a cycle (feedback channel) */ - int wrap_around() { - if (nodes_list.size()<2) { - error("PIPE, too few pipeline nodes\n"); - return -1; - } - const int last = static_cast(nodes_list.size())-1; - - bool isa2a_first = get_node(0)->isAll2All(); - bool isa2a_last = get_lastnode()->isAll2All(); - bool isfarm_last = get_lastnode()->isFarm(); - // possible cases: [captured by] - // - // the last stage is a standard node (the first stage can also be a multi-input node) [last_single_standard] - // the first stage is a standard node (the last stage can also be a multi-output node) [first_single_standard] - // - // - // the last stage is a multi-output node: - // - it's a farm with a multi-output collector [last_single_multioutput] - // - it's a farm without collector and workers are standard nodes [last_multi_standard] - // - it's a farm without collector with multi-output workers [last_multi_multioutput] - // - it's a all2all with standard nodes [last_multi_standard] - // - it's a all2all with multi-output nodes [last_multi_multioutput] - // - it's a singolo nodo (comp or pipeline) with the last stage multi-output [last_single_multioutput] - // - // - // the first stage is multi-input - // - it's a farm (the emettitore is multi-input by default) [first_single_multiinput] - // - it's a single multi-input node [first_single_multiinput] - // - it's all2all with standard nodes [first_multi_standard] - // - its' all2all with multi-input nodes [first_multi_multiinput] - // - - bool last_isfarm_nocollector = get_lastnode()->isFarm() && !isfarm_withcollector(get_lastnode()); - bool last_isfarm_withcollector = get_lastnode()->isFarm() && !last_isfarm_nocollector; - - bool first_single_standard = (!nodes_list[0]->isMultiInput()); - // the farm is considered single_multiinput - bool first_single_multiinput = (nodes_list[0]->isMultiInput() && !isa2a_first); - bool first_multi_standard = [&]() { - if (!isa2a_first) return false; - const svector& w1=isa2a_getfirstset(get_node(0)); - assert(w1.size()>0); - if (w1[0]->isMultiInput()) return false; // NOTE: we suppose homogeneous first set - return true; - }(); - bool first_multi_multiinput = [&]() { - if (!isa2a_first) return false; - const svector& w1=isa2a_getfirstset(get_node(0)); - assert(w1.size()>0); - if (w1[0]->isMultiInput()) return true; // NOTE: we suppose homogeneous first set - return false; - } (); - - bool last_single_standard = (!nodes_list[last]->isMultiOutput()); - bool last_single_multioutput = ((nodes_list[last]->isMultiOutput() && !isa2a_last && !isfarm_last) || - (last_isfarm_withcollector && nodes_list[last]->isMultiOutput())); - bool last_multi_standard = [&]() { - if (last_isfarm_nocollector) { - return !isfarm_multimultioutput(get_lastnode()); - } if (isa2a_last) { - const svector& w1=isa2a_getsecondset(nodes_list[last]); - assert(w1.size()>0); - if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous second set - } - return false; - } (); - bool last_multi_multioutput = [&]() { - if (last_isfarm_nocollector) { - return isfarm_multimultioutput(get_lastnode()); - } if (isa2a_last) { - const svector& w1=isa2a_getsecondset(nodes_list[last]); - assert(w1.size()>0); - if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous second set - } - return false; - } (); - - // first stage: standard node - if (first_single_standard) { - if (create_input_buffer(out_buffer_entries, false)<0) return -1; - if (last_single_standard) { - if (set_output_buffer(get_in_buffer())<0) return -1; - - // blocking stuff ------ - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!nodes_list[last]->init_output_blocking(m,c)) return -1; - if (!nodes_list[0]->init_input_blocking(m,c)) return -1; - nodes_list[last]->set_output_blocking(m,c); - // --------------------- - } else { - if (last_single_multioutput) { - ff_node *t = new ff_buffernode(last, get_in_buffer(), get_in_buffer()); - internalSupportNodes.push_back(t); - assert(t); - nodes_list[last]->set_output_feedback(t); - - // blocking stuff ------ - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!nodes_list[last]->init_output_blocking(m,c)) return -1; - if (!nodes_list[0]->init_input_blocking(m,c)) return -1; - // NOTE: if the node0 is multi-input, then the init_input_blocking - // method already calls set_output_blocking - t->set_output_blocking(m,c); - // --------------------- - - } else { - error("PIPE, cannot connect stage %d with node %d\n", last, 0); - return -1; - } - } - nodes_list[0]->skipfirstpop(true); - } - // first stage: multi-input - if (first_single_multiinput) { - - if (last_single_standard) { - if (nodes_list[last]->create_output_buffer(out_buffer_entries,false)<0) return -1; - nodes_list[0]->set_input_feedback(nodes_list[last]); - - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!nodes_list[last]->init_output_blocking(m,c)) return -1; - // multi-input nodes execute set_output_blocking - if (!nodes_list[0]->init_input_blocking(m,c)) return -1; - // --------------------- - } else { - if (last_single_multioutput) { - ff_node *t = new ff_buffernode(out_buffer_entries,false, last); - assert(t); - internalSupportNodes.push_back(t); - nodes_list[last]->set_output_feedback(t); - nodes_list[0]->set_input_feedback(t); - - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!nodes_list[last]->init_output_blocking(m,c)) return -1; - // multi-input nodes execute set_output_blocking - if (!nodes_list[0]->init_input_blocking(m,c)) return -1; - // --------------------- - } else { - if (last_multi_standard) { - if (nodes_list[last]->create_output_buffer(out_buffer_entries, false) <0) - return -1; - svector w(MAX_NUM_THREADS); - nodes_list[last]->get_out_nodes(w); - assert(w.size()); - for(size_t j=0;jset_input_feedback(w[j]); - - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!nodes_list[last]->init_output_blocking(m,c)) return -1; - // multi-input nodes execute set_output_blocking - if (!nodes_list[0]->init_input_blocking(m,c)) return -1; - // --------------------- - } else { - if (last_multi_multioutput) { - svector w(MAX_NUM_THREADS); - nodes_list[last]->get_out_nodes(w); - assert(w.size()); - - for(size_t j=0;jset_input_feedback(t); - w[j]->set_output_feedback(t); - } - - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!nodes_list[last]->init_output_blocking(m,c)) return -1; - // multi-input nodes execute set_output_blocking - if (!nodes_list[0]->init_input_blocking(m,c)) return -1; - // --------------------- - } else { - error("PIPE, wrap_around invalid stage\n"); - return -1; - } - } - } - } - nodes_list[0]->skipfirstpop(true); - } - // first stage: multi standard - if (first_multi_standard) { // all-to-all - assert(get_node(0)->isAll2All()); - ff_node* a2a = nodes_list[0]; - const svector& firstSet=isa2a_getfirstset(a2a); - assert(firstSet.size()>0); - - if (last_single_standard) { - error("PIPE, wrap_around, cannot connect last with first stage\n"); - return -1; - } - if (last_single_multioutput) { - for(size_t j=0;jset_input(t); - nodes_list[last]->set_output_feedback(t); - - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!firstSet[j]->init_input_blocking(m,c)) return -1; - t->set_output_blocking(m,c); - // --------------------- - } - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - nodes_list[last]->init_output_blocking(m,c); - // --------------------- - } - if (last_multi_standard) { - svector w(MAX_NUM_THREADS); - nodes_list[last]->get_out_nodes(w); - assert(w.size()); - assert(w.size() == firstSet.size()); - - if (a2a->create_input_buffer(in_buffer_entries, false)<0) return -1; - for(size_t j=0;jset_output_buffer(firstSet[j]->get_in_buffer()); - - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!a2a->init_output_blocking(m,c)) return -1; - for(size_t j=0;jinit_input_blocking(m,c)) return -1; - w[j]->set_output_blocking(m,c); - } - // --------------------- - } - if (last_multi_multioutput) { - error("PIPE, wrap_around, cannot connect last stage with first stage\n"); - return -1; - } - nodes_list[0]->skipfirstpop(true); - } - // first stage: multi multi-input - if (first_multi_multiinput) { - assert(get_node(0)->isAll2All()); - const svector& firstSet=isa2a_getfirstset(nodes_list[0]); - assert(firstSet.size()>0); - - if (last_single_standard) { - error("PIPE, wrap_around, cannot connect last with first stage\n"); - return -1; - } - if (last_single_multioutput) { - for(size_t j=0;jset_input_feedback(t); - nodes_list[last]->set_output_feedback(t); - } - } - if (last_multi_standard) { - svector w(MAX_NUM_THREADS); - nodes_list[last]->get_out_nodes(w); - assert(w.size()); - assert(w.size() == firstSet.size()); - - for(size_t i=0;iset_input_feedback(t); - w[i]->set_output(t); - } - } - if (last_multi_multioutput) { - svector w(MAX_NUM_THREADS); - nodes_list[last]->get_out_nodes(w); - assert(w.size()); - - // here we have to create all connections - for(size_t i=0;iset_input_feedback(t); - w[j]->set_output_feedback(t); - } - } - } - // blocking stuff ...... - pthread_mutex_t *m = NULL; - pthread_cond_t *c = NULL; - if (!nodes_list[last]->init_output_blocking(m,c)) return -1; - if (!nodes_list[0]->init_input_blocking(m,c)) return -1; - // --------------------- - nodes_list[0]->skipfirstpop(true); - } - - return 0; - } + int wrap_around() { wraparound=true; return 0; }; + bool isset_wraparound() { return wraparound; } bool isset_cleanup_nodes() const { return node_cleanup; } @@ -1355,7 +1367,8 @@ class ff_pipeline: public ff_node { return nodes_list[last]->isMultiOutput(); } - // remove internal pipeline + // remove internal pipeline + // WARNING: if there are feedback channels calling this method is dangerous! void flatten() { const svector>& W = this->get_and_remove_nodes(); assert(get_pipeline_nodes().size() == 0); @@ -1679,6 +1692,7 @@ class ff_pipeline: public ff_node { bool has_input_channel; // for accelerator bool node_cleanup; bool fixedsizeIN, fixedsizeOUT; + bool wraparound=false; int in_buffer_entries; int out_buffer_entries; svector nodes_list; diff --git a/tests/distributed/test_group13.cpp b/tests/distributed/test_group13.cpp index eafbbb38..ec9e0792 100644 --- a/tests/distributed/test_group13.cpp +++ b/tests/distributed/test_group13.cpp @@ -8,9 +8,9 @@ * /<--------- a2a0 ---------->/ * /<--------------------- pipe --------------------->/ * - * G1: pipe0 - * G2: a2a0 - * G3: pipe1 + * G1: Node1 + * G2: a2a + * G3: Node4 */ diff --git a/tests/distributed/test_group16.cpp b/tests/distributed/test_group16.cpp index bb373682..8d0bf792 100644 --- a/tests/distributed/test_group16.cpp +++ b/tests/distributed/test_group16.cpp @@ -1,9 +1,9 @@ /* * - * Node1--->Node2 ---> Node3 ---> Node4 + * Node1--->Node2 ---> Node3 -----> Node4 * - * /<-- pipe0-->//<-- pipe1 -->//<-pipe2->/ - * /<----------- pipe ------------>/ + * /<-- pipe0-->/ /<-- pipe2 -->/ + * /<------------------- pipe ------------>/ * * G1: pipe0 * G2: pipe1 diff --git a/tests/distributed/test_group17.cpp b/tests/distributed/test_group17.cpp index d3c73cdb..5f6ccf23 100644 --- a/tests/distributed/test_group17.cpp +++ b/tests/distributed/test_group17.cpp @@ -2,44 +2,47 @@ * FastFlow concurrent network: * * - * ----------------------------- - * | |--> Sink1 --> | - * | Source1-->| | - * | |--> Sink2 --> | - * | Source2-->| | - * | |--> Sink3 --> | - * ----------------------------- + * ------------------------ + * | |--> Sink1 | + * | Source1-->| | + * | |--> Sink2 | + * | Source2-->| | + * | |--> Sink3 | + * | Source3-->| | + * | |--> Sink4 | + * ------------------------ * * distributed version: * - * G1: all Source(s) - * G2: all Sink(s) + * ------------ ----------- + * | | | -> Sink1 | + * | Source1-> | | | + * | | | -> Sink2 | + * | Source2-->| ---> | | + * | | | -> Sink3 | + * | Source3-->| | | + * | | | -> Sink4 | + * ------------ ----------- + * G1 G2 * */ -#include -#include -#include #include +#include +#include +#include using namespace ff; -std::mutex mtx; +std::mutex mtx; // used for pretty printing struct Source : ff_monode_t{ - int numWorker, generatorID; - Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} - - int svc_init(){ - std::cout << "Source init called!\n"; - return 0; - } - - std::string* svc(std::string* in){ - std::cout << "Entering the loop of Source\n"; - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); + std::string* svc(std::string* in){ + int numWorkers = get_num_outchannels(); + + for(int i = 0; i < numWorkers; i++) + ff_send_out_to(new std::string("Task generated from " + std::to_string(get_my_id()) + " for " + std::to_string(i)), i); return EOS; } }; @@ -51,7 +54,7 @@ struct Sink : ff_minode_t{ std::string* svc(std::string* in){ const std::lock_guard lock(mtx); - ff::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << ff::endl; + ff::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << "\n"; delete in; return this->GO_ON; } @@ -70,8 +73,8 @@ int main(int argc, char*argv[]){ auto g1 = a2a.createGroup("G1"); auto g2 = a2a.createGroup("G2"); - for(int i = 0 ; i < 3; i++){ - auto s = new Source(4, i); + for(int i = 0 ; i < 3; i++){ + auto s = new Source(); firstSet.push_back(s); g1 << s; } diff --git a/tests/distributed/test_group17.json b/tests/distributed/test_group17.json index f5873926..59599480 100644 --- a/tests/distributed/test_group17.json +++ b/tests/distributed/test_group17.json @@ -2,11 +2,15 @@ "groups" : [ { "endpoint" : "localhost:9004", - "name" : "G1" + "name" : "G1", + "batchSize": 2, + "messageOTF": 1 }, { "name" : "G2", - "endpoint": "localhost:9005" + "endpoint": "localhost:9005", + "batchSize": 2, + "messageOTF": 1 } ] } diff --git a/tests/distributed/test_group21.cpp b/tests/distributed/test_group21.cpp new file mode 100644 index 00000000..8c0b9031 --- /dev/null +++ b/tests/distributed/test_group21.cpp @@ -0,0 +1,206 @@ +/* + * FastFlow concurrent network: + * + * + * (feedback channels, each FdbOutput has one channel toward + * each FdbInput) + * ------------------------------------------------------ + * | | + * | |-> Node3_1 FdbOutput -->| | + * v |-> FdbInput Node2_1 ->| | -- + * Node1 --->| |-> Node3_2 FdbOutput -->| ---> Node4 + * |-> FdbInput Node2_2 ->| | + * |-> Node3_3 FdbOutput -->| + * + * /<--- combine -->/ /<--- combine -->/ + * + * /<---------------- a2a ------------------------>/ + * /<----------------------------- pipe ------------------------------>/ + * + * + * distributed version: + * + * --------------------------------------------------------- + * | ----------------------------------------------------- | + * | | | | + * | | |-> Node3_1 FdbOutput -->| | | + * ------- | | |-> FdbInput Node2_1 ->| |-- | ------- + * | Node1 |-->| ->| |-> Node3_2 FdbOutput -->|--->|-->| Node4 | + * | | | |-> FdbInput Node2_2 ->| | | | | + * ------- | |-> Node3_3 FdbOutput -->| | ------- + * G1 | | G3 + * --------------------------------------------------------- + * G2 + * + * G1: Node1 + * G2: a2a + * G3: Node4 + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + myTask_t() {} + myTask_t(myTask_t* t){ + str = std::string(t->str); + S.t = t->S.t; + S.f = t->S.f; + } + + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct FdbInput : ff_minode_t{ + FdbInput(long ntasks) : ntasks(ntasks) {} + + myTask_t * svc(myTask_t* t){ + if (fromInput()) return t; + --ntasks; + //ff::cout << "FdbInput" << get_my_id() << " received from feedback channel: " << t->str << "\n"; + ff_send_out(t); + return (ntasks>0 ? GO_ON : EOS); + } + long ntasks; +}; + + +struct Node2: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + return t; + } +}; + +struct FdbOutput : ff_monode_t{ + int svc_init() { + feedbacks = get_num_feedbackchannels(); + return 0; + } + + myTask_t* svc(myTask_t* t){ + if (t->str == "Hello World"){ + t->str = "Feedback!"; + for(int i = 0; i < feedbacks; i++) { + //std::cout << "FdbOutput" << get_my_id() << " sending task back to " << std::to_string(i) << "\n"; + ff_send_out_to(new myTask_t(t), i); + } + delete t; + return this->GO_ON; + } + //std::cout << "FdbOutput" << get_my_id() << " sending task forward into channel " << std::to_string(feedbacks) << "\n"; + ff_send_out_to(t, feedbacks); + return this->GO_ON; + } + + int feedbacks=0; +}; + +struct Node3: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + std::cerr << "ERROR: processed " << processed << " tasks, expected " << ntasks << "\n"; + exit(-1); + } + std::cout << "RESULT OK, processed " << processed << " tasks\n"; + } + long ntasks; + long processed=0; +}; + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 100; + size_t nL = 3; + size_t nR = 5; + if (argc>1) { + if (argc != 4) { + std::cerr << "usage: " << argv[0] + << " ntasks num-left num-right\n"; + return -1; + } + ntasks = std::stol(argv[1]); + nL = std::stol(argv[2]); + nR = std::stol(argv[3]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node4 n4(ntasks*nL); + ff_a2a a2a; + + std::vector L; + for(size_t i=0;i R; + for(size_t i=0;i| | Node3->FdbOutput ->| | + * -> | | | | | |-- + * Node1---->| FdbInput-Node2 ->|->Col1|-->|Col2->| Node3->FdbOutput ->|---> Node4 + * | | | | | | + * | FdbInput-Node2 ->| | Node3->FdbOutput ->| + * + * /<------ a2a1------------>/ /<--------- a2a2----------->/ + * /<---------------------------- pipe1----------------------------->/ + * /<---------------------------------- pipe ----------------------------------->/ + * + * distributed version: + * + * G1: Node1 + * G2: pipe1 + * G3: Node4 + * + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + myTask_t() {} + myTask_t(myTask_t* t){ + str = std::string(t->str); + S.t = t->S.t; + S.f = t->S.f; + } + + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct Node2: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World") + std::to_string(get_my_id()); + return t; + } +}; +struct Col1: ff_minode_t{ + myTask_t* svc(myTask_t* in){ + return in; + } +}; +struct Col2: ff_monode_t{ + myTask_t* svc(myTask_t* in){ + return in; + } +}; + +struct Node3: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + t->S.t += get_my_id(); + t->S.f += get_my_id()*1.0; + return t; + } +}; + +struct FdbOutput : ff_monode_t{ + int svc_init() { + feedbacks = get_num_feedbackchannels(); + return 0; + } + + myTask_t* svc(myTask_t* t){ + if (t->str == "Hello World"){ + t->str = "Feedback!"; + for(int i = 0; i < feedbacks; i++) { + //std::cout << "FdbOutput" << get_my_id() << " sending task back to " << std::to_string(i) << "\n"; + ff_send_out_to(new myTask_t(t), i); + } + delete t; + return this->GO_ON; + } + //std::cout << "FdbOutput" << get_my_id() << " sending task forward into channel " << std::to_string(feedbacks) << "\n"; + ff_send_out_to(t, feedbacks); + return this->GO_ON; + } + + int feedbacks=0; +}; + +struct FdbInput : ff_minode_t{ + FdbInput(long ntasks) : ntasks(ntasks) {} + + myTask_t * svc(myTask_t* t){ + if (fromInput()) return t; + --ntasks; + //ff::cout << "FdbInput" << get_my_id() << " received from feedback channel: " << t->str << "\n"; + ff_send_out(t); + return (ntasks>0 ? GO_ON : EOS); + } + long ntasks; +}; + + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + delete t; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + std::cerr << "ERROR: processed " << processed << " tasks, expected " << ntasks << "\n"; + exit(-1); + } + std::cout << "RESULT OK, processed " << processed << " tasks\n"; + } + long ntasks; + long processed=0; +}; + + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 100; + size_t nL = 3; + size_t nR = 5; + if (argc>1) { + if (argc != 4) { + std::cerr << "usage: " << argv[0] + << " ntasks num-left num-right\n"; + return -1; + } + ntasks = std::stol(argv[1]); + nL = std::stol(argv[2]); + nR = std::stol(argv[3]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + ff_pipeline pipe1; + ff_a2a a2a1; + ff_a2a a2a2; + Node4 n4(ntasks*nL); + + pipe1.add_stage(&a2a1); + pipe1.add_stage(&a2a2); + pipe1.wrap_around(); + + pipe.add_stage(&n1); + pipe.add_stage(&pipe1); + pipe.add_stage(&n4); + + std::vector L; + for(size_t i=0;i({new Col1}, true); + + std::vector R; + for(size_t i=0;i({new Col2}, 0, true); + a2a2.add_secondset(R, true); + + + //----- defining the distributed groups ------ + + auto G1 = n1.createGroup("G1"); + auto G2 = pipe1.createGroup("G2"); + auto G3 = n4.createGroup("G3"); + + // ------------------------------------------- + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group3.cpp b/tests/distributed/test_group3.cpp index f7d3a037..ab8d6e5d 100644 --- a/tests/distributed/test_group3.cpp +++ b/tests/distributed/test_group3.cpp @@ -67,7 +67,7 @@ int main(int argc, char*argv[]){ Sink sink1; Sink sink2; a2a.add_firstset({&source1, &source2, &source3}); - a2a.add_secondset({&sink1, &sink2}); + a2a.add_secondset({&sink1, &sink2}); //----- defining the distributed groups ------ diff --git a/tests/distributed/test_parametricPerf.cpp b/tests/distributed/test_parametricPerf.cpp index 8c0e9de1..53ab9c90 100644 --- a/tests/distributed/test_parametricPerf.cpp +++ b/tests/distributed/test_parametricPerf.cpp @@ -26,7 +26,7 @@ #include // to test serialization without using Cereal -#define MANUAL_SERIALIZATION 1 +//#define MANUAL_SERIALIZATION 1 // ------------------------------------------------------ std::mutex mtx; // used only for pretty printing diff --git a/tests/test_all-to-all11.cpp b/tests/test_all-to-all11.cpp index a356b766..adf4777a 100644 --- a/tests/test_all-to-all11.cpp +++ b/tests/test_all-to-all11.cpp @@ -141,7 +141,7 @@ struct MultiInputHelper: ff_minode_t { struct Worker: ff_monode_t { long* svc(long* in) { - //std::cout << "Worker" << get_my_id() << " got " << *in << "\n"; + std::cout << "Worker" << get_my_id() << " got " << *in << "\n"; // Here we know that we have only one output channel // and we have 'get_num_feedbackchannels()' feedback channels. @@ -149,27 +149,39 @@ struct Worker: ff_monode_t { // and then the output ones ..... ff_send_out_to(in, get_num_feedbackchannels() ); // to Last + //std::cout << "Worker" << get_my_id() << " sending ack back\n"; + ff_send_out((long*)0x1); // sends it back return GO_ON; } void eosnotify(ssize_t) { - broadcast_task(EOS); + //broadcast_task(EOS); // sending EOS back ff_send_out_to(EOS, get_num_feedbackchannels()); } }; struct Last: ff_minode_t { + Last(size_t nt):nt(nt) {} long* svc(long* in) { std::cout << "Last: received " << *in << " from " << get_channel_id() << "\n"; + --nt; delete in; return GO_ON; } + void svc_end() { + if (nt != 0) { + std::cerr << "Test FAILED\n"; + exit(-1); + } + std::cout << "Test OK\n"; + } + size_t nt; }; int main() { - int nworkers = 2; + int nworkers = 3; ntasks.store(100); // ---- first stage @@ -201,7 +213,7 @@ int main() { a2a.wrap_around(); // ---- last stage - Last last; + Last last(ntasks.load()); // ---- building the topology ff_Pipe<> pipe(first, E, a2a, last); diff --git a/tests/test_changesize2.cpp b/tests/test_changesize2.cpp index 6678e0a2..bded7728 100644 --- a/tests/test_changesize2.cpp +++ b/tests/test_changesize2.cpp @@ -153,7 +153,7 @@ int main() { new ff_comb(helper2,w2) }; - assert(firstSet.size() == NWORKER2); + assert(secondSet.size() == NWORKER2); ff_a2a a2a; a2a.add_firstset(firstSet, 1000, false); a2a.add_secondset(secondSet, true); diff --git a/tests/test_multi_input11.cpp b/tests/test_multi_input11.cpp index 0ec277bc..451345d4 100644 --- a/tests/test_multi_input11.cpp +++ b/tests/test_multi_input11.cpp @@ -78,7 +78,7 @@ struct Collector: ff_monode_t { long* svc(long* in) { ff_send_out_to(in, 0); // backward ff_send_out_to(in, next + 1); // forward - next = ((next + 1) % get_num_outchannels()); + next = ((next + 1) % (get_num_outchannels() - 1)); // get_num_outchannels() comprises also feedback channels return GO_ON; } void eosnotify(ssize_t) { From b640df9ff204ed36f692df3da3c6dca81c80f35d Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Mon, 6 Jun 2022 14:34:29 +0200 Subject: [PATCH 170/202] removed an old file --- tests/distributed/test_group13_feedback.cpp | 159 -------------------- 1 file changed, 159 deletions(-) delete mode 100644 tests/distributed/test_group13_feedback.cpp diff --git a/tests/distributed/test_group13_feedback.cpp b/tests/distributed/test_group13_feedback.cpp deleted file mode 100644 index 8f795023..00000000 --- a/tests/distributed/test_group13_feedback.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * |-> Node31 FdbOutput -->| - * |-> FdbInput Node21 -->| | - * Node1---->| |-> Node32 FdbOutput -->| ----> Node4 - * |-> FdbInput Node22 -->| | - * |-> Node33 FdbOutput -->| - * - * /<--------- a2a0 ---------->/ - * /<--------------------- pipe --------------------->/ - * - * G1: pipe0 - * G2: a2a0 - * G3: pipe1 - */ - - -#include -#include - -using namespace ff; - -struct myTask_t { - myTask_t() {} - myTask_t(myTask_t* t){ - str = std::string(t->str); - S.t = t->S.t; - S.f = t->S.f; - } - - std::string str; - struct S_t { - long t; - float f; - } S; - - template - void serialize(Archive & archive) { - archive(str, S.t, S.f); - } - -}; - -struct Node1: ff_monode_t{ - Node1(long ntasks):ntasks(ntasks) {} - myTask_t* svc(myTask_t*){ - for(long i=0; i< ntasks; i++) { - myTask_t* task = new myTask_t; - task->str="Hello"; - task->S.t = i; - task->S.f = i*1.0; - ff_send_out(task); - } - return EOS; - } - const long ntasks; -}; - -struct FdbInput : ff_minode_t{ - int feedbacks, feedbackTasks = 0; - - FdbInput(int feedbacks) : feedbacks(feedbacks) {} - - myTask_t * svc(myTask_t* t){ - if (fromInput()) return t; - feedbackTasks++; - ff::cout << "Received from feeback channel!\n"; - ff_send_out(t); - return (feedbackTasks == feedbacks ? this->EOS : this->GO_ON); - } -}; - - -struct Node2: ff_monode_t{ - myTask_t* svc(myTask_t* t){ - t->str += std::string(" World"); - return t; - } -}; - -struct FdbOutput : ff_monode_t{ - int feedbacks; - FdbOutput(int feedbacks) : feedbacks(feedbacks) {} - myTask_t* svc(myTask_t* t){ - if (t->str == "Hello World"){ - t->str = "Feedback!"; - for(int i = 0; i < feedbacks; i++) ff_send_out_to(new myTask_t(t), i); - delete t; - return this->GO_ON; - } - ff_send_out_to(t, feedbacks); - return this->GO_ON; - } -}; - -struct Node3: ff_minode_t{ - myTask_t* svc(myTask_t* t){ - t->S.t += get_my_id(); - t->S.f += get_my_id()*1.0; - return t; - } -}; - -struct Node4: ff_minode_t{ - Node4(long ntasks):ntasks(ntasks) {} - myTask_t* svc(myTask_t* t){ - ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; - ++processed; - delete t; - return GO_ON; - } - void svc_end() { - if (processed != ntasks) { - return; - //abort(); - } - std::cout << "RESULT OK\n"; - } - long ntasks; - long processed=0; -}; - -int main(int argc, char*argv[]){ - if (DFF_Init(argc, argv)<0 ) { - error("DFF_Init\n"); - return -1; - } - long ntasks = 1; - if (argc>1) { - ntasks = std::stol(argv[1]); - } - - ff_pipeline pipe; - Node1 n1(ntasks); - Node2 n21, n22; - Node3 n31, n32, n33; - Node4 n4(ntasks*2); - ff_a2a a2a; - a2a.add_firstset({new ff_comb(new FdbInput(1), &n21)}); - a2a.add_secondset({new ff_comb(&n31,new FdbOutput(1))}); - a2a.wrap_around(); - - pipe.add_stage(&n1); - pipe.add_stage(&a2a); - pipe.add_stage(&n4); - - //----- defining the distributed groups ------ - - auto G1 = n1.createGroup("G1"); - auto G2 = a2a.createGroup("G2"); - auto G3 = n4.createGroup("G3"); - - // ------------------------------------------- - - if (pipe.run_and_wait_end()<0) { - error("running the main pipe\n"); - return -1; - } - return 0; -} From 9a012b8fc3587b71d61e89c284c088fbbad05777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 6 Jun 2022 15:25:09 +0200 Subject: [PATCH 171/202] Indentation commit --- ff/distributed/ff_dgroup.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index b502ceb4..9a8ba39a 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -114,20 +114,20 @@ class dGroup : public ff::ff_farm { else workers.push_back(buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels)); } else { - if (ir.hasReceiver){ + if (ir.hasReceiver){ for(ff_node* input : inputs){ ff_node* inputParent = getBB(child, input); if (inputParent) inputParent->change_node(input, buildWrapperIN(input), true); //cleanup?? removefromcleanuplist?? } - } + } - if (ir.hasSender){ + if (ir.hasSender){ for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes), outputChannels), true); // cleanup?? removefromcleanuplist?? } - } - + } + workers.push_back(child); } } From e7e91eed4a7a1c80881acc6d2ce25c528a667697 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 7 Jun 2022 14:45:38 +0200 Subject: [PATCH 172/202] improved feedback channels support --- ff/pipeline.hpp | 27 +++++++++++++------- tests/distributed/test_group22.cpp | 38 +++++++++++++++-------------- tests/distributed/test_group22.json | 16 ++++++++++++ 3 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 tests/distributed/test_group22.json diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 50620544..34003d01 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -58,6 +58,7 @@ static bool isfarm_withcollector(ff_node*); static bool isfarm_multimultioutput(ff_node*); static const svector& isa2a_getfirstset(ff_node*); static const svector& isa2a_getsecondset(ff_node*); +static const svector& isfarm_getworkers(ff_node*); /** * \class ff_pipeline @@ -90,7 +91,7 @@ class ff_pipeline: public ff_node { // // the last stage is a multi-output node: // - it's a farm with a multi-output collector [last_single_multioutput] - // - it's a farm without collector and workers are standard nodes [last_multi_standard] + // - it's a farm without collector and workers are standard nodes [last_multi_standard] // - it's a farm without collector with multi-output workers [last_multi_multioutput] // - it's a all2all with standard nodes [last_multi_standard] // - it's a all2all with multi-output nodes [last_multi_multioutput] @@ -184,7 +185,6 @@ class ff_pipeline: public ff_node { return -1; } } - nodes_list[0]->skipfirstpop(true); } // first stage: multi-input if (first_single_multiinput) { @@ -234,8 +234,14 @@ class ff_pipeline: public ff_node { // --------------------- } else { if (last_multi_multioutput) { - svector w(MAX_NUM_THREADS); - nodes_list[last]->get_out_nodes(w); + svector w; + ff_node *lastbb = get_lastnode(); + if (lastbb->isAll2All()) + w = isa2a_getsecondset(lastbb); + else { + assert(lastbb->isFarm()); + w = isfarm_getworkers(lastbb); + } assert(w.size()); for(size_t j=0;jskipfirstpop(true); } // first stage: multi standard if (first_multi_standard) { // all-to-all @@ -318,7 +323,6 @@ class ff_pipeline: public ff_node { error("PIPE, wrap_around, cannot connect last stage with first stage\n"); return -1; } - nodes_list[0]->skipfirstpop(true); } // first stage: multi multi-input if (first_multi_multiinput) { @@ -354,8 +358,14 @@ class ff_pipeline: public ff_node { } } if (last_multi_multioutput) { - svector w(MAX_NUM_THREADS); - nodes_list[last]->get_out_nodes(w); + svector w; + ff_node *lastbb = get_lastnode(); + if (lastbb->isAll2All()) + w = isa2a_getsecondset(lastbb); + else { + assert(lastbb->isFarm()); + w = isfarm_getworkers(lastbb); + } assert(w.size()); // here we have to create all connections @@ -375,7 +385,6 @@ class ff_pipeline: public ff_node { if (!nodes_list[last]->init_output_blocking(m,c)) return -1; if (!nodes_list[0]->init_input_blocking(m,c)) return -1; // --------------------- - nodes_list[0]->skipfirstpop(true); } return 0; } diff --git a/tests/distributed/test_group22.cpp b/tests/distributed/test_group22.cpp index 653122ba..e53276be 100644 --- a/tests/distributed/test_group22.cpp +++ b/tests/distributed/test_group22.cpp @@ -1,4 +1,6 @@ /* + * FastFlow concurrent network: + * * ---------------------------------------------------------------- * | | | * | | FdbInput-Node2 ->| | Node3->FdbOutput ->| | @@ -61,8 +63,8 @@ struct Node1: ff_monode_t{ }; struct Node2: ff_monode_t{ - myTask_t* svc(myTask_t* t){ - t->str += std::string(" World") + std::to_string(get_my_id()); + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); return t; } }; @@ -85,6 +87,21 @@ struct Node3: ff_minode_t{ } }; + +struct FdbInput : ff_minode_t{ + FdbInput(long ntasks) : ntasks(ntasks) {} + + myTask_t * svc(myTask_t* t){ + if (fromInput()) return t; + --ntasks; + //ff::cout << "FdbInput" << get_my_id() << " received from feedback channel: " << t->str << "\n"; + ff_send_out(t); + return (ntasks>0 ? GO_ON : EOS); + } + + long ntasks; +}; + struct FdbOutput : ff_monode_t{ int svc_init() { feedbacks = get_num_feedbackchannels(); @@ -105,28 +122,13 @@ struct FdbOutput : ff_monode_t{ ff_send_out_to(t, feedbacks); return this->GO_ON; } - int feedbacks=0; }; -struct FdbInput : ff_minode_t{ - FdbInput(long ntasks) : ntasks(ntasks) {} - - myTask_t * svc(myTask_t* t){ - if (fromInput()) return t; - --ntasks; - //ff::cout << "FdbInput" << get_my_id() << " received from feedback channel: " << t->str << "\n"; - ff_send_out(t); - return (ntasks>0 ? GO_ON : EOS); - } - long ntasks; -}; - - struct Node4: ff_minode_t{ Node4(long ntasks):ntasks(ntasks) {} myTask_t* svc(myTask_t* t){ - ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; ++processed; delete t; return GO_ON; diff --git a/tests/distributed/test_group22.json b/tests/distributed/test_group22.json new file mode 100644 index 00000000..732c6801 --- /dev/null +++ b/tests/distributed/test_group22.json @@ -0,0 +1,16 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8000", + "name" : "G1" + }, + { + "endpoint" : "localhost:8004", + "name" : "G2" + }, + { + "endpoint" : "localhost:8005", + "name" : "G3" + } + ] +} From a3aea326d220a0a2d5fa6f4717e08795ff08c30b Mon Sep 17 00:00:00 2001 From: Marco Aldinucci Date: Thu, 9 Jun 2022 00:57:33 +0200 Subject: [PATCH 173/202] minor fix to compile on MacOS --- ff/distributed/loader/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ff/distributed/loader/Makefile b/ff/distributed/loader/Makefile index e7623b9f..ffc4aabd 100644 --- a/ff/distributed/loader/Makefile +++ b/ff/distributed/loader/Makefile @@ -36,9 +36,13 @@ else INCS += -I ~/cereal/include endif +OS = $(shell uname) CXXFLAGS += -Wall -LIBS = -pthread -lstdc++fs +LIBS = -pthread -lstdc++fs +ifeq ($(strip $(OS)),Darwin) + LIBS = -pthread +endif INCLUDES = $(INCS) SOURCES = $(wildcard *.cpp) From 04883094600aaac8a3d3cb1b8523900473b1780c Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 9 Jun 2022 14:38:03 +0200 Subject: [PATCH 174/202] new test with feedback channel --- ff/distributed/ff_dsenderMPI.hpp | 4 - tests/distributed/test_group23.cpp | 176 ++++++++++++++++++++++++++++ tests/distributed/test_group23.json | 16 +++ 3 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 tests/distributed/test_group23.cpp create mode 100644 tests/distributed/test_group23.json diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 72b8f528..ebba8449 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -96,10 +96,6 @@ class ff_dsenderMPI: public ff_minode_t { } } int push(message_t* m){ - - std::cerr << "directBatchBuffer PUSH\n"; - - waitCompletion(); currHeader[1] = m->sender; currHeader[2] = m->chid; currHeader[3] = m->data.getLen(); MPI_Isend(currHeader, 4, MPI_LONG, this->rank, DFF_HEADER_TAG, MPI_COMM_WORLD, &this->headersR); diff --git a/tests/distributed/test_group23.cpp b/tests/distributed/test_group23.cpp new file mode 100644 index 00000000..71b126cf --- /dev/null +++ b/tests/distributed/test_group23.cpp @@ -0,0 +1,176 @@ +/* + * FastFlow concurrent network: + * + * ------------- + * ----|-------------|---- + * | v | | + * Node1 ---> | Node2 ----> Node3 ->| --> Node4 + * ^ | pipe1 | | + * | ----------------------- | + * --------------------------------------- + * + * /<------------------ pipe ------------------>/ + * + * distributed version: + * + * G1: Node1 + * G2: pipe1 + * G3: Node4 + * + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + myTask_t() {} + myTask_t(myTask_t* t){ + str = std::string(t->str); + S.t = t->S.t; + S.f = t->S.f; + } + + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks),cnt(ntasks) {} + myTask_t* svc(myTask_t*in){ + long min = std::min(cnt,appbatch); + for(long i=0; i < min; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + --cnt; + } + if (in) { + ++back; + delete in; + } + if (back == ntasks) { + return EOS; + } + return GO_ON; + } + + const long ntasks; + long back=0,cnt; + long appbatch=10; +}; + + +struct Node2: ff_minode_t{ + Node2(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + if (fromInput()) { + t->str += std::string(" World"); + return t; + } + --ntasks; + ff_send_out(t); + if (ntasks == 0 && eosreceived) return EOS; + return GO_ON; + } + void eosnotify(ssize_t) { + eosreceived=true; + if (ntasks==0) { + ff_send_out(EOS); + ntasks=-1; + } + } + long ntasks; + bool eosreceived=false; +}; +struct Node3: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + if (t->str == "Hello World") { + t->str = "Feedback!"; + t->S.t += 1; + t->S.f += 1.0; + ff_send_out_to(t, 0); // sends it back + return GO_ON; + } + ff_send_out_to(t, 1); // forward + return GO_ON; + } +}; + +struct Node4: ff_minode_t{ + Node4(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t* t){ + ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + return t; + } + void svc_end() { + if (processed != ntasks) { + std::cerr << "ERROR: processed " << processed << " tasks, expected " << ntasks << "\n"; + exit(-1); + } + std::cout << "RESULT OK, processed " << processed << " tasks\n"; + } + long ntasks; + long processed=0; +}; + + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 100; + if (argc>1) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] + << " ntasks\n"; + return -1; + } + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2(ntasks); + Node3 n3; + Node4 n4(ntasks); + ff_pipeline pipe1; + + pipe1.add_stage(&n2); + pipe1.add_stage(&n3); + pipe1.wrap_around(); + + pipe.add_stage(&n1); + pipe.add_stage(&pipe1); + pipe.add_stage(&n4); + pipe.wrap_around(); + + //----- defining the distributed groups ------ + + auto G1 = n1.createGroup("G1"); + auto G2 = pipe1.createGroup("G2"); + auto G3 = n4.createGroup("G3"); + + // ------------------------------------------- + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group23.json b/tests/distributed/test_group23.json new file mode 100644 index 00000000..732c6801 --- /dev/null +++ b/tests/distributed/test_group23.json @@ -0,0 +1,16 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8000", + "name" : "G1" + }, + { + "endpoint" : "localhost:8004", + "name" : "G2" + }, + { + "endpoint" : "localhost:8005", + "name" : "G3" + } + ] +} From 7116448505ef64d4814178129d49dac25e9f07ba Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Thu, 9 Jun 2022 19:43:37 +0200 Subject: [PATCH 175/202] new tests; minor improvement of the "add_group" interface --- ff/distributed/ff_dinterface.hpp | 6 +- tests/distributed/test_group24.cpp | 132 ++++++++++++++++++++++++++++ tests/distributed/test_group24.json | 20 +++++ 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 tests/distributed/test_group24.cpp create mode 100644 tests/distributed/test_group24.json diff --git a/ff/distributed/ff_dinterface.hpp b/ff/distributed/ff_dinterface.hpp index 0dd37e5e..3354b683 100644 --- a/ff/distributed/ff_dinterface.hpp +++ b/ff/distributed/ff_dinterface.hpp @@ -29,6 +29,10 @@ struct GroupInterface { #endif return *this; } + + GroupInterface& operator<<(ff_node& node){ + return *this << &node; + } }; @@ -41,4 +45,4 @@ GroupInterface ff_node::createGroup(std::string name){ } -#endif \ No newline at end of file +#endif diff --git a/tests/distributed/test_group24.cpp b/tests/distributed/test_group24.cpp new file mode 100644 index 00000000..54dbfffd --- /dev/null +++ b/tests/distributed/test_group24.cpp @@ -0,0 +1,132 @@ +/* + * FastFlow concurrent network: + * + * Client ->| + * | -> level1Gatherer ->| + * Client ->| | + * | + * .... | --> level0Gatherer + * | + * Client ->| | + * | -> level1Gatherer ->| + * Client ->| + * + * + * distributed version: + * + * + * G1: Node1 + * G2: a2a + * G3: Node4 + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + myTask_t() {} + myTask_t(myTask_t* t){ + str = std::string(t->str); + S.t = t->S.t; + S.f = t->S.f; + } + + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } + +}; + +struct Client: ff_monode_t { + Client(long ntasks):ntasks(ntasks) {} + myTask_t* svc(myTask_t*){ + for(long i=0; i< ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + } + return EOS; + } + const long ntasks; +}; + +struct level1Gatherer: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + return t; + } +}; +struct HelperNode: ff_monode_t { + myTask_t* svc(myTask_t* t) { return t; } +}; + +struct level0Gatherer: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + std::cerr << "level0Gatherer: from (" << get_channel_id() << ") " << t->str << "\n"; + return GO_ON; + } +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 100; + size_t nL = 3; + size_t pL = 2; + if (argc>1) { + if (argc != 4) { + std::cerr << "usage: " << argv[0] + << " ntasks n-left-groups pargroup\n"; + return -1; + } + ntasks = std::stol(argv[1]); + nL = std::stol(argv[2]); + pL = std::stol(argv[3]); + } + + ff_a2a mainA2A; + level0Gatherer root; + std::vector globalLeft; + + for(size_t i=0;iadd_stage(a2a, true); + std::vector localLeft; + for(size_t j=0;jadd_firstset(localLeft, 0, true); + a2a->add_secondset({new ff_comb(new level1Gatherer, new HelperNode, true, true)}); + + globalLeft.push_back(pipe); + + auto g = mainA2A.createGroup("G"+std::to_string(i+1)); + g << pipe; + } + + mainA2A.add_firstset(globalLeft, true); + mainA2A.add_secondset({&root}); + + mainA2A.createGroup("G0") << root; + + if (mainA2A.run_and_wait_end()<0) { + error("running the main All-to-All\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group24.json b/tests/distributed/test_group24.json new file mode 100644 index 00000000..f76ad1ba --- /dev/null +++ b/tests/distributed/test_group24.json @@ -0,0 +1,20 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8000", + "name" : "G1" + }, + { + "endpoint" : "localhost:8001", + "name" : "G2" + }, + { + "endpoint" : "localhost:8001", + "name" : "G3" + }, + { + "endpoint" : "localhost:8006", + "name" : "G0" + } + ] +} From 6501d5bdb3758848a533379d8c74c6f1fa5bc55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 9 Jun 2022 21:17:10 +0200 Subject: [PATCH 176/202] Added first non working version of the feedback at level 0 (main pipeline) --- ff/distributed/ff_dgroup.hpp | 10 ++-- ff/distributed/ff_dgroups.hpp | 40 ++++++++++----- ff/distributed/ff_dintermediate.hpp | 77 +++++++++++++++-------------- ff/distributed/ff_dsender.hpp | 14 +++--- ff/distributed/ff_dsenderMPI.hpp | 15 +++--- ff/distributed/ff_network.hpp | 1 + tests/distributed/test_group23.cpp | 20 +++++++- 7 files changed, 108 insertions(+), 69 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index aac46093..481ae9a0 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -111,7 +111,9 @@ class dGroup : public ff::ff_farm { if (ir.hasReceiver && ir.hasSender) { wrapper = new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes)); workers.push_back(wrapper); + if (ir.isSource) wrapper->skipfirstpop(true); } else if (ir.hasReceiver) { + if (ir.isSource) wrapper->skipfirstpop(true); wrapper = buildWrapperIN(child); workers.push_back(wrapper); } else { @@ -119,13 +121,13 @@ class dGroup : public ff::ff_farm { workers.push_back(wrapper); } // TODO: in case there are feedback channels we cannot skip all pops! - if (ir.isSource) - wrapper->skipallpop(true); + /*if (ir.isSource) + wrapper->skip(true);*/ } else { // TODO: in case there are feedback channels we cannot skip all pops! - if (ir.isSource) - child->skipallpop(true); + /* if (ir.isSource) + child->skipallpop(true);*/ if (ir.hasReceiver){ for(ff_node* input : inputs){ diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index d92aa060..24ca37b8 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -267,6 +267,8 @@ class dGroups { if (g.internalMessageOTF) annotatedGroups[g.name].internalMessageOTF = g.internalMessageOTF; } + // TODO check first level pipeline before strting building the groups. + // build the map parentBB -> std::map> parentBB2GroupsName; for(auto& p: annotatedGroups) parentBB2GroupsName[p.second.parentBB].insert(p.first); @@ -275,8 +277,8 @@ class dGroups { for(const auto& pair : parentBB2GroupsName){ //just build the current previous the current and the next stage of the parentbuilding block i'm going to exeecute //// TODO: reprhase this comment! - if (!(pair.first == previousStage || pair.first == runningGroup_IR.parentBB || pair.first == nextStage)) - continue; + //if (!(pair.first == previousStage || pair.first == runningGroup_IR.parentBB || pair.first == nextStage)) + // continue; bool isSrc = isSource(pair.first, parentPipe); bool isSnk = isSink(pair.first, parentPipe); @@ -331,7 +333,7 @@ class dGroups { if (!tail) - annotatedGroups[gName].destinationEndpoints.push_back(annotatedGroups[annotated[originalPipe->get_nextstage(mypipe->get_laststage())]].listenEndpoint); + annotatedGroups[gName].destinationEndpoints.push_back({ChannelType::FWD, annotatedGroups[annotated[originalPipe->get_nextstage(mypipe->get_laststage())]].listenEndpoint}); if (!head) annotatedGroups[gName].expectedEOS = 1; @@ -403,9 +405,11 @@ class dGroups { } + + // compute routing for horizzontally splitted A2A // this is meaningful only if the group is horizontal and made of an a2a if (!runningGroup_IR.isVertical()){ - assert(runningGroup_IR.parentBB->isAll2All()); + assert(runningGroup_IR.parentBB->isAll2All()); ff_a2a* parentA2A = reinterpret_cast(runningGroup_IR.parentBB); { ff::svector inputs; @@ -419,7 +423,10 @@ class dGroups { } } + //############# compute the number of excpected input connections + + // handle horizontal groups if (runningGroup_IR.hasRightChildren()){ auto& currentGroups = parentBB2GroupsName[runningGroup_IR.parentBB]; runningGroup_IR.expectedEOS = std::count_if(currentGroups.cbegin(), currentGroups.cend(), [&](auto& gName){return (!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasLeftChildren());}); @@ -429,6 +436,12 @@ class dGroups { // if the previousStage exists, count all the ouput groups pointing to the one i'm going to run if (previousStage && runningGroup_IR.hasLeftChildren()) runningGroup_IR.expectedEOS += outputGroups(parentBB2GroupsName[previousStage]).size(); + + // FEEDBACK RELATED (wrap around of the main pipe!) + // if the main pipe is wrapped-around i take all the outputgroups of the last stage of the pipeline and the cardinality must set to the expected input connections + if (!previousStage && parentPipe->isset_wraparound()) + runningGroup_IR.expectedEOS += outputGroups(parentBB2GroupsName[parentPipe->getStages().back()]).size(); + if (runningGroup_IR.expectedEOS > 0) runningGroup_IR.hasReceiver = true; @@ -437,18 +450,24 @@ class dGroups { // inserisci tutte i gruppi di questo bb a destra for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) if (!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasRightChildren()) - runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); + runningGroup_IR.destinationEndpoints.push_back({ChannelType::FWD, annotatedGroups[gName].listenEndpoint}); } else { if (!runningGroup_IR.isVertical()){ // inserisci tutti i gruppi come sopra for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) if ((!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasRightChildren()) && gName != runningGroup) - runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); + runningGroup_IR.destinationEndpoints.push_back({ChannelType::INT, annotatedGroups[gName].listenEndpoint}); } if (nextStage) for(const auto& gName : inputGroups(parentBB2GroupsName[nextStage])) - runningGroup_IR.destinationEndpoints.push_back(annotatedGroups[gName].listenEndpoint); + runningGroup_IR.destinationEndpoints.push_back({ChannelType::FWD, annotatedGroups[gName].listenEndpoint}); + else + // FEEDBACK RELATED (wrap around of the main pipe!) + if (parentPipe->isset_wraparound()){ + for(const auto& gName : inputGroups(parentBB2GroupsName[parentPipe->getStages().front()])) + runningGroup_IR.destinationEndpoints.push_back({ChannelType::FBK, annotatedGroups[gName].listenEndpoint}); + } } @@ -458,14 +477,13 @@ class dGroups { // experimental building the expected routing table for the running group offline (i.e., statically) if (runningGroup_IR.hasSender) - for(auto& ep : runningGroup_IR.destinationEndpoints){ + for(auto& [ct, ep] : runningGroup_IR.destinationEndpoints){ auto& destIR = annotatedGroups[ep.groupName]; destIR.buildIndexes(); - bool internalConnection = runningGroup_IR.parentBB == destIR.parentBB; - runningGroup_IR.routingTable[ep.groupName] = std::make_pair(destIR.getInputIndexes(internalConnection), internalConnection); + bool internalConnection = ct == ChannelType::INT; //runningGroup_IR.parentBB == destIR.parentBB; + runningGroup_IR.routingTable[ep.groupName] = std::make_pair(destIR.getInputIndexes(internalConnection), ct); } - //runningGroup_IR.print(); } std::set outputGroups(std::set groupNames){ diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index f16b65a5..132cd284 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -77,7 +77,8 @@ class ff_IR { ff_node* parentBB; ff_endpoint listenEndpoint; - std::vector destinationEndpoints; + std::vector> destinationEndpoints; + std::set otherGroupsFromSameParentBB; size_t expectedEOS = 0; int outBatchSize = 1; @@ -85,7 +86,8 @@ class ff_IR { // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table std::vector inputL, outputL, inputR, outputR; - std::map, bool>> routingTable; + + std::map, ChannelType>> routingTable; // TODO: implmentare l'assegnamento di questi campi int leftTotalOuputs; @@ -110,49 +112,50 @@ class ff_IR { } void print(){ - std::cout << "###### BEGIN GROUP ######\n"; - std::cout << "Group Orientation: " << (isVertical() ? "vertical" : "horizontal") << std::endl; - std::cout << std::boolalpha << "Source group: " << isSource << std::endl; - std::cout << std::boolalpha << "Sink group: " << isSink << std::endl; - std::cout << std::boolalpha << "Coverage Left: " << coverageL << std::endl; - std::cout << std::boolalpha << "Coverage Right: " << coverageR << std::endl << std::endl; - - std::cout << std::boolalpha << "Has Receiver: " << hasReceiver << std::endl; - std::cout << "Expected input connections: " << expectedEOS << std::endl; - std::cout << "Listen endpoint: " << listenEndpoint.address << ":" << listenEndpoint.port << std::endl << std::endl; - - std::cout << std::boolalpha << "Has Sender: " << hasSender << std::endl; - std::cout << "Destination endpoints: " << std::endl; - for(ff_endpoint& e : destinationEndpoints) - std::cout << "\t* " << e.groupName << "\t[[" << e.address << ":" << e.port << "]]" << std::endl; - - std::cout << "Precomputed routing table: \n"; + ff::cout << "###### BEGIN GROUP ######\n"; + ff::cout << "Group Orientation: " << (isVertical() ? "vertical" : "horizontal") << std::endl; + ff::cout << std::boolalpha << "Source group: " << isSource << std::endl; + ff::cout << std::boolalpha << "Sink group: " << isSink << std::endl; + ff::cout << std::boolalpha << "Coverage Left: " << coverageL << std::endl; + ff::cout << std::boolalpha << "Coverage Right: " << coverageR << std::endl << std::endl; + + ff::cout << std::boolalpha << "Has Receiver: " << hasReceiver << std::endl; + ff::cout << "Expected input connections: " << expectedEOS << std::endl; + ff::cout << "Listen endpoint: " << listenEndpoint.address << ":" << listenEndpoint.port << std::endl << std::endl; + + ff::cout << std::boolalpha << "Has Sender: " << hasSender << std::endl; + ff::cout << "Destination endpoints: " << std::endl; + for(auto& [ct, e] : destinationEndpoints) + ff::cout << "\t* " << e.groupName << "\t[[" << e.address << ":" << e.port << "]] - " << (ct==ChannelType::FBK ? "Feedback" : (ct==ChannelType::INT ? "Internal" : "Forward")) << std::endl; + + ff::cout << "Precomputed routing table: \n"; for(auto& [gName, p] : routingTable){ - std::cout << "\t* " << gName << (p.second ? "(Internal) :" : "(External) :"); - for(auto i : p.first) std::cout << i << " "; - std::cout << std::endl; + ff::cout << "\t* " << gName << (p.second==ChannelType::FBK ? "Feedback" : (p.second==ChannelType::INT ? "Internal" : "Forward")) << ":"; + for(auto i : p.first) ff::cout << i << " "; + ff::cout << std::endl; } - std::cout << "\nPrecomputed external destinations: " << std::accumulate(routingTable.begin(), routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? 0 : f.second.first.size());}) << std::endl; - std::cout << "Precomputed internal destinations: " << std::accumulate(routingTable.begin(), routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? f.second.first.size() : 0);}) << std::endl; + ff::cout << "\nPrecomputed FWD destinations: " << std::accumulate(routingTable.begin(), routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second == ChannelType::FWD ? f.second.first.size() : 0);}) << std::endl; + ff::cout << "Precomputed INT destinations: " << std::accumulate(routingTable.begin(), routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second == ChannelType::INT ? f.second.first.size() : 0);}) << std::endl; + ff::cout << "Precomputed FBK destinations: " << std::accumulate(routingTable.begin(), routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second == ChannelType::FBK ? f.second.first.size() : 0);}) << std::endl; - std::cout << "\n\nIndex Input Left: "; - for(int i : inputL) std::cout << i << " "; - std::cout << "\n"; + ff::cout << "\n\nIndex Input Left: "; + for(int i : inputL) ff::cout << i << " "; + ff::cout << "\n"; - std::cout << "Index Output Left: "; - for(int i : outputL) std::cout << i << " "; - std::cout << "\n"; + ff::cout << "Index Output Left: "; + for(int i : outputL) ff::cout << i << " "; + ff::cout << "\n"; - std::cout << "Index Input Right: "; - for(int i : inputR) std::cout << i << " "; - std::cout << "\n"; + ff::cout << "Index Input Right: "; + for(int i : inputR) ff::cout << i << " "; + ff::cout << "\n"; - std::cout << "Index Output Right: "; - for(int i : outputR) std::cout << i << " "; - std::cout << "\n"; + ff::cout << "Index Output Right: "; + for(int i : outputR) ff::cout << i << " "; + ff::cout << "\n"; - std::cout << "###### END GROUP ######\n"; + ff::cout << "###### END GROUP ######\n"; } diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 433bac4f..33165cb4 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -50,7 +50,7 @@ using namespace ff; class ff_dsender: public ff_minode_t { protected: size_t neos=0; - std::vector dest_endpoints; + std::vector> dest_endpoints; std::map dest2Socket; std::vector sockets; int last_rr_socket = -1; @@ -298,11 +298,11 @@ class ff_dsender: public ff_minode_t { public: - ff_dsender(ff_endpoint dest_endpoint, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1): gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { + ff_dsender(std::pair dest_endpoint, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1): gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector dest_endpoints_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} + ff_dsender( std::vector> dest_endpoints_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} @@ -315,7 +315,7 @@ class ff_dsender: public ff_minode_t { sockets.resize(dest_endpoints.size()); for(size_t i=0; i < this->dest_endpoints.size(); i++){ - int sck = tryConnect(this->dest_endpoints[i]); + int sck = tryConnect(this->dest_endpoints[i].second); if (sck <= 0) return -1; sockets[i] = sck; socketsCounters[sck] = messageOTF; @@ -462,8 +462,8 @@ class ff_dsenderH : public ff_dsender { public: - ff_dsenderH(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} - ff_dsenderH(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderH(std::pair e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderH(std::vector> dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} int handshakeHandler(const int sck, bool isInternal){ if (sendGroupName(sck) < 0) return -1; @@ -479,7 +479,7 @@ class ff_dsenderH : public ff_dsender { FD_ZERO(&set); FD_ZERO(&tmpset); - for(const auto& endpoint : this->dest_endpoints){ + for(const auto& [ct, endpoint] : this->dest_endpoints){ int sck = tryConnect(endpoint); if (sck <= 0) return -1; bool isInternal = internalGroupNames.contains(endpoint.groupName); diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index ebba8449..424a44e7 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -111,7 +111,7 @@ class ff_dsenderMPI: public ff_minode_t { std::map rankCounters; std::map>> buffers; std::vector ranks; - std::vector destRanks; + std::vector> destRanks; std::string gName; int batchSize; int messageOTF; @@ -215,19 +215,19 @@ class ff_dsenderMPI: public ff_minode_t { } public: - ff_dsenderMPI(ff_endpoint destRank, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) + ff_dsenderMPI(std::pair destRank, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { this->destRanks.push_back(std::move(destRank)); } - ff_dsenderMPI( std::vector destRanks_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) + ff_dsenderMPI( std::vector> destRanks_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : destRanks(std::move(destRanks_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} int svc_init() { if (coreid!=-1) ff_mapThreadToCpu(coreid); - for(ff_endpoint& ep: this->destRanks){ + for(auto& [ct, ep]: this->destRanks){ handshakeHandler(ep.getRank(), false); //rankCounters[ep.getRank()] = messageOTF; ranks.push_back(ep.getRank()); @@ -320,8 +320,8 @@ class ff_dsenderHMPI : public ff_dsenderMPI { public: - ff_dsenderHMPI(ff_endpoint e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} - ff_dsenderHMPI(std::vector dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderHMPI(std::pair e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} + ff_dsenderHMPI(std::vector> dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} int handshakeHandler(const int rank, bool isInternal){ sendGroupName(rank); @@ -334,7 +334,7 @@ class ff_dsenderHMPI : public ff_dsenderMPI { if (coreid!=-1) ff_mapThreadToCpu(coreid); - for(auto& endpoint : this->destRanks){ + for(auto& [ct, endpoint] : this->destRanks){ int rank = endpoint.getRank(); bool isInternal = internalGroupNames.contains(endpoint.groupName); if (isInternal) @@ -365,7 +365,6 @@ class ff_dsenderHMPI : public ff_dsenderMPI { } else rank = getMostFilledInternalBufferRank(); - auto& buffs = buffers[rank]; if (buffs.second[buffs.first]->push(task)) // the push triggered a flush, so we must go ion the next buffer buffs.first = (buffs.first + 1) % buffs.second.size(); // increment the used buffer of 1 diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index 7f80bb5b..a761b9cd 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -62,6 +62,7 @@ #endif enum Proto {TCP , MPI}; +enum ChannelType {FWD, INT, FBK}; class dataBuffer: public std::stringbuf { public: diff --git a/tests/distributed/test_group23.cpp b/tests/distributed/test_group23.cpp index 71b126cf..c894b332 100644 --- a/tests/distributed/test_group23.cpp +++ b/tests/distributed/test_group23.cpp @@ -45,8 +45,12 @@ struct myTask_t { } }; -struct Node1: ff_monode_t{ +struct Node1: ff_node_t{ Node1(long ntasks):ntasks(ntasks),cnt(ntasks) {} + int svc_init(){ + ff::cout << "Node1 initialized!\n"; + return 0; + } myTask_t* svc(myTask_t*in){ long min = std::min(cnt,appbatch); for(long i=0; i < min; i++) { @@ -75,6 +79,10 @@ struct Node1: ff_monode_t{ struct Node2: ff_minode_t{ Node2(long ntasks):ntasks(ntasks) {} + int svc_init(){ + ff::cout << "Node2 initialized!\n"; + return 0; + } myTask_t* svc(myTask_t* t){ if (fromInput()) { t->str += std::string(" World"); @@ -96,6 +104,10 @@ struct Node2: ff_minode_t{ bool eosreceived=false; }; struct Node3: ff_monode_t{ + int svc_init(){ + ff::cout << "Node3 initialized!\n"; + return 0; + } myTask_t* svc(myTask_t* t){ if (t->str == "Hello World") { t->str = "Feedback!"; @@ -109,8 +121,12 @@ struct Node3: ff_monode_t{ } }; -struct Node4: ff_minode_t{ +struct Node4: ff_node_t{ Node4(long ntasks):ntasks(ntasks) {} + int svc_init(){ + ff::cout << "Node4 initialized!\n"; + return 0; + } myTask_t* svc(myTask_t* t){ ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; ++processed; From fb59ee7bc069f9d8989683fe196718d627f41f68 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Fri, 10 Jun 2022 10:24:56 +0200 Subject: [PATCH 177/202] fixes for feedbacks (still not fully working) --- ff/distributed/ff_dgroup.hpp | 2 +- ff/distributed/ff_wrappers.hpp | 27 +++++++++-------- ff/farm.hpp | 4 +-- ff/lb.hpp | 4 +-- tests/distributed/test_group23.cpp | 3 ++ tests/distributed/test_group24.cpp | 46 +++++++++++++++++++---------- tests/distributed/test_group24.json | 6 ++-- 7 files changed, 57 insertions(+), 35 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 481ae9a0..80f2f124 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -113,8 +113,8 @@ class dGroup : public ff::ff_farm { workers.push_back(wrapper); if (ir.isSource) wrapper->skipfirstpop(true); } else if (ir.hasReceiver) { - if (ir.isSource) wrapper->skipfirstpop(true); wrapper = buildWrapperIN(child); + if (ir.isSource) wrapper->skipfirstpop(true); workers.push_back(wrapper); } else { wrapper = buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels); diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 1179f039..6b4579f3 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -226,18 +226,21 @@ class WrapperINOUT: public internal_mi_transformer { void svc_end(){this->n->svc_end();} void * svc(void* in) { - message_t* msg = (message_t*)in; - - if (this->n->isMultiInput()) { - int channelid = msg->sender; - ff_minode* mi = reinterpret_cast(this->n); - mi->set_input_channelid(channelid, true); - } - bool datacopied=true; - void* out = n->svc(this->n->deserializeF(msg->data, datacopied)); - if (!datacopied) msg->data.doNotCleanup(); - delete msg; - + void* out; + if (in != nullptr) { + message_t* msg = (message_t*)in; + + if (this->n->isMultiInput()) { + int channelid = msg->sender; + ff_minode* mi = reinterpret_cast(this->n); + mi->set_input_channelid(channelid, true); + } + bool datacopied=true; + out = n->svc(this->n->deserializeF(msg->data, datacopied)); + if (!datacopied) msg->data.doNotCleanup(); + delete msg; + } else // it can happen if we have a feedback channel + out = n->svc(nullptr); serialize(out, defaultDestination); return GO_ON; } diff --git a/ff/farm.hpp b/ff/farm.hpp index ae7ebfcc..9e60c84b 100644 --- a/ff/farm.hpp +++ b/ff/farm.hpp @@ -1375,7 +1375,7 @@ class ff_farm: public ff_node { assert(blocking_in==blocking_out); workers[i]->blocking_mode(blocking_in); if (!default_mapping) workers[i]->no_mapping(); - workers[i]->skipfirstpop(false); + //workers[i]->skipfirstpop(false); if (workers[i]->freeze_and_run(true)<0) { error("FARM, spawning worker thread\n"); return -1; @@ -1388,7 +1388,7 @@ class ff_farm: public ff_node { assert(blocking_in==blocking_out); workers[i]->blocking_mode(blocking_in); if (!default_mapping) workers[i]->no_mapping(); - workers[i]->skipfirstpop(false); + //workers[i]->skipfirstpop(false); if (workers[i]->run(true)<0) { error("FARM, spawning worker thread\n"); return -1; diff --git a/ff/lb.hpp b/ff/lb.hpp index 6f33a913..4a730296 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -1196,7 +1196,7 @@ class ff_loadbalancer: public ff_thread { assert(blocking_in==blocking_out); workers[i]->blocking_mode(blocking_in); if (!default_mapping) workers[i]->no_mapping(); - workers[i]->skipfirstpop(false); + //workers[i]->skipfirstpop(false); if (workers[i]->freeze_and_run(true)<0) { error("LB, spawning worker thread\n"); return -1; @@ -1209,7 +1209,7 @@ class ff_loadbalancer: public ff_thread { assert(blocking_in==blocking_out); workers[i]->blocking_mode(blocking_in); if (!default_mapping) workers[i]->no_mapping(); - workers[i]->skipfirstpop(false); + //workers[i]->skipfirstpop(false); if (workers[i]->run(true)<0) { error("LB, spawning worker thread\n"); return -1; diff --git a/tests/distributed/test_group23.cpp b/tests/distributed/test_group23.cpp index c894b332..a347503b 100644 --- a/tests/distributed/test_group23.cpp +++ b/tests/distributed/test_group23.cpp @@ -113,6 +113,9 @@ struct Node3: ff_monode_t{ t->str = "Feedback!"; t->S.t += 1; t->S.f += 1.0; + + std::cerr << "Node3, sending task back\n"; + ff_send_out_to(t, 0); // sends it back return GO_ON; } diff --git a/tests/distributed/test_group24.cpp b/tests/distributed/test_group24.cpp index 54dbfffd..890d652c 100644 --- a/tests/distributed/test_group24.cpp +++ b/tests/distributed/test_group24.cpp @@ -1,23 +1,35 @@ /* * FastFlow concurrent network: * - * Client ->| - * | -> level1Gatherer ->| - * Client ->| | - * | - * .... | --> level0Gatherer - * | - * Client ->| | - * | -> level1Gatherer ->| - * Client ->| + * ----------------------------------------------------------- + * | /<------------ a2a(0)----------->/ | + * | ------------------------------- | + * | | | | + * | | Client ->| | | + * | | | -> level1Gatherer | ->| | + * | | Client ->| | | | + * | | | | | + * | ------------------------------- | | + * | .... | --> level0Gatherer | + * | | | + * | ------------------------------- | | + * | | | | | + * | | Client ->| | | | + * | | | -> level1Gatherer | ->| | + * | | Client ->| | | + * | | | | + * | ------------------------------- | + * | /<------------ a2a(n)----------->/ | + * | | + * ------------------------------------------------------------ + * /<-------------------------- mainA2A ----------------------->/ * * * distributed version: * - * - * G1: Node1 - * G2: a2a - * G3: Node4 + * each a2a(i) is a group, a2a(i) --> Gi i>0 + * level0Gatherer: G0 + * */ @@ -104,7 +116,7 @@ int main(int argc, char*argv[]){ std::vector globalLeft; for(size_t i=0;iadd_stage(a2a, true); std::vector localLeft; @@ -115,14 +127,18 @@ int main(int argc, char*argv[]){ globalLeft.push_back(pipe); + // create here Gi groups for each a2a(i) i>0 auto g = mainA2A.createGroup("G"+std::to_string(i+1)); - g << pipe; + g << pipe; + // ---------------------------------------- } mainA2A.add_firstset(globalLeft, true); mainA2A.add_secondset({&root}); + // adding the root node as G0 mainA2A.createGroup("G0") << root; + // ------------------------------- if (mainA2A.run_and_wait_end()<0) { error("running the main All-to-All\n"); diff --git a/tests/distributed/test_group24.json b/tests/distributed/test_group24.json index f76ad1ba..19eef835 100644 --- a/tests/distributed/test_group24.json +++ b/tests/distributed/test_group24.json @@ -9,11 +9,11 @@ "name" : "G2" }, { - "endpoint" : "localhost:8001", + "endpoint" : "localhost:8002", "name" : "G3" - }, + } { - "endpoint" : "localhost:8006", + "endpoint" : "localhost:8010", "name" : "G0" } ] From 622843e78aafe6f8a32cb28cf08d0782ad066695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Fri, 10 Jun 2022 11:08:05 +0200 Subject: [PATCH 178/202] Fixed bug related to skipfirstpop on vertical groups. Fixed some tests with the new dsender constructor signature. --- ff/distributed/ff_dgroup.hpp | 4 ++-- ff/distributed/ff_dgroups.hpp | 2 +- tests/distributed/test_a2a_h1.cpp | 4 ++-- tests/distributed/test_a2a_h2.cpp | 6 +++--- tests/distributed/test_a2a_h3.cpp | 6 +++--- tests/distributed/test_a2a_h4.cpp | 8 ++++---- tests/distributed/test_group24.json | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 80f2f124..6d87e65e 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -111,10 +111,10 @@ class dGroup : public ff::ff_farm { if (ir.hasReceiver && ir.hasSender) { wrapper = new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes)); workers.push_back(wrapper); - if (ir.isSource) wrapper->skipfirstpop(true); + if (ir.isSource && ir.isVertical() && ir.hasLeftChildren()) wrapper->skipfirstpop(true); } else if (ir.hasReceiver) { wrapper = buildWrapperIN(child); - if (ir.isSource) wrapper->skipfirstpop(true); + if (ir.isSource && ir.isVertical() && ir.hasLeftChildren()) wrapper->skipfirstpop(true); workers.push_back(wrapper); } else { wrapper = buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels); diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index 24ca37b8..e5da88c2 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -450,7 +450,7 @@ class dGroups { // inserisci tutte i gruppi di questo bb a destra for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) if (!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasRightChildren()) - runningGroup_IR.destinationEndpoints.push_back({ChannelType::FWD, annotatedGroups[gName].listenEndpoint}); + runningGroup_IR.destinationEndpoints.push_back({ChannelType::INT, annotatedGroups[gName].listenEndpoint}); } else { if (!runningGroup_IR.isVertical()){ // inserisci tutti i gruppi come sopra diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp index 2aa6b171..fb7fcd65 100644 --- a/tests/distributed/test_a2a_h1.cpp +++ b/tests/distributed/test_a2a_h1.cpp @@ -71,7 +71,7 @@ int main(int argc, char*argv[]){ if (atoi(argv[1]) == 0){ dGroups::Instance()->setRunningGroup("G1"); gFarm.add_emitter(new ff_dreceiverH(g1, 1, {{0, 0}}, {0})); - gFarm.add_collector(new ff_dsenderH(g2)); + gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g2})); auto ea1 = new EmitterAdapter(new Source(2,0), 2, 0, {{0,0}}, true); ea1->skipallpop(true); auto ea2 = new EmitterAdapter(new Source(2,1), 2, 1, {{0,0}}, true); ea2->skipallpop(true); @@ -82,7 +82,7 @@ int main(int argc, char*argv[]){ } else { dGroups::Instance()->setRunningGroup("G2"); gFarm.add_emitter(new ff_dreceiverH(g2, 1, {{0, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH(g1)); + gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g1})); auto ea = new EmitterAdapter(new Source(2,2), 2, 2, {{1,0}}, true); ea->skipallpop(true); diff --git a/tests/distributed/test_a2a_h2.cpp b/tests/distributed/test_a2a_h2.cpp index 4ee9571e..1c48c23d 100644 --- a/tests/distributed/test_a2a_h2.cpp +++ b/tests/distributed/test_a2a_h2.cpp @@ -90,7 +90,7 @@ int main(int argc, char*argv[]){ if (atoi(argv[1]) == 0){ dGroups::Instance()->setRunningGroup("G0"); - gFarm.add_collector(new ff_dsender({g1, g2})); + gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g1}, {ChannelType::FWD, g2}})); gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); gFarm.run_and_wait_end(); @@ -98,7 +98,7 @@ int main(int argc, char*argv[]){ } else if (atoi(argv[1]) == 1){ dGroups::Instance()->setRunningGroup("G1"); gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0})); - gFarm.add_collector(new ff_dsenderH(g2)); + gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g2})); auto s = new Source(2,0); auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); @@ -110,7 +110,7 @@ int main(int argc, char*argv[]){ } else { dGroups::Instance()->setRunningGroup("G2"); gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH(g1)); + gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g1})); auto s = new Source(2,1); auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp index 67bf221d..63c0f6bd 100644 --- a/tests/distributed/test_a2a_h3.cpp +++ b/tests/distributed/test_a2a_h3.cpp @@ -103,14 +103,14 @@ int main(int argc, char*argv[]){ ff_a2a a2a; if (atoi(argv[1]) == 0){ - gFarm.add_collector(new ff_dsender({g1, g2}, "G0")); + gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g1}, {ChannelType::FWD, g2}}, "G0")); gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); gFarm.run_and_wait_end(); return 0; } else if (atoi(argv[1]) == 1){ gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0}, {"G2"})); - gFarm.add_collector(new ff_dsenderH({g2,g3}, "G1", {"G2"})); + gFarm.add_collector(new ff_dsenderH({{ChannelType::INT, g2},{ChannelType::FWD, g3}}, "G1", {"G2"})); auto s = new Source(2,0); auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); @@ -121,7 +121,7 @@ int main(int argc, char*argv[]){ } else if (atoi(argv[1]) == 2) { gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1}, {"G1"})); - gFarm.add_collector(new ff_dsenderH({g1, g3}, "G2", {"G1"})); + gFarm.add_collector(new ff_dsenderH({{ChannelType::INT, g1}, {ChannelType::FWD, g3}}, "G2", {"G1"})); gFarm.cleanup_emitter(); gFarm.cleanup_collector(); diff --git a/tests/distributed/test_a2a_h4.cpp b/tests/distributed/test_a2a_h4.cpp index 9b6e4378..767b29c7 100644 --- a/tests/distributed/test_a2a_h4.cpp +++ b/tests/distributed/test_a2a_h4.cpp @@ -114,7 +114,7 @@ int main(int argc, char*argv[]){ if (atoi(argv[1]) == 0){ dGroups::Instance()->setRunningGroup("G0"); - gFarm.add_collector(new ff_dsender({g1, g3})); + gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g1}, {ChannelType::FWD, g3}})); gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); gFarm.run_and_wait_end(); @@ -122,7 +122,7 @@ int main(int argc, char*argv[]){ } else if (atoi(argv[1]) == 1){ dGroups::Instance()->setRunningGroup("G1"); gFarm.add_emitter(new ff_dreceiver(g1, 1, {{0, 0}})); - gFarm.add_collector(new ff_dsender({g2,g3})); + gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g2}, {ChannelType::FWD, g3}})); gFarm.add_workers({new WrapperINOUT(new Source(2,0), 1, true)}); @@ -132,7 +132,7 @@ int main(int argc, char*argv[]){ } else if (atoi(argv[1]) == 2){ dGroups::Instance()->setRunningGroup("G2"); gFarm.add_emitter(new ff_dreceiver(g2, 2, {{0, 0}})); - gFarm.add_collector(new ff_dsender(g4)); + gFarm.add_collector(new ff_dsender({ChannelType::FWD, g4})); gFarm.add_workers({new WrapperINOUT(new Sink(0), 1, true)}); @@ -142,7 +142,7 @@ int main(int argc, char*argv[]){ } else if (atoi(argv[1]) == 3) { dGroups::Instance()->setRunningGroup("G3"); gFarm.add_emitter(new ff_dreceiverH(g3, 2, {{1, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH({g2, g4})); + gFarm.add_collector(new ff_dsenderH({{ChannelType::FWD, g2}, {ChannelType::FWD, g4}})); gFarm.cleanup_emitter(); gFarm.cleanup_collector(); diff --git a/tests/distributed/test_group24.json b/tests/distributed/test_group24.json index 19eef835..42102bf8 100644 --- a/tests/distributed/test_group24.json +++ b/tests/distributed/test_group24.json @@ -11,7 +11,7 @@ { "endpoint" : "localhost:8002", "name" : "G3" - } + }, { "endpoint" : "localhost:8010", "name" : "G0" From a0e2bafd3132bf46864982e8a26bfa872e934a19 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 14 Jun 2022 11:55:54 +0200 Subject: [PATCH 179/202] improved the distributed support for feedback channels --- ff/all2all.hpp | 27 ++++- ff/distributed/ff_dgroup.hpp | 7 +- ff/distributed/ff_wrappers.hpp | 6 +- ff/gt.hpp | 4 +- ff/pipeline.hpp | 1 + ff/svector.hpp | 22 ++-- tests/distributed/test_group23.cpp | 33 +++--- tests/distributed/test_group25.cpp | 149 ++++++++++++++++++++++++++++ tests/distributed/test_group25.json | 12 +++ 9 files changed, 221 insertions(+), 40 deletions(-) create mode 100644 tests/distributed/test_group25.cpp create mode 100644 tests/distributed/test_group25.json diff --git a/ff/all2all.hpp b/ff/all2all.hpp index 84bd32a6..cd0cfd97 100644 --- a/ff/all2all.hpp +++ b/ff/all2all.hpp @@ -343,6 +343,30 @@ class ff_a2a: public ff_node { in_buffer_entries(in_buffer_entries), out_buffer_entries(out_buffer_entries) {} + + ff_a2a(const ff_a2a& p):prepared(false) { + if (p.prepared) { + error("ff_a2a, copy constructor, the input all-to-all has already been prepared\n"); + return; + } + + workers1 = p.workers1; + workers2 = p.workers2; + workers1_cleanup = p.workers1_cleanup; + workers2_cleanup = p.workers2_cleanup; + fixedsizeIN = p.fixedsizeIN; + fixedsizeOUT = p.fixedsizeOUT; + in_buffer_entries = p.in_buffer_entries; + out_buffer_entries = p.out_buffer_entries; + wraparound = p.wraparound; + ondemand_chunk = p.ondemand_chunk; + outputNodes = p.outputNodes; + internalSupportNodes = p.internalSupportNodes; + + // this is a dirty part, we modify a const object..... + ff_a2a* dirty= const_cast(&p); + dirty->internalSupportNodes.resize(0); + } virtual ~ff_a2a() { if (barrier) delete barrier; @@ -350,9 +374,8 @@ class ff_a2a: public ff_node { workers1[i] = nullptr; for(size_t i=0;ichange_node(input, buildWrapperIN(input), true); //cleanup?? removefromcleanuplist?? + if (inputParent) { + ff_node* wrapper = buildWrapperIN(input); + inputParent->change_node(input, wrapper, true); //cleanup?? removefromcleanuplist?? + if (ir.isSource && ir.isVertical() && ir.hasLeftChildren()) + inputParent->skipfirstpop(true); + } } } diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 6b4579f3..f0e071b0 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -70,15 +70,17 @@ class WrapperIN: public internal_mi_transformer { } void * svc(void* in) { + // with feedback channels in might be null + if (in == nullptr) return n->svc(nullptr); message_t* msg = (message_t*)in; if (this->n->isMultiInput()) { - int channelid = msg->sender; + int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, fromInput()); if (!this->fromInput()) return n->svc(in); } - + bool datacopied=true; void* inputData = this->n->deserializeF(msg->data, datacopied); if (!datacopied) msg->data.doNotCleanup(); diff --git a/ff/gt.hpp b/ff/gt.hpp index 44cd5581..f1404dd8 100644 --- a/ff/gt.hpp +++ b/ff/gt.hpp @@ -581,7 +581,9 @@ class ff_gatherer: public ff_thread { // In this case we want to call notifyeos only when we have received EOS from all // input channel. bool notify_each_eos = filter ? (filter->neos==1): false; - // TODO: inserire skipallpop! + + // TODO: skipallpop missing! + gettimeofday(&wtstart,NULL); do { task = NULL; diff --git a/ff/pipeline.hpp b/ff/pipeline.hpp index 34003d01..6cfe8b21 100644 --- a/ff/pipeline.hpp +++ b/ff/pipeline.hpp @@ -866,6 +866,7 @@ class ff_pipeline: public ff_node { node_cleanup = p.node_cleanup; fixedsizeIN = p.fixedsizeIN; fixedsizeOUT = p.fixedsizeOUT; + wraparound = p.wraparound; in_buffer_entries = p.in_buffer_entries; out_buffer_entries = p.out_buffer_entries; nodes_list = p.nodes_list; diff --git a/ff/svector.hpp b/ff/svector.hpp index a3cf9633..4e91b6c7 100644 --- a/ff/svector.hpp +++ b/ff/svector.hpp @@ -62,11 +62,7 @@ namespace ff { template class svector { - - /** - * TODO - */ - enum {SVECTOR_CHUNK=1024}; + enum {SVECTOR_CHUNK=512}; public: typedef T* iterator; typedef const T* const_iterator; @@ -76,7 +72,7 @@ class svector { * * @param chunk is the allocation chunk */ - svector(size_t chunk=SVECTOR_CHUNK):first(NULL),len(0),cap(0),chunk(chunk) { + svector(size_t chunk=SVECTOR_CHUNK):first(NULL),len(0),cap(0),chunk(chunk?chunk:1) { reserve(chunk); } @@ -133,12 +129,10 @@ class svector { * Copy */ svector& operator=(const svector & v) { - if(!v.len) clear(); - else { + if (this != &v) { + clear(); const_iterator i1=v.begin(); const_iterator i2=v.end(); - if (first) { clear(); ::free(first); } - first=(vector_type*)::malloc((i2-i1)*sizeof(vector_type)); while(i1!=i2) push_back(*(i1++)); } return *this; @@ -196,13 +190,15 @@ class svector { */ inline void push_back(const vector_type & elem) { if (len==cap) reserve(cap+chunk); - new (first + len++) vector_type(elem); + new (first + len++) vector_type(elem); } /** * pop_back */ - inline void pop_back() { (first + --len)->~vector_type(); } + inline void pop_back() { + (first + --len)->~vector_type(); + } /** * back @@ -325,7 +321,7 @@ class svector { const vector_type& operator[](size_t n) const { return first[n]; } private: - vector_type * first; + vector_type* first; size_t len; size_t cap; size_t chunk; diff --git a/tests/distributed/test_group23.cpp b/tests/distributed/test_group23.cpp index a347503b..12d99b7a 100644 --- a/tests/distributed/test_group23.cpp +++ b/tests/distributed/test_group23.cpp @@ -47,10 +47,6 @@ struct myTask_t { struct Node1: ff_node_t{ Node1(long ntasks):ntasks(ntasks),cnt(ntasks) {} - int svc_init(){ - ff::cout << "Node1 initialized!\n"; - return 0; - } myTask_t* svc(myTask_t*in){ long min = std::min(cnt,appbatch); for(long i=0; i < min; i++) { @@ -79,10 +75,6 @@ struct Node1: ff_node_t{ struct Node2: ff_minode_t{ Node2(long ntasks):ntasks(ntasks) {} - int svc_init(){ - ff::cout << "Node2 initialized!\n"; - return 0; - } myTask_t* svc(myTask_t* t){ if (fromInput()) { t->str += std::string(" World"); @@ -104,10 +96,6 @@ struct Node2: ff_minode_t{ bool eosreceived=false; }; struct Node3: ff_monode_t{ - int svc_init(){ - ff::cout << "Node3 initialized!\n"; - return 0; - } myTask_t* svc(myTask_t* t){ if (t->str == "Hello World") { t->str = "Feedback!"; @@ -126,10 +114,6 @@ struct Node3: ff_monode_t{ struct Node4: ff_node_t{ Node4(long ntasks):ntasks(ntasks) {} - int svc_init(){ - ff::cout << "Node4 initialized!\n"; - return 0; - } myTask_t* svc(myTask_t* t){ ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; ++processed; @@ -180,11 +164,18 @@ int main(int argc, char*argv[]){ pipe.wrap_around(); //----- defining the distributed groups ------ - - auto G1 = n1.createGroup("G1"); - auto G2 = pipe1.createGroup("G2"); - auto G3 = n4.createGroup("G3"); - + // option 1 + n1.createGroup("G1"); + pipe1.createGroup("G2"); + n4.createGroup("G3"); + + // option 2 + //auto G1 = pipe.createGroup("G1"); + //G1 << n1; + //auto G2 = pipe.createGroup("G2"); + //G2 << pipe1; + //auto G3 = pipe.createGroup("G3"); + //G3 << n4; // ------------------------------------------- if (pipe.run_and_wait_end()<0) { diff --git a/tests/distributed/test_group25.cpp b/tests/distributed/test_group25.cpp new file mode 100644 index 00000000..dc9c17fd --- /dev/null +++ b/tests/distributed/test_group25.cpp @@ -0,0 +1,149 @@ +/* + * FastFlow concurrent network: + * + * /<------------- pipe -------------->/ + * /<-------- a2a -------->/ + * + * | -> Node3 ->| + * |-> Node2->| | + * Node1--->| | -> Node3 ->| -- + * ^ |-> Node2->| | | + * | | -> Node3 ->| | + * | | + * ---------------------------------- + * + * + * distributed version: + * + * Node1 -> G1 + * a2a -> G2 + * + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + myTask_t() {} + myTask_t(myTask_t* t){ + str = std::string(t->str); + S.t = t->S.t; + S.f = t->S.f; + } + + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } +}; + +struct Node1Helper: ff_minode_t { + myTask_t* svc(myTask_t*in) { + return in; + } +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks),cnt(ntasks) {} + myTask_t* svc(myTask_t*in){ + long min = std::min(cnt,appbatch); + for(long i=0; i < min; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out(task); + --cnt; + } + if (in) { + ++back; + delete in; + } + if (back == ntasks) { + return EOS; + } + return GO_ON; + } + void svc_end() { + if (back != ntasks) { + std::cerr << "ERROR\n"; + exit(-1); + } + std::cout << "RESULT OK\n"; + } + + const long ntasks; + long back=0,cnt; + long appbatch=10; +}; + + +struct Node2: ff_monode_t{ + myTask_t* svc(myTask_t* t){ + t->str += std::string(" World"); + return t; + } +}; +struct Node3: ff_minode_t{ + myTask_t* svc(myTask_t* t){ + t->str = "Feedback!"; + t->S.t += 1; + t->S.f += 1.0; + //std::cout << "Node3(" << get_my_id() << ") sending task back\n"; + return t; + } +}; + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 100; + if (argc>1) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] + << " ntasks\n"; + return -1; + } + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + ff_comb n1(new Node1Helper, new Node1(ntasks), true, true); + + ff_a2a a2a; + a2a.add_firstset({new Node2, new Node2}, 0, true); + a2a.add_secondset({new Node3, new Node3, new Node3}, true); + + pipe.add_stage(&n1); + pipe.add_stage(&a2a); + pipe.wrap_around(); + + //----- defining the distributed groups ------ + //auto G0 = pipe.createGroup("G1"); + //G0 << pipe.get_firststage(); + //auto G1 = pipe.createGroup("G2"); + //G1 << pipe.get_laststage(); + n1.createGroup("G1"); + a2a.createGroup("G2"); + // ------------------------------------------- + + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + + return 0; +} diff --git a/tests/distributed/test_group25.json b/tests/distributed/test_group25.json new file mode 100644 index 00000000..18e4f6b9 --- /dev/null +++ b/tests/distributed/test_group25.json @@ -0,0 +1,12 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8000", + "name" : "G1" + }, + { + "endpoint" : "localhost:8001", + "name" : "G2" + } + ] +} From f03eaf1a42e356a7a0b360e41c49c51d794cb8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Tue, 21 Jun 2022 15:35:40 +0200 Subject: [PATCH 180/202] Added new support for feedback channels at level 0 and level 1 of the dataflow graph. Still some minor bugs are present and need to be fixed. Routing tables are no more exchanged at "runtime", but statically computed during the bootstrap of the application. --- ff/distributed/ff_batchbuffer.hpp | 10 +- ff/distributed/ff_dgroup.hpp | 38 +++--- ff/distributed/ff_dgroups.hpp | 60 +++++---- ff/distributed/ff_dintermediate.hpp | 16 ++- ff/distributed/ff_dreceiver.hpp | 62 +++++---- ff/distributed/ff_dreceiverMPI.hpp | 59 ++++----- ff/distributed/ff_dsender.hpp | 179 ++++++++------------------ ff/distributed/ff_dsenderMPI.hpp | 178 ++++++-------------------- ff/distributed/ff_network.hpp | 2 + ff/distributed/ff_wrappers.hpp | 55 +++++--- tests/distributed/test_a2a_h1.cpp | 95 -------------- tests/distributed/test_a2a_h2.cpp | 124 ------------------ tests/distributed/test_a2a_h3.cpp | 151 ---------------------- tests/distributed/test_a2a_h4.cpp | 173 ------------------------- tests/distributed/test_group26.cpp | 189 ++++++++++++++++++++++++++++ tests/distributed/test_group26.json | 24 ++++ tests/distributed/test_group7.cpp | 9 +- 17 files changed, 485 insertions(+), 939 deletions(-) delete mode 100644 tests/distributed/test_a2a_h1.cpp delete mode 100644 tests/distributed/test_a2a_h2.cpp delete mode 100644 tests/distributed/test_a2a_h3.cpp delete mode 100644 tests/distributed/test_a2a_h4.cpp create mode 100644 tests/distributed/test_group26.cpp create mode 100644 tests/distributed/test_group26.json diff --git a/ff/distributed/ff_batchbuffer.hpp b/ff/distributed/ff_batchbuffer.hpp index c77d5b96..f03b4782 100644 --- a/ff/distributed/ff_batchbuffer.hpp +++ b/ff/distributed/ff_batchbuffer.hpp @@ -11,9 +11,9 @@ class ff_batchBuffer { std::vector> toCleanup; public: int size = 0; - ff_batchBuffer() { - } - ff_batchBuffer(int _size, std::function cbk) : callback(cbk), batchSize(_size) { + ChannelType ct; + ff_batchBuffer() {} + ff_batchBuffer(int _size, ChannelType ct, std::function cbk) : callback(cbk), batchSize(_size), ct(ct) { if (_size*4+1 > UIO_MAXIOV){ error("Size too big!\n"); abort(); @@ -37,8 +37,8 @@ class ff_batchBuffer { iov[indexBase+3].iov_len = sizeof(size_t); iov[indexBase+4].iov_base = m->data.getPtr(); iov[indexBase+4].iov_len = m->data.getLen(); - - toCleanup.emplace_back(sz, m); + + toCleanup.push_back(std::make_pair(sz, m)); if (++size == batchSize) return this->flush(); diff --git a/ff/distributed/ff_dgroup.hpp b/ff/distributed/ff_dgroup.hpp index 32339236..2741aff3 100644 --- a/ff/distributed/ff_dgroup.hpp +++ b/ff/distributed/ff_dgroup.hpp @@ -84,9 +84,9 @@ class dGroup : public ff::ff_farm { return new WrapperIN(n); } - static ff_node* buildWrapperOUT(ff_node* n, int id, int outputChannels){ - if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF, n->freetaskF), id, 1, true), false, true); - return new WrapperOUT(n, id, outputChannels); + static ff_node* buildWrapperOUT(ff_node* n, int id, int outputChannels, int feedbackChannels = 0){ + if (n->isMultiInput()) return new ff_comb(n, new WrapperOUT(new ForwarderNode(n->serializeF, n->freetaskF), id, outputChannels, feedbackChannels, true), false, true); + return new WrapperOUT(n, id, outputChannels, feedbackChannels); } public: @@ -94,8 +94,11 @@ class dGroup : public ff::ff_farm { if (ir.isVertical()){ int outputChannels = 0; - if (ir.hasSender) - outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+f.second.first.size();}); + int feedbacksChannels = 0; + if (ir.hasSender){ + outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+ (f.second.second != ChannelType::FBK ? f.second.first.size() : 0);}); + feedbacksChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second == ChannelType::FBK ? f.second.first.size() : 0);}); + } std::vector reverseOutputIndexes(ir.hasLeftChildren() ? ir.outputL.rbegin() : ir.outputR.rbegin(), ir.hasLeftChildren() ? ir.outputL.rend() : ir.outputR.rend()); for(ff_node* child: (ir.hasLeftChildren() ? ir.L : ir.R)){ @@ -109,7 +112,7 @@ class dGroup : public ff::ff_farm { if (isSeq(child)){ ff_node* wrapper = nullptr; if (ir.hasReceiver && ir.hasSender) { - wrapper = new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes)); + wrapper = new WrapperINOUT(child, getBackAndPop(reverseOutputIndexes), 1, feedbacksChannels); workers.push_back(wrapper); if (ir.isSource && ir.isVertical() && ir.hasLeftChildren()) wrapper->skipfirstpop(true); } else if (ir.hasReceiver) { @@ -117,7 +120,7 @@ class dGroup : public ff::ff_farm { if (ir.isSource && ir.isVertical() && ir.hasLeftChildren()) wrapper->skipfirstpop(true); workers.push_back(wrapper); } else { - wrapper = buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels); + wrapper = buildWrapperOUT(child, getBackAndPop(reverseOutputIndexes), outputChannels, feedbacksChannels); workers.push_back(wrapper); } // TODO: in case there are feedback channels we cannot skip all pops! @@ -144,7 +147,7 @@ class dGroup : public ff::ff_farm { if (ir.hasSender){ for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes), outputChannels), true); // cleanup?? removefromcleanuplist?? + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseOutputIndexes), outputChannels, feedbacksChannels), true); // cleanup?? removefromcleanuplist?? } } @@ -164,11 +167,11 @@ class dGroup : public ff::ff_farm { if (ir.hasSender){ if(ir.protocol == Proto::TCP) - this->add_collector(new ff_dsender(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); + this->add_collector(new ff_dsender(ir.destinationEndpoints, &ir.routingTable, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); #ifdef DFF_MPI else - this->add_collector(new ff_dsenderMPI(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); + this->add_collector(new ff_dsenderMPI(ir.destinationEndpoints, &ir.routingTable, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF), true); #endif } @@ -229,7 +232,8 @@ class dGroup : public ff::ff_farm { }); innerA2A->add_firstset(firstSet, reinterpret_cast(ir.parentBB)->ondemand_buffer()); // note the ondemand!! - int outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second ? 0 : f.second.first.size());}); + int outputChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+ (f.second.second == ChannelType::FWD ? f.second.first.size() : 0);}); + int feedbacksChannels = std::accumulate(ir.routingTable.begin(), ir.routingTable.end(), 0, [](const auto& s, const auto& f){return s+(f.second.second == ChannelType::FBK ? f.second.first.size() : 0);}); std::vector reverseRightOutputIndexes(ir.outputR.rbegin(), ir.outputR.rend()); std::vector secondSet; @@ -238,7 +242,7 @@ class dGroup : public ff::ff_farm { auto s = child->getSerializationFunction(); secondSet.push_back( (ir.isSink) ? (ff_node*)new CollectorAdapter(child, ir.outputL) - : (ff_node*)new ff_comb(new CollectorAdapter(child, ir.outputL), new WrapperOUT(new ForwarderNode(s.first, s.second), getBackAndPop(reverseRightOutputIndexes), 1, true), true, true) + : (ff_node*)new ff_comb(new CollectorAdapter(child, ir.outputL), new WrapperOUT(new ForwarderNode(s.first, s.second), getBackAndPop(reverseRightOutputIndexes), outputChannels, feedbacksChannels, true), true, true) ); } else { ff::svector inputs; child->get_in_nodes(inputs); @@ -251,7 +255,7 @@ class dGroup : public ff::ff_farm { ff::svector outputs; child->get_out_nodes(outputs); for(ff_node* output : outputs){ ff_node* outputParent = getBB(child, output); - if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseRightOutputIndexes), outputChannels), true); //cleanup?? removefromcleanuplist? + if (outputParent) outputParent->change_node(output, buildWrapperOUT(output, getBackAndPop(reverseRightOutputIndexes), outputChannels, feedbacksChannels), true); //cleanup?? removefromcleanuplist? } } @@ -275,19 +279,19 @@ class dGroup : public ff::ff_farm { if (ir.hasReceiver){ if (ir.protocol == Proto::TCP) - this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); + this->add_emitter(new ff_dreceiverH(ir.listenEndpoint, ir.expectedEOS, vector2Map(ir.inputL))); #ifdef DFF_MPI else - this->add_emitter(new ff_dreceiverHMPI(ir.expectedEOS, vector2Map(ir.inputL), ir.inputR, ir.otherGroupsFromSameParentBB)); + this->add_emitter(new ff_dreceiverHMPI(ir.expectedEOS, vector2Map(ir.inputL))); #endif } if (ir.hasSender){ if(ir.protocol == Proto::TCP) - this->add_collector(new ff_dsenderH(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF) , true); + this->add_collector(new ff_dsenderH(ir.destinationEndpoints, &ir.routingTable, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF) , true); #ifdef DFF_MPI else - this->add_collector(new ff_dsenderHMPI(ir.destinationEndpoints, ir.listenEndpoint.groupName, ir.otherGroupsFromSameParentBB, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF), true); + this->add_collector(new ff_dsenderHMPI(ir.destinationEndpoints, &ir.routingTable, ir.listenEndpoint.groupName, ir.outBatchSize, ir.messageOTF, ir.internalMessageOTF), true); #endif } } diff --git a/ff/distributed/ff_dgroups.hpp b/ff/distributed/ff_dgroups.hpp index e5da88c2..ea466666 100644 --- a/ff/distributed/ff_dgroups.hpp +++ b/ff/distributed/ff_dgroups.hpp @@ -167,11 +167,6 @@ class dGroups { std::cerr << "Error waiting the group!" << std::endl; return -1; } - - //ff_node* runningGroup = this->groups[this->runningGroup]; - - //if (runningGroup->run(parent) < 0) return -1; - //if (runningGroup->wait() < 0) return -1; #ifdef DFF_MPI if (usedProtocol == Proto::MPI) @@ -293,23 +288,30 @@ class dGroups { continue; // skip anyway } - // if i'm here it means that from this 1st level building block, multiple groups have been created! (An Error or an A2A or a Farm BB) + // if i'm here it means that from this 1st level building block, multiple groups have been created! (An Error or an A2A or a Pipe BB) + + // multiple groups created from a pipeline! if (pair.first->isPipe()){ + bool isMainPipe = pair.first == parentPipe; + ff_pipeline* originalPipe = reinterpret_cast(pair.first); + + // if the pipe coincide with the main Pipe, just ignore the fact that is wrapped around, since it will be handled later on! + bool iswrappedAround = isMainPipe ? false : originalPipe->isset_wraparound(); // check that all stages were annotated - for(ff_node* child : reinterpret_cast(pair.first)->getStages()) + for(ff_node* child : originalPipe->getStages()) if (!annotated.contains(child)){ - error("Need to abla "); + error("When create a group from a pipeline, all the stages must be annotated on a group!"); abort(); } for(auto& gName: pair.second){ - ff_pipeline* originalPipe = reinterpret_cast(pair.first); ff_pipeline* mypipe = new ff_pipeline; for(ff_node* child : originalPipe->getStages()){ if (annotated[child] == gName){ + // if the current builiding pipe has a stages, and the last one is not the previous i'm currently including there is a problem! if (mypipe->getStages().size() != 0 && originalPipe->get_stageindex(mypipe->get_laststage())+1 != originalPipe->get_stageindex(child)) { error("There are some stages missing in the annottation!\n"); abort(); @@ -321,7 +323,7 @@ class dGroups { bool head = mypipe->get_firststage() == originalPipe->get_firststage(); bool tail = mypipe->get_laststage() == originalPipe->get_laststage(); - if (((head && isSrc) || mypipe->isDeserializable()) && ((tail && isSnk) || mypipe->isSerializable())) + if (((head && isSrc && !iswrappedAround) || mypipe->isDeserializable()) && ((tail && isSnk && !iswrappedAround) || mypipe->isSerializable())) annotatedGroups[gName].insertInList(std::make_pair(mypipe, SetEnum::L)); else { error("The group cannot serialize something!\n"); @@ -334,17 +336,17 @@ class dGroups { if (!tail) annotatedGroups[gName].destinationEndpoints.push_back({ChannelType::FWD, annotatedGroups[annotated[originalPipe->get_nextstage(mypipe->get_laststage())]].listenEndpoint}); + else if (iswrappedAround) + // if this is the last stage & the pipe is wrapper around i connect tot he "head" groups of this pipeline + annotatedGroups[gName].destinationEndpoints.push_back({ChannelType::FBK, annotatedGroups[annotated[originalPipe->get_firststage()]].listenEndpoint}); - if (!head) + // add a new expected connection if i'm not the head or i'm the head and the pipeline is wrapped around + if (!head || iswrappedAround) annotatedGroups[gName].expectedEOS = 1; } - - - } else { - - // all2all here! + } else { // multiple groups created from an all2all! std::set> children = getChildBB(pair.first); @@ -433,8 +435,10 @@ class dGroups { // if the current group is horizontal count out itsleft from the all horizontals if (!runningGroup_IR.isVertical()) runningGroup_IR.expectedEOS -= 1; } + // if the previousStage exists, count all the ouput groups pointing to the one i'm going to run - if (previousStage && runningGroup_IR.hasLeftChildren()) + // if the runningGroup comes from a pipe, make sure i'm the head + if (previousStage && runningGroup_IR.hasLeftChildren() && (!runningGroup_IR.parentBB->isPipe() || inputGroups(parentBB2GroupsName[runningGroup_IR.parentBB]).contains(runningGroup))) runningGroup_IR.expectedEOS += outputGroups(parentBB2GroupsName[previousStage]).size(); // FEEDBACK RELATED (wrap around of the main pipe!) @@ -450,8 +454,14 @@ class dGroups { // inserisci tutte i gruppi di questo bb a destra for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) if (!annotatedGroups[gName].isVertical() || annotatedGroups[gName].hasRightChildren()) - runningGroup_IR.destinationEndpoints.push_back({ChannelType::INT, annotatedGroups[gName].listenEndpoint}); - } else { + runningGroup_IR.destinationEndpoints.push_back({ChannelType::FWD, annotatedGroups[gName].listenEndpoint}); + } + else if (runningGroup_IR.parentBB->isPipe() && runningGroup_IR.parentBB != parentPipe){ + if (nextStage && outputGroups(parentBB2GroupsName[runningGroup_IR.parentBB]).contains(runningGroup)) + for(const auto& gName : inputGroups(parentBB2GroupsName[nextStage])) + runningGroup_IR.destinationEndpoints.push_back({ChannelType::FWD, annotatedGroups[gName].listenEndpoint}); + } + else { if (!runningGroup_IR.isVertical()){ // inserisci tutti i gruppi come sopra for(const auto& gName: parentBB2GroupsName[runningGroup_IR.parentBB]) @@ -480,7 +490,7 @@ class dGroups { for(auto& [ct, ep] : runningGroup_IR.destinationEndpoints){ auto& destIR = annotatedGroups[ep.groupName]; destIR.buildIndexes(); - bool internalConnection = ct == ChannelType::INT; //runningGroup_IR.parentBB == destIR.parentBB; + bool internalConnection = ct == ChannelType::INT || (ct == ChannelType::FWD && runningGroup_IR.parentBB == destIR.parentBB); //runningGroup_IR.parentBB == destIR.parentBB; runningGroup_IR.routingTable[ep.groupName] = std::make_pair(destIR.getInputIndexes(internalConnection), ct); } @@ -488,13 +498,19 @@ class dGroups { std::set outputGroups(std::set groupNames){ if (groupNames.size() > 1) - std::erase_if(groupNames, [this](const auto& gName){return (annotatedGroups[gName].isVertical() && annotatedGroups[gName].hasLeftChildren());}); + std::erase_if(groupNames, [this](const auto& gName){ + ff_IR& ir = annotatedGroups[gName]; + return ((ir.parentBB->isAll2All() && ir.isVertical() && ir.hasLeftChildren()) || (ir.parentBB->isPipe() && annotated[reinterpret_cast(ir.parentBB)->get_laststage()] != gName)); + }); return groupNames; } std::set inputGroups(std::set groupNames){ if (groupNames.size() > 1) - std::erase_if(groupNames,[this](const auto& gName){return (annotatedGroups[gName].isVertical() && annotatedGroups[gName].hasRightChildren());}); + std::erase_if(groupNames,[this](const auto& gName){ + ff_IR& ir = annotatedGroups[gName]; + return ((ir.parentBB->isAll2All() && ir.isVertical() && annotatedGroups[gName].hasRightChildren()) || (ir.parentBB->isPipe() && annotated[reinterpret_cast(ir.parentBB)->get_firststage()] != gName)); + }); return groupNames; } diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index 132cd284..94388197 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -28,6 +28,18 @@ class ff_IR { void buildIndexes(){ if (!L.empty()){ + if (parentBB->isPipe() && !wholeParent){ + assert(L.size() == 1); + ff::svector inputs, outputs; + for (ff_node* n : L){ + n->get_in_nodes(inputs); + n->get_out_nodes(outputs); + } + for (int i = 0; i < inputs.size(); i++) inputL.push_back(i); + for (int i = 0; i < outputs.size(); i++) outputL.push_back(i); + return; + } + ff::svector parentInputs; parentBB->get_in_nodes(parentInputs); @@ -86,7 +98,7 @@ class ff_IR { // liste degli index dei nodi input/output nel builiding block in the shared memory context. The first list: inputL will become the rouitng table std::vector inputL, outputL, inputR, outputR; - + // pre computed routing table for the sender module (shoud coincide with the one exchanged actually at runtime) std::map, ChannelType>> routingTable; // TODO: implmentare l'assegnamento di questi campi @@ -107,7 +119,7 @@ class ff_IR { } std::vector getInputIndexes(bool internal){ - if (internal && !R.empty()) return inputR; + if ((isVertical() && hasRightChildren()) || (internal && !R.empty())) return inputR; return inputL; } diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 0a5f8dd7..456536c7 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -45,8 +45,9 @@ using namespace ff; class ff_dreceiver: public ff_monode_t { protected: + std::map sck2ChannelType; - static int sendRoutingTable(const int sck, const std::vector& dest){ + /*static int sendRoutingTable(const int sck, const std::vector& dest){ dataBuffer buff; std::ostream oss(&buff); cereal::PortableBinaryOutputArchive oarchive(oss); oarchive << dest; @@ -64,13 +65,16 @@ class ff_dreceiver: public ff_monode_t { } return 0; - } + }*/ virtual int handshakeHandler(const int sck){ // ricevo l'handshake e mi salvo che tipo di connessione è size_t size; - struct iovec iov; iov.iov_base = &size; iov.iov_len = sizeof(size); - switch (readvn(sck, &iov, 1)) { + ChannelType t; + struct iovec iov[2]; + iov[0].iov_base = &t; iov[0].iov_len = sizeof(ChannelType); + iov[1].iov_base = &size; iov[1].iov_len = sizeof(size); + switch (readvn(sck, iov, 2)) { case -1: error("Error reading from socket\n"); // fatal error case 0: return -1; // connection close } @@ -81,13 +85,15 @@ class ff_dreceiver: public ff_monode_t { if (readn(sck, groupName, size) < 0){ error("Error reading from socket groupName\n"); return -1; } - std::vector reachableDestinations; - for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); + + sck2ChannelType[sck] = t; - return this->sendRoutingTable(sck, reachableDestinations); + return 0; //this->sendRoutingTable(sck, reachableDestinations); } virtual void registerEOS(int sck){ + for(int i = 0; i < this->get_num_outchannels(); i++) + ff_send_out(new message_t(0,0), i); neos++; } @@ -113,15 +119,15 @@ class ff_dreceiver: public ff_monode_t { return -1; } } - + ChannelType t = sck2ChannelType[sck]; requestSize = ntohl(requestSize); for(int i = 0; i < requestSize; i++) - if (handleRequest(sck)<0) return -1; + if (handleRequest(sck, t)<0) return -1; return 0; } - virtual int handleRequest(int sck){ + virtual int handleRequest(int sck, ChannelType t){ int sender; int chid; size_t sz; @@ -153,6 +159,7 @@ class ff_dreceiver: public ff_monode_t { } message_t* out = new message_t(buff, sz, true); assert(out); + out->feedback = t == ChannelType::FBK; out->sender = sender; out->chid = chid; this->forward(out, sck); @@ -295,29 +302,35 @@ class ff_dreceiver: public ff_monode_t { class ff_dreceiverH : public ff_dreceiver { - std::vector internalDestinations; - std::map isInternalConnection; - std::set internalGroupsNames; + //std::map isInternalConnection; size_t internalNEos = 0, externalNEos = 0; long next_rr_destination = 0; void registerEOS(int sck){ neos++; - size_t internalConn = std::count_if(std::begin(isInternalConnection), - std::end (isInternalConnection), - [](std::pair const &p) {return p.second;}); + size_t internalConn = std::count_if(std::begin(sck2ChannelType), + std::end (sck2ChannelType), + [](std::pair const &p) {return p.second == ChannelType::INT;}); + + if (sck2ChannelType[sck] != ChannelType::INT){ + // logical EOS!! + /*for(int i = 0; i < this->get_num_outchannels()-1; i++) + ff_send_out(new message_t(0,0), i);*/ - if (!isInternalConnection[sck]){ - if (++externalNEos == (isInternalConnection.size()-internalConn)) + if (++externalNEos == (sck2ChannelType.size()-internalConn)) for(size_t i = 0; i < this->get_num_outchannels()-1; i++) ff_send_out_to(this->EOS, i); - } else - if (++internalNEos == internalConn) + } else{ + /// logical EOS! + //ff_send_out_to(new message_t(0,0), this->get_num_outchannels()-1); + + if (++internalNEos == internalConn) ff_send_out_to(this->EOS, this->get_num_outchannels()-1); + } } - virtual int handshakeHandler(const int sck){ + /*virtual int handshakeHandler(const int sck){ size_t size; struct iovec iov; iov.iov_base = &size; iov.iov_len = sizeof(size); switch (readvn(sck, &iov, 1)) { @@ -342,9 +355,10 @@ class ff_dreceiverH : public ff_dreceiver { for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); return this->sendRoutingTable(sck, reachableDestinations); } + */ void forward(message_t* task, int sck){ - if (isInternalConnection[sck]) ff_send_out_to(task, this->get_num_outchannels()-1); + if (sck2ChannelType[sck] == ChannelType::INT) ff_send_out_to(task, this->get_num_outchannels()-1); else if (task->chid != -1) ff_send_out_to(task, this->routingTable[task->chid]); else { ff_send_out_to(task, next_rr_destination); @@ -353,8 +367,8 @@ class ff_dreceiverH : public ff_dreceiver { } public: - ff_dreceiverH(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {{0,0}}, std::vector internalRoutingTable = {0}, std::set internalGroups = {}, int coreid=-1) - : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid), internalDestinations(internalRoutingTable), internalGroupsNames(internalGroups) { + ff_dreceiverH(ff_endpoint acceptAddr, size_t input_channels, std::map routingTable = {{0,0}}, int coreid=-1) + : ff_dreceiver(acceptAddr, input_channels, routingTable, coreid){ } diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 95f7586e..68de2368 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -17,7 +17,7 @@ using namespace ff; class ff_dreceiverMPI: public ff_monode_t { protected: - static int sendRoutingTable(const int rank, const std::vector& dest){ + /*static int sendRoutingTable(const int rank, const std::vector& dest){ dataBuffer buff; std::ostream oss(&buff); cereal::PortableBinaryOutputArchive oarchive(oss); oarchive << dest; @@ -27,23 +27,27 @@ class ff_dreceiverMPI: public ff_monode_t { } return 0; - } + }*/ virtual int handshakeHandler(){ int sz; + ChannelType ct; MPI_Status status; MPI_Probe(MPI_ANY_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, &status); MPI_Get_count(&status, MPI_BYTE, &sz); char* buff = new char [sz]; MPI_Recv(buff, sz, MPI_BYTE, status.MPI_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + MPI_Recv(&ct, sizeof(ct), MPI_BYTE, status.MPI_SOURCE, DFF_CHANNEL_TYPE_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - std::vector reachableDestinations; - for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); + rank2ChannelType[status.MPI_SOURCE] = ct; - return this->sendRoutingTable(status.MPI_SOURCE, reachableDestinations); + return 0; } virtual void registerEOS(int rank){ + // logical EOS sent to nodes + for(int i = 0; i < this->get_num_outchannels(); i++) + ff_send_out(new message_t(0,0), i); neos++; } @@ -83,7 +87,7 @@ class ff_dreceiverMPI: public ff_monode_t { if (MPI_Recv(headers, headersLen, MPI_LONG, status.MPI_SOURCE, DFF_HEADER_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) error("Error on Recv Receiver primo in alto\n"); - + bool feedback = ChannelType::FBK == rank2ChannelType[status.MPI_SOURCE]; assert(headers[0]*3+1 == headersLen); if (headers[0] == 1){ size_t sz = headers[3]; @@ -99,6 +103,7 @@ class ff_dreceiverMPI: public ff_monode_t { message_t* out = new message_t(buff, sz, true); out->sender = headers[1]; out->chid = headers[2]; + out->feedback = feedback; this->forward(out, status.MPI_SOURCE); } else { @@ -122,6 +127,7 @@ class ff_dreceiverMPI: public ff_monode_t { message_t* out = new message_t(outBuff, sz, true); out->sender = headers[3*i+1]; out->chid = headers[3*i+2]; + out->feedback = feedback; this->forward(out, status.MPI_SOURCE); } @@ -137,52 +143,35 @@ class ff_dreceiverMPI: public ff_monode_t { size_t neos = 0; size_t input_channels; std::map routingTable; + std::map rank2ChannelType; int coreid; }; class ff_dreceiverHMPI : public ff_dreceiverMPI { - std::vector internalDestinations; - std::set internalGroupNames; - std::set internalRanks; size_t internalNEos = 0, externalNEos = 0; int next_rr_destination = 0; virtual void registerEOS(int rank){ neos++; + + size_t internalConn = std::count_if(std::begin(rank2ChannelType), + std::end (rank2ChannelType), + [](std::pair const &p) {return p.second == ChannelType::INT;}); + - if (!internalRanks.contains(rank)){ - if (++externalNEos == (input_channels-internalRanks.size())) + if (rank2ChannelType[rank] != ChannelType::INT){ + if (++externalNEos == (rank2ChannelType.size()-internalConn)) for(size_t i = 0; i < this->get_num_outchannels()-1; i++) ff_send_out_to(this->EOS, i); } else - if (++internalNEos == internalRanks.size()) + if (++internalNEos == internalConn) ff_send_out_to(this->EOS, this->get_num_outchannels()-1); } - virtual int handshakeHandler(){ - int sz; - MPI_Status status; - MPI_Probe(MPI_ANY_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, &status); - MPI_Get_count(&status, MPI_BYTE, &sz); - char* buff = new char [sz]; - MPI_Recv(buff, sz, MPI_BYTE, status.MPI_SOURCE, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - // the connection is internal! - if (internalGroupNames.contains(std::string(buff, sz))) { - internalRanks.insert(status.MPI_SOURCE); - return this->sendRoutingTable(status.MPI_SOURCE, internalDestinations); - } - - std::vector reachableDestinations; - for(const auto& [key, value] : this->routingTable) reachableDestinations.push_back(key); - - return this->sendRoutingTable(status.MPI_SOURCE, reachableDestinations); - } - void forward(message_t* task, int rank){ - if (internalRanks.contains(rank)) ff_send_out_to(task, this->get_num_outchannels()-1); + if (rank2ChannelType[rank] == ChannelType::INT) ff_send_out_to(task, this->get_num_outchannels()-1); else if (task->chid != -1) ff_send_out_to(task, this->routingTable[task->chid]); else { ff_send_out_to(task, next_rr_destination); @@ -191,8 +180,8 @@ class ff_dreceiverHMPI : public ff_dreceiverMPI { } public: - ff_dreceiverHMPI(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, std::vector internalRoutingTable = {0}, std::set internalGroups = {}, int coreid=-1) - : ff_dreceiverMPI(input_channels, routingTable, coreid), internalDestinations(internalRoutingTable), internalGroupNames(internalGroups) {} + ff_dreceiverHMPI(size_t input_channels, std::map routingTable = {std::make_pair(0,0)}, int coreid=-1) + : ff_dreceiverMPI(input_channels, routingTable, coreid) {} }; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 33165cb4..91f30f8a 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -46,12 +46,13 @@ using namespace ff; - +using precomputedRT_t = std::map, ChannelType>>; class ff_dsender: public ff_minode_t { protected: size_t neos=0; std::vector> dest_endpoints; - std::map dest2Socket; + precomputedRT_t* precomputedRT; + std::map, int> dest2Socket; std::vector sockets; int last_rr_socket = -1; std::map socketsCounters; @@ -63,68 +64,23 @@ class ff_dsender: public ff_minode_t { fd_set set, tmpset; int fdmax = -1; - int receiveReachableDestinations(int sck, std::map& m){ - - size_t sz; - ssize_t r; - - if ((r=readn(sck, (char*)&sz, sizeof(sz)))!=sizeof(sz)) { - if (r==0) - error("Error unexpected connection closed by receiver\n"); - else - error("Error reading size (errno=%d)"); - return -1; - } - - sz = be64toh(sz); - - - char* buff = new char [sz]; - assert(buff); - - if(readn(sck, buff, sz) < 0){ - error("Error reading from socket in routing table!\n"); - delete [] buff; - return -1; - } - - dataBuffer dbuff(buff, sz, true); - std::istream iss(&dbuff); - cereal::PortableBinaryInputArchive iarchive(iss); - std::vector destinationsList; - - iarchive >> destinationsList; - - for (const int& d : destinationsList) m[d] = sck; - - /*for (const auto& p : m) - std::cout << p.first << " - " << p.second << std::endl; - */ - //ff::cout << "Receiving routing table (" << sz << " bytes)" << ff::endl; - return 0; - } - - int sendGroupName(const int sck){ + virtual int handshakeHandler(const int sck, ChannelType t){ size_t sz = htobe64(gName.size()); - struct iovec iov[2]; - iov[0].iov_base = &sz; - iov[0].iov_len = sizeof(sz); - iov[1].iov_base = (char*)(gName.c_str()); - iov[1].iov_len = gName.size(); - - if (writevn(sck, iov, 2) < 0){ + struct iovec iov[3]; + iov[0].iov_base = &t; + iov[0].iov_len = sizeof(ChannelType); + iov[1].iov_base = &sz; + iov[1].iov_len = sizeof(sz); + iov[2].iov_base = (char*)(gName.c_str()); + iov[2].iov_len = gName.size(); + + if (writevn(sck, iov, 3) < 0){ error("Error writing on socket\n"); return -1; } return 0; } - - virtual int handshakeHandler(const int sck, bool){ - if (sendGroupName(sck) < 0) return -1; - - return receiveReachableDestinations(sck, dest2Socket); - } int create_connect(const ff_endpoint& destination){ int socketFD; @@ -247,62 +203,30 @@ class ff_dsender: public ff_minode_t { return 1; } - int waitAckFromAny() { - tmpset = set; - if (select(fdmax + 1, &tmpset, NULL, NULL, NULL) == -1){ - perror("select"); - return -1; - } - // try to receive from all connections in a non blocking way - for (auto& [sck, counter] : socketsCounters){ - int r; ack_t a; - if ((r = recvnnb(sck, reinterpret_cast(&a), sizeof(ack_t))) != sizeof(ack_t)){ - if (errno == EWOULDBLOCK){ - continue; - } - perror("recvnnb ack any"); - return -1; - } else { - counter++; - return sck; - } - } - - return -1; - } - - int getNextReady(){ - for(size_t i = 0; i < this->sockets.size(); i++){ - int actualSocketIndex = (last_rr_socket + 1 + i) % this->sockets.size(); - int sck = sockets[actualSocketIndex]; - if (socketsCounters[sck] > 0) { - last_rr_socket = actualSocketIndex; - return sck; - } - } - return waitAckFromAny(); - } - - int getMostFilledBufferSck(){ + int getMostFilledBufferSck(bool feedback){ int sckMax = 0; int sizeMax = 0; for(auto& [sck, buffer] : batchBuffers){ + if ((feedback && buffer.ct != ChannelType::FBK) || (!feedback && buffer.ct != ChannelType::FWD)) continue; if (buffer.size > sizeMax) sckMax = sck; } + if (sckMax > 0) return sckMax; - + + do { last_rr_socket = (last_rr_socket + 1) % this->sockets.size(); + } while (batchBuffers[sockets[last_rr_socket]].ct != (feedback ? ChannelType::FBK : ChannelType::FWD)); return sockets[last_rr_socket]; } public: - ff_dsender(std::pair dest_endpoint, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1): gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { + ff_dsender(std::pair dest_endpoint, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1): precomputedRT(rt), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { this->dest_endpoints.push_back(std::move(dest_endpoint)); } - ff_dsender( std::vector> dest_endpoints_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} + ff_dsender( std::vector> dest_endpoints_, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) : dest_endpoints(std::move(dest_endpoints_)), precomputedRT(rt), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} @@ -313,13 +237,13 @@ class ff_dsender: public ff_minode_t { FD_ZERO(&set); FD_ZERO(&tmpset); - sockets.resize(dest_endpoints.size()); - for(size_t i=0; i < this->dest_endpoints.size(); i++){ - int sck = tryConnect(this->dest_endpoints[i].second); + //sockets.resize(dest_endpoints.size()); + for(auto& [ct, ep] : this->dest_endpoints){ + int sck = tryConnect(ep); if (sck <= 0) return -1; - sockets[i] = sck; + sockets.push_back(sck); socketsCounters[sck] = messageOTF; - batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { + batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, ct, [this, sck](struct iovec* v, int size) -> bool { if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ error("Errore waiting ack from socket inside the callback\n"); @@ -334,9 +258,13 @@ class ff_dsender: public ff_minode_t { this->socketsCounters[sck]--; return true; - })); // change with the correct size + })); - if (handshakeHandler(sck, false) < 0) { + // compute the routing table! + for(int dest : precomputedRT->operator[](ep.groupName).first) + dest2Socket[std::make_pair(dest, ct)] = sck; + + if (handshakeHandler(sck, ct) < 0) { error("svc_init ff_dsender failed"); return -1; } @@ -353,11 +281,13 @@ class ff_dsender: public ff_minode_t { message_t *svc(message_t* task) { int sck; + //if (task->chid == -1) task->chid = 0; if (task->chid != -1) - sck = dest2Socket[task->chid]; - else - sck = getMostFilledBufferSck(); // get the most filled buffer socket or a rr socket - + sck = dest2Socket[{task->chid, (task->feedback ? ChannelType::FBK : ChannelType::FWD)}]; + else { + sck = getMostFilledBufferSck(task->feedback); // get the most filled buffer socket or a rr socket + } + if (batchBuffers[sck].push(task) == -1) { return EOS; } @@ -412,14 +342,12 @@ class ff_dsender: public ff_minode_t { class ff_dsenderH : public ff_dsender { - std::map internalDest2Socket; std::vector internalSockets; int last_rr_socket_Internal = -1; - std::set internalGroupNames; int internalMessageOTF; bool squareBoxEOS = false; - int getNextReadyInternal(){ + /*int getNextReadyInternal(){ for(size_t i = 0; i < this->internalSockets.size(); i++){ int actualSocketIndex = (last_rr_socket_Internal + 1 + i) % this->internalSockets.size(); int sck = internalSockets[actualSocketIndex]; @@ -442,7 +370,7 @@ class ff_dsenderH : public ff_dsender { last_rr_socket_Internal = it - internalSockets.begin(); return sck; - } + }*/ int getMostFilledInternalBufferSck(){ int sckMax = 0; @@ -462,14 +390,8 @@ class ff_dsenderH : public ff_dsender { public: - ff_dsenderH(std::pair e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} - ff_dsenderH(std::vector> dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} - - int handshakeHandler(const int sck, bool isInternal){ - if (sendGroupName(sck) < 0) return -1; - - return receiveReachableDestinations(sck, isInternal ? internalDest2Socket : dest2Socket); - } + ff_dsenderH(std::pair e, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(e, rt, gName, batchSize, messageOTF, coreid), internalMessageOTF(internalMessageOTF) {} + ff_dsenderH(std::vector> dest_endpoints_, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsender(dest_endpoints_, rt, gName, batchSize, messageOTF, coreid), internalMessageOTF(internalMessageOTF) {} int svc_init() { @@ -482,11 +404,11 @@ class ff_dsenderH : public ff_dsender { for(const auto& [ct, endpoint] : this->dest_endpoints){ int sck = tryConnect(endpoint); if (sck <= 0) return -1; - bool isInternal = internalGroupNames.contains(endpoint.groupName); + bool isInternal = ct == ChannelType::INT; if (isInternal) internalSockets.push_back(sck); else sockets.push_back(sck); - socketsCounters[sck] = isInternal ? internalMessageOTF: messageOTF; - batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, [this, sck](struct iovec* v, int size) -> bool { + socketsCounters[sck] = isInternal ? internalMessageOTF : messageOTF; + batchBuffers.emplace(std::piecewise_construct, std::forward_as_tuple(sck), std::forward_as_tuple(this->batchSize, ct, [this, sck](struct iovec* v, int size) -> bool { if (this->socketsCounters[sck] == 0 && this->waitAckFrom(sck) == -1){ error("Errore waiting ack from socket inside the callback\n"); @@ -502,7 +424,11 @@ class ff_dsenderH : public ff_dsender { return true; })); // change with the correct size - if (handshakeHandler(sck, isInternal) < 0) return -1; + + for(int dest : precomputedRT->operator[](endpoint.groupName).first) + dest2Socket[std::make_pair(dest, ct)] = sck; + + if (handshakeHandler(sck, ct) < 0) return -1; FD_SET(sck, &set); if (sck > fdmax) fdmax = sck; @@ -517,10 +443,9 @@ class ff_dsenderH : public ff_dsender { message_t *svc(message_t* task) { if (this->get_channel_id() == (ssize_t)(this->get_num_inchannels() - 1)){ int sck; - // pick destination from the list of internal connections! if (task->chid != -1){ // roundrobin over the destinations - sck = internalDest2Socket[task->chid]; + sck = dest2Socket[{task->chid, ChannelType::INT}]; } else sck = getMostFilledInternalBufferSck(); @@ -531,7 +456,7 @@ class ff_dsenderH : public ff_dsender { return this->GO_ON; } - + return ff_dsender::svc(task); } diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 424a44e7..a0cf36af 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -17,7 +17,7 @@ #include using namespace ff; - +using precomputedRT_t = std::map, ChannelType>>; class ff_dsenderMPI: public ff_minode_t { protected: class batchBuffer { @@ -106,101 +106,28 @@ class ff_dsenderMPI: public ff_minode_t { } }; size_t neos=0; + precomputedRT_t* rt; int last_rr_rank = 0; //next destiation to send for round robin policy - std::map dest2Rank; - std::map rankCounters; + std::map, int> dest2Rank; std::map>> buffers; - std::vector ranks; + std::vector> ranks; std::vector> destRanks; std::string gName; int batchSize; int messageOTF; int coreid; - static int receiveReachableDestinations(int rank, std::map& m){ - int sz; - //int cmd = DFF_REQUEST_ROUTING_TABLE; - - MPI_Status status; - //MPI_Send(&cmd, 1, MPI_INT, rank, DFF_ROUTING_TABLE_REQUEST_TAG, MPI_COMM_WORLD); - MPI_Probe(rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD, &status); - MPI_Get_count(&status,MPI_BYTE, &sz); - char* buff = new char [sz]; - std::cout << "Received routing table (" << sz << " bytes)" << std::endl; - MPI_Recv(buff, sz, MPI_BYTE, rank, DFF_ROUTING_TABLE_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - - dataBuffer dbuff(buff, sz, true); - std::istream iss(&dbuff); - cereal::PortableBinaryInputArchive iarchive(iss); - std::vector destinationsList; - - iarchive >> destinationsList; - - for (const int& d : destinationsList) m[d] = rank; - - return 0; - } - - int sendGroupName(const int rank){ + virtual int handshakeHandler(const int rank, ChannelType ct){ MPI_Send(gName.c_str(), gName.size(), MPI_BYTE, rank, DFF_GROUP_NAME_TAG, MPI_COMM_WORLD); + MPI_Send(&ct, sizeof(ChannelType), MPI_BYTE, rank, DFF_CHANNEL_TYPE_TAG, MPI_COMM_WORLD); return 0; } - virtual int handshakeHandler(const int rank, bool){ - sendGroupName(rank); - return receiveReachableDestinations(rank, dest2Rank); - } - - int waitAckFrom(int rank){ - ack_t tmpAck; - MPI_Status status; - while(true){ - if (MPI_Recv(&tmpAck, sizeof(ack_t), MPI_BYTE, MPI_ANY_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) - return -1; - rankCounters[status.MPI_SOURCE]++; - if (rank == status.MPI_SOURCE) return 0; - } - } - - int waitAckFromAny(){ - ack_t tmpAck; - MPI_Status status; - if (MPI_Recv(&tmpAck, sizeof(ack_t), MPI_BYTE, MPI_ANY_SOURCE, DFF_ACK_TAG, MPI_COMM_WORLD, &status) != MPI_SUCCESS) - return -1; - rankCounters[status.MPI_SOURCE]++; - return status.MPI_SOURCE; - } - - int getNextReady(){ - for(size_t i = 0; i < this->ranks.size(); i++){ - int rankIndex = (last_rr_rank + 1 + i) % this->ranks.size(); - int rank = ranks[rankIndex]; - if (rankCounters[rank] > 0) { - last_rr_rank = rankIndex; - return rank; - } - } - return waitAckFromAny(); - } - - int sendToRank(const int rank, const message_t* task){ - size_t sz = task->data.getLen(); - - long header[3] = {(long)sz, task->sender, task->chid}; - - MPI_Send(header, 3, MPI_LONG, rank, DFF_HEADER_TAG, MPI_COMM_WORLD); - - MPI_Send(task->data.getPtr(), sz, MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD); - - return 0; - - } - - int getMostFilledBufferRank(){ + int getMostFilledBufferRank(bool feedback){ int rankMax = -1; size_t sizeMax = 0; - for(int rank : ranks){ + for(auto& [rank,ct] : ranks){ + if ((feedback && ct != ChannelType::FBK) || (!feedback && ct != ChannelType::FWD)) continue; auto& batchBB = buffers[rank]; size_t sz = batchBB.second[batchBB.first]->size(); if (sz > sizeMax) { @@ -210,30 +137,34 @@ class ff_dsenderMPI: public ff_minode_t { } if (rankMax >= 0) return rankMax; - last_rr_rank = (last_rr_rank + 1) % this->ranks.size(); - return this->ranks[last_rr_rank]; + do { + last_rr_rank = (last_rr_rank + 1) % this->ranks.size(); + } while (this->ranks[last_rr_rank].second != (feedback ? ChannelType::FBK : ChannelType::FWD)); + return this->ranks[last_rr_rank].first; } public: - ff_dsenderMPI(std::pair destRank, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) - : gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { + ff_dsenderMPI(std::pair destRank, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) + : rt(rt), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) { this->destRanks.push_back(std::move(destRank)); } - ff_dsenderMPI( std::vector> destRanks_, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) - : destRanks(std::move(destRanks_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} + ff_dsenderMPI( std::vector> destRanks_, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int coreid=-1) + : rt(rt), destRanks(std::move(destRanks_)), gName(gName), batchSize(batchSize), messageOTF(messageOTF), coreid(coreid) {} int svc_init() { if (coreid!=-1) ff_mapThreadToCpu(coreid); for(auto& [ct, ep]: this->destRanks){ - handshakeHandler(ep.getRank(), false); - //rankCounters[ep.getRank()] = messageOTF; - ranks.push_back(ep.getRank()); + handshakeHandler(ep.getRank(), ct); + ranks.push_back({ep.getRank(), ct}); std::vector appo; for(int i = 0; i < messageOTF; i++) appo.push_back(batchSize == 1 ? new directBatchBuffer(ep.getRank()) : new batchBuffer(batchSize, ep.getRank())); buffers.emplace(std::make_pair(ep.getRank(), std::make_pair(0, std::move(appo)))); + + for(int dest : rt->operator[](ep.groupName).first) + dest2Rank[std::make_pair(dest, ct)] = ep.getRank(); } this->destRanks.clear(); @@ -249,9 +180,9 @@ class ff_dsenderMPI: public ff_minode_t { message_t *svc(message_t* task) { int rank; if (task->chid != -1) - rank = dest2Rank[task->chid]; + rank = dest2Rank[{task->chid, task->feedback ? ChannelType::FBK : ChannelType::FWD}]; else - rank = getMostFilledBufferRank(); + rank = getMostFilledBufferRank(task->feedback); auto& buffs = buffers[rank]; assert(buffs.second.size() > 0); @@ -263,7 +194,7 @@ class ff_dsenderMPI: public ff_minode_t { void eosnotify(ssize_t) { if (++neos >= this->get_num_inchannels()) - for(auto& rank : ranks){ + for(auto& [rank, ct] : ranks){ auto& buffs = buffers[rank]; buffs.second[buffs.first]->pushEOS(); } @@ -272,35 +203,11 @@ class ff_dsenderMPI: public ff_minode_t { class ff_dsenderHMPI : public ff_dsenderMPI { - - std::map internalDest2Rank; std::vector internalRanks; int last_rr_rank_Internal = -1; - std::set internalGroupNames; int internalMessageOTF; bool squareBoxEOS = false; - int getNextReadyInternal(){ - for(size_t i = 0; i < this->internalRanks.size(); i++){ - int actualRankIndex = (last_rr_rank_Internal + 1 + i) % this->internalRanks.size(); - int sck = internalRanks[actualRankIndex]; - if (rankCounters[sck] > 0) { - last_rr_rank_Internal = actualRankIndex; - return sck; - } - } - - int rank; - decltype(internalRanks)::iterator it; - - do - rank = waitAckFromAny(); // FIX: error management! - while ((it = std::find(internalRanks.begin(), internalRanks.end(), rank)) != internalRanks.end()); - - last_rr_rank_Internal = it - internalRanks.begin(); - return rank; - } - int getMostFilledInternalBufferRank(){ int rankMax = -1; size_t sizeMax = 0; @@ -320,14 +227,8 @@ class ff_dsenderHMPI : public ff_dsenderMPI { public: - ff_dsenderHMPI(std::pair e, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(e, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} - ff_dsenderHMPI(std::vector> dest_endpoints_, std::string gName = "", std::set internalGroups = {}, int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(dest_endpoints_, gName, batchSize, messageOTF, coreid), internalGroupNames(internalGroups), internalMessageOTF(internalMessageOTF) {} - - int handshakeHandler(const int rank, bool isInternal){ - sendGroupName(rank); - - return receiveReachableDestinations(rank, isInternal ? internalDest2Rank : dest2Rank); - } + ff_dsenderHMPI(std::pair e, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(e, rt, gName, batchSize, messageOTF, coreid), internalMessageOTF(internalMessageOTF) {} + ff_dsenderHMPI(std::vector> dest_endpoints_, precomputedRT_t* rt, std::string gName = "", int batchSize = DEFAULT_BATCH_SIZE, int messageOTF = DEFAULT_MESSAGE_OTF, int internalMessageOTF = DEFAULT_INTERNALMSG_OTF, int coreid=-1) : ff_dsenderMPI(dest_endpoints_, rt, gName, batchSize, messageOTF, coreid), internalMessageOTF(internalMessageOTF) {} int svc_init() { @@ -336,17 +237,20 @@ class ff_dsenderHMPI : public ff_dsenderMPI { for(auto& [ct, endpoint] : this->destRanks){ int rank = endpoint.getRank(); - bool isInternal = internalGroupNames.contains(endpoint.groupName); + bool isInternal = ct == ChannelType::INT; if (isInternal) internalRanks.push_back(rank); else - ranks.push_back(rank); + ranks.push_back({rank, ct}); std::vector appo; for(int i = 0; i < (isInternal ? internalMessageOTF : messageOTF); i++) appo.push_back(batchSize == 1 ? new directBatchBuffer(rank) : new batchBuffer(batchSize, rank)); buffers.emplace(std::make_pair(rank, std::make_pair(0, std::move(appo)))); - if (handshakeHandler(rank, isInternal) < 0) return -1; + if (handshakeHandler(rank, ct) < 0) return -1; + + for(int dest : rt->operator[](endpoint.groupName).first) + dest2Rank[std::make_pair(dest, ct)] = rank; } @@ -361,7 +265,7 @@ class ff_dsenderHMPI : public ff_dsenderMPI { // pick destination from the list of internal connections! if (task->chid != -1){ // roundrobin over the destinations - rank = internalDest2Rank[task->chid]; + rank = dest2Rank[{task->chid, ChannelType::INT}]; } else rank = getMostFilledInternalBufferRank(); @@ -385,20 +289,8 @@ class ff_dsenderHMPI : public ff_dsenderMPI { buffs.second[buffs.first]->pushEOS(); } } - if (++neos >= this->get_num_inchannels()) { - // all input EOS received, now sending the EOS to all - // others connections - for(const auto& rank : ranks){ - auto& buffs = buffers[rank]; - buffs.second[buffs.first]->pushEOS(); - } - } + ff_dsenderMPI::eosnotify(id); } - - void svc_end(){ - for(auto& [rank, bb] : buffers) - for(auto& b : bb.second) b->waitCompletion(); - } }; diff --git a/ff/distributed/ff_network.hpp b/ff/distributed/ff_network.hpp index a761b9cd..c86348a7 100644 --- a/ff/distributed/ff_network.hpp +++ b/ff/distributed/ff_network.hpp @@ -120,6 +120,7 @@ struct message_t { int sender; int chid; + bool feedback = false; dataBuffer data; }; @@ -225,6 +226,7 @@ static inline ssize_t recvnnb(int fd, char *buf, size_t size) { #define DFF_HEADER_TAG 4 #define DFF_ACK_TAG 5 #define DFF_GROUP_NAME_TAG 6 + #define DFF_CHANNEL_TYPE_TAG 7 #define DFF_REQUEST_ROUTING_TABLE 10 #endif diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index f0e071b0..a1ffe1a7 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -73,6 +73,7 @@ class WrapperIN: public internal_mi_transformer { // with feedback channels in might be null if (in == nullptr) return n->svc(nullptr); message_t* msg = (message_t*)in; + if (this->n->isMultiInput()) { int channelid = msg->sender; @@ -80,6 +81,12 @@ class WrapperIN: public internal_mi_transformer { mi->set_input_channelid(channelid, fromInput()); if (!this->fromInput()) return n->svc(in); } + + // received a logical EOS ------> must be inserted also in the wrapperIN!!!!! + if (msg->data.getLen() == 0){ + this->n->eosnotify(msg->sender); //msg->sender here is not consistent... always 0 + return GO_ON; + } bool datacopied=true; void* inputData = this->n->deserializeF(msg->data, datacopied); @@ -103,35 +110,39 @@ class WrapperOUT: public internal_mo_transformer { int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; int myID; - int feedbackChannels; + int feedbackChannels, localFeedbacks, remoteFeedbacks; public: - WrapperOUT(ff_node* n, int id, int outchannels=-1, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id){ + WrapperOUT(ff_node* n, int id, int outchannels=-1, int remoteFeedbacks = 0, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id), remoteFeedbacks(remoteFeedbacks){ this->n = n; this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); } bool serialize(void* in, int id) { - if (feedbackChannels){ - if (id < feedbackChannels) { + if (localFeedbacks){ + if (id < localFeedbacks) { if (id == -1) return ff_send_out(in); return ff_send_out_to(in, id); } // from 0 to feedbackChannels-1 are feedback channels // from feedbackChannels to outchannels-1 are forward channels - id -= feedbackChannels; + id -= localFeedbacks; } - message_t* msg = new message_t; + + message_t* msg = new message_t; + msg->feedback = (id < remoteFeedbacks && remoteFeedbacks); + if (!msg->feedback) id -= remoteFeedbacks; + bool datacopied = this->n->serializeF(in, msg->data); msg->sender = myID; msg->chid = id; if (!datacopied) msg->data.freetaskF = this->n->freetaskF; - if (feedbackChannels) { - // all forward channels are multiplexed in feedbackChannels - ff_send_out_to(msg, feedbackChannels); + if (localFeedbacks) { + // all forward channels are multiplexed in feedbackChannels which coicide with the sender node! + ff_send_out_to(msg, localFeedbacks); } else ff_send_out(msg); if (datacopied) this->n->freetaskF(in); @@ -142,12 +153,13 @@ class WrapperOUT: public internal_mo_transformer { // save the channel id fo the sender, useful for when there are feedbacks in the application // these are local feedback channels - feedbackChannels = internal_mo_transformer::get_num_feedbackchannels(); + localFeedbacks = internal_mo_transformer::get_num_feedbackchannels(); + feedbackChannels = localFeedbacks + remoteFeedbacks; if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); mo->set_virtual_outchannels(outchannels); - mo->set_virtual_feedbackchannels(-1); + mo->set_virtual_feedbackchannels(feedbackChannels); // maybe... must be checked! } return n->svc_init(); @@ -169,7 +181,7 @@ class WrapperOUT: public internal_mo_transformer { /** returns the total number of output channels */ size_t get_num_outchannels() const { return outchannels; } - size_t get_num_feedbackchannels() const { return 0; } // TODO <--------- + size_t get_num_feedbackchannels() const { return feedbackChannels; } ff::ff_node* getOriginal(){return this->n;} @@ -190,9 +202,10 @@ class WrapperINOUT: public internal_mi_transformer { int inchannels; // number of input channels the wrapped node is supposed to have int defaultDestination; int myID; + int remoteFeedbacks; public: - WrapperINOUT(ff_node* n, int id, int inchannels=1, bool cleanup=false, int defaultDestination = -1): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), myID(id){ + WrapperINOUT(ff_node* n, int id, int inchannels=1, int remoteFeedbacks = 0, bool cleanup=false, int defaultDestination = -1): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), myID(id), remoteFeedbacks(remoteFeedbacks){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); @@ -207,10 +220,14 @@ class WrapperINOUT: public internal_mi_transformer { message_t* msg = new message_t; + + msg->feedback = (id < remoteFeedbacks && remoteFeedbacks); + bool datacopied= this->n->serializeF(in, msg->data); msg->sender = myID; // FIX! msg->chid = id; + if (!msg->feedback) msg->chid -= remoteFeedbacks; if (!datacopied) msg->data.freetaskF = this->n->freetaskF; ff_node::ff_send_out(msg); if (datacopied) this->n->freetaskF(in); @@ -218,10 +235,10 @@ class WrapperINOUT: public internal_mi_transformer { } int svc_init() { - if (this->n->isMultiInput()) { // ??? what?? + /*if (this->n->isMultiInput()) { // ??? what?? ff_minode* mi = reinterpret_cast(this->n); // what????? mi->set_running(inchannels); - } + }*/ return n->svc_init(); } @@ -232,10 +249,16 @@ class WrapperINOUT: public internal_mi_transformer { if (in != nullptr) { message_t* msg = (message_t*)in; + // received a logical EOS ------> must be inserted also in the wrapperIN!!!!! + if (msg->data.getLen() == 0){ + this->n->eosnotify(msg->sender); //msg->sender here is not consistent... always 0 + return GO_ON; + } + if (this->n->isMultiInput()) { int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); - mi->set_input_channelid(channelid, true); + mi->set_input_channelid(channelid, !msg->feedback); } bool datacopied=true; out = n->svc(this->n->deserializeF(msg->data, datacopied)); diff --git a/tests/distributed/test_a2a_h1.cpp b/tests/distributed/test_a2a_h1.cpp deleted file mode 100644 index fb7fcd65..00000000 --- a/tests/distributed/test_a2a_h1.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Source1 ->| - * | | ->Sink1 - * Source2 ->| -> | - * | | ->Sink2 - * Source3 ->| - * - * G1: Source1, Source2, Sink1 - * G2: Source3, Sink2 - * - */ - - - -#include -#include -#include -#include -#include - -using namespace ff; -std::mutex mtx; - -struct Source : ff_monode_t{ - int numWorker, generatorID; - Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} - - std::string* svc(std::string* in){ - - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); - - return EOS; - } -}; - - -struct Sink : ff_minode_t{ - int sinkID; - Sink(int id): sinkID(id) {} - std::string* svc(std::string* in){ - const std::lock_guard lock(mtx); - std::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << std::endl; - delete in; - return this->GO_ON; - } -}; - -int main(int argc, char*argv[]){ - - if (argc != 2){ - std::cerr << "Execute with the index of process!" << std::endl; - return 1; - } - - ff_endpoint g1("127.0.0.1", 8001); - g1.groupName = "G1"; - - ff_endpoint g2("127.0.0.1", 8002); - g2.groupName = "G2"; - - - ff_farm gFarm; - ff_a2a a2a; - - - // the following are just for building this example! - a2a.createGroup("G1"); - a2a.createGroup("G2"); - - if (atoi(argv[1]) == 0){ - dGroups::Instance()->setRunningGroup("G1"); - gFarm.add_emitter(new ff_dreceiverH(g1, 1, {{0, 0}}, {0})); - gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g2})); - - auto ea1 = new EmitterAdapter(new Source(2,0), 2, 0, {{0,0}}, true); ea1->skipallpop(true); - auto ea2 = new EmitterAdapter(new Source(2,1), 2, 1, {{0,0}}, true); ea2->skipallpop(true); - - a2a.add_firstset({ea1, ea2, new SquareBoxLeft({std::make_pair(0,0)})}); - a2a.add_secondset({new CollectorAdapter(new Sink(0), {0, 1}, true), new SquareBoxRight()}); - - } else { - dGroups::Instance()->setRunningGroup("G2"); - gFarm.add_emitter(new ff_dreceiverH(g2, 1, {{0, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g1})); - - auto ea = new EmitterAdapter(new Source(2,2), 2, 2, {{1,0}}, true); ea->skipallpop(true); - - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}); - a2a.add_secondset({new CollectorAdapter(new Sink(1), {2}, true), new SquareBoxRight()}); - - } - gFarm.add_workers({&a2a}); - gFarm.run_and_wait_end(); -} diff --git a/tests/distributed/test_a2a_h2.cpp b/tests/distributed/test_a2a_h2.cpp deleted file mode 100644 index 1c48c23d..00000000 --- a/tests/distributed/test_a2a_h2.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * |-> Forwarder1 ->| - * | | -> |-> Sink1 - * Source ->|-> Forwarder2 ->| | - * | | -> |-> Sink2 - * |-> Forwarder3 ->| - * - * - * G0: Source - * G1: Forwarer1, Forwarder2, Sink1 - * G2: Forwarder3, Sink2 - * - */ - -#include -#include -#include -#include -#include - -using namespace ff; -std::mutex mtx; - -struct RealSource : ff_monode_t{ - std::string* svc(std::string*){ - for(int i = 0; i < 2; i++) ff_send_out_to(new std::string("Trigger string!"), i); - return EOS; - } -}; - -struct Source : ff_monode_t{ - int numWorker, generatorID; - Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} - - std::string* svc(std::string* in){ - delete in; - std::cout << "Source starting generating tasks!" << std::endl; - for(int i = 0; i < 10; i++) - ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i%numWorker)), i%numWorker); - - std::cout << "Source generated all task sending now EOS!" << std::endl; - return EOS; - } -}; - - -struct Sink : ff_minode_t{ - int sinkID; - Sink(int id): sinkID(id) {} - std::string* svc(std::string* in){ - const std::lock_guard lock(mtx); - std::cout << *in << " received by Sink " << sinkID << " from " << get_channel_id() << std::endl; - delete in; - return this->GO_ON; - } -}; - - -struct ForwarderNode : ff_node{ - ForwarderNode(std::function f){ - this->serializeF = f; - } - ForwarderNode(std::function f){ - this->deserializeF = f; - } - void* svc(void* input){return input;} - }; - - -int main(int argc, char*argv[]){ - - if (argc != 2){ - std::cerr << "Execute with the index of process!" << std::endl; - return 1; - } - - ff_farm gFarm; - ff_a2a a2a; - - ff_endpoint g1("127.0.0.1", 8001); - g1.groupName = "G1"; - - ff_endpoint g2("127.0.0.1", 8002); - g2.groupName = "G2"; - - - a2a.createGroup("G1"); - a2a.createGroup("G2"); - ff_pipeline dummypipe; dummypipe.createGroup("G0"); - - if (atoi(argv[1]) == 0){ - dGroups::Instance()->setRunningGroup("G0"); - gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g1}, {ChannelType::FWD, g2}})); - gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; - } else if (atoi(argv[1]) == 1){ - dGroups::Instance()->setRunningGroup("G1"); - gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0})); - gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g2})); - - auto s = new Source(2,0); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); - - - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(0,0)})}); - a2a.add_secondset({new CollectorAdapter(new Sink(0), {0}, true), new SquareBoxRight}); - - } else { - dGroups::Instance()->setRunningGroup("G2"); - gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH({ChannelType::INT, g1})); - - auto s = new Source(2,1); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); - - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}); - a2a.add_secondset({new CollectorAdapter(new Sink(1), {1}, true), new SquareBoxRight}); - - } - gFarm.add_workers({&a2a}); - gFarm.run_and_wait_end(); -} diff --git a/tests/distributed/test_a2a_h3.cpp b/tests/distributed/test_a2a_h3.cpp deleted file mode 100644 index 63c0f6bd..00000000 --- a/tests/distributed/test_a2a_h3.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * |-> Forwarder1 ->| - * | | -> |-> Sink1 ->| - * Source ->|-> Forwarder2 ->| | | -> StringPrinter - * | | -> |-> Sink2 ->| - * |-> Forwarder3 ->| - * - * - * G0: Source - * G1: Forwarer1, Forwarder2, Sink1 - * G2: Forwarder3, Sink2 - * G3: StringPrinter - * - */ - -#include -#include -#include -#include -#include - -using namespace ff; -std::mutex mtx; - -struct RealSource : ff_monode_t{ - std::string* svc(std::string*){ - for(int i = 0; i < 2; i++) ff_send_out_to(new std::string("Trigger string!"), i); - return EOS; - } -}; - -struct Source : ff_monode_t{ - int numWorker, generatorID; - Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} - - std::string* svc(std::string* in){ - delete in; - std::cout << "Source starting generating tasks!" << std::endl; - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); - - std::cout << "Source generated all task sending now EOS!" << std::endl; - return EOS; - } -}; - - -struct Sink : ff_minode_t{ - int sinkID; - Sink(int id): sinkID(id) {} - std::string* svc(std::string* in){ - std::string* output = new std::string(*in + " received by Sink " + std::to_string(sinkID) + " from " + std::to_string(get_channel_id())); - delete in; - return output; - } -}; - -struct StringPrinter : ff_node_t{ - std::string* svc(std::string* in){ - const std::lock_guard lock(mtx); - std::cout << "Received something! Addr:" << in << "\n"; -#if 1 - try { - std::cout << *in << std::endl; - delete in; - } catch (const std::exception& ex){ - std::cerr << ex.what(); - } -#endif - return this->GO_ON; - } -}; - - -struct ForwarderNode : ff_node{ - ForwarderNode(std::function f){ - this->serializeF = f; - } - ForwarderNode(std::function f){ - this->deserializeF = f; - } - void* svc(void* input){return input;} -}; - - -int main(int argc, char*argv[]){ - - if (argc != 2){ - std::cerr << "Execute with the index of process!" << std::endl; - return 1; - } - - ff_endpoint g1("127.0.0.1", 8001); - g1.groupName = "G1"; - - ff_endpoint g2("127.0.0.1", 8002); - g2.groupName = "G2"; - - ff_endpoint g3("127.0.0.1", 8003); - g3.groupName = "G3"; - - ff_farm gFarm; - ff_a2a a2a; - - if (atoi(argv[1]) == 0){ - gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g1}, {ChannelType::FWD, g2}}, "G0")); - gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; - } else if (atoi(argv[1]) == 1){ - gFarm.add_emitter(new ff_dreceiverH(g1, 2, {{0, 0}}, {0}, {"G2"})); - gFarm.add_collector(new ff_dsenderH({{ChannelType::INT, g2},{ChannelType::FWD, g3}}, "G1", {"G2"})); - - auto s = new Source(2,0); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 0, {{0,0}}, true), true, true); - - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(0,0)})}); - auto sink = new Sink(0); - a2a.add_secondset({new ff_comb(new CollectorAdapter(sink, {0}, true), new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true)), new SquareBoxRight}); - - } else if (atoi(argv[1]) == 2) { - gFarm.add_emitter(new ff_dreceiverH(g2, 2, {{1, 0}}, {1}, {"G1"})); - gFarm.add_collector(new ff_dsenderH({{ChannelType::INT, g1}, {ChannelType::FWD, g3}}, "G2", {"G1"})); - gFarm.cleanup_emitter(); - gFarm.cleanup_collector(); - - auto s = new Source(2,1); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); - - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}, 0, true); - - auto sink = new Sink(1); - a2a.add_secondset({ - new ff_comb(new CollectorAdapter(sink, {1}, true), - new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true), true, true), - new SquareBoxRight - }, true); - - - - } else { - gFarm.add_emitter(new ff_dreceiver(g3, 2)); - gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; - } - gFarm.add_workers({&a2a}); - gFarm.run_and_wait_end(); -} diff --git a/tests/distributed/test_a2a_h4.cpp b/tests/distributed/test_a2a_h4.cpp deleted file mode 100644 index 767b29c7..00000000 --- a/tests/distributed/test_a2a_h4.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * |-> Forwarder1 ->| - * | | -> |-> Sink1 ->| - * Source ->|-> Forwarder2 ->| | | -> StringPrinter - * | | -> |-> Sink2 ->| - * |-> Forwarder3 ->| - * - * - * G0: Source - * G1: Forwarer1 - * G2: Sink1 - * G3: Forwarder3, Sink2 - * G4: StringPrinter - * - */ - -#include -#include -#include -#include -#include - -using namespace ff; -std::mutex mtx; - -struct RealSource : ff_monode_t{ - std::string* svc(std::string*){ - for(int i = 0; i < 2; i++) ff_send_out_to(new std::string("Trigger string!"), i); - return EOS; - } -}; - -struct Source : ff_monode_t{ - int numWorker, generatorID; - Source(int numWorker, int generatorID) : numWorker(numWorker), generatorID(generatorID) {} - - std::string* svc(std::string* in){ - delete in; - std::cout << "Source starting generating tasks!" << std::endl; - for(int i = 0; i < numWorker; i++) - ff_send_out_to(new std::string("Task" + std::to_string(i) + " generated from " + std::to_string(generatorID) + " for " + std::to_string(i)), i); - - std::cout << "Source generated all task sending now EOS!" << std::endl; - return EOS; - } -}; - - -struct Sink : ff_minode_t{ - int sinkID; - Sink(int id): sinkID(id) {} - std::string* svc(std::string* in){ - std::string* output = new std::string(*in + " received by Sink " + std::to_string(sinkID) + " from " + std::to_string(get_channel_id())); - delete in; - return output; - } -}; - -struct StringPrinter : ff_node_t{ - std::string* svc(std::string* in){ - const std::lock_guard lock(mtx); - std::cout << "Received something! Addr:" << in << "\n"; -#if 1 - try { - std::cout << *in << std::endl; - delete in; - } catch (const std::exception& ex){ - std::cerr << ex.what(); - } -#endif - return this->GO_ON; - } -}; - - -struct ForwarderNode : ff_node{ - ForwarderNode(std::function f){ - this->serializeF = f; - } - ForwarderNode(std::function f){ - this->deserializeF = f; - } - void* svc(void* input){return input;} -}; - - -int main(int argc, char*argv[]){ - - if (argc != 2){ - std::cerr << "Execute with the index of process!" << std::endl; - return 1; - } - - ff_endpoint g1("127.0.0.1", 8001); - g1.groupName = "G1"; - - ff_endpoint g2("127.0.0.1", 8002); - g2.groupName = "G2"; - - ff_endpoint g3("127.0.0.1", 8003); - g3.groupName = "G3"; - - ff_endpoint g4("127.0.0.1", 8004); - g4.groupName = "G4"; - - ff_farm gFarm; - ff_a2a a2a; - - a2a.createGroup("G1"); - a2a.createGroup("G2"); - a2a.createGroup("G3"); - ff_pipeline dummypipe; dummypipe.createGroup("G4"); dummypipe.createGroup("G0"); - - - if (atoi(argv[1]) == 0){ - dGroups::Instance()->setRunningGroup("G0"); - gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g1}, {ChannelType::FWD, g3}})); - gFarm.add_workers({new WrapperOUT(new RealSource(), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; - } else if (atoi(argv[1]) == 1){ - dGroups::Instance()->setRunningGroup("G1"); - gFarm.add_emitter(new ff_dreceiver(g1, 1, {{0, 0}})); - gFarm.add_collector(new ff_dsender({{ChannelType::FWD, g2}, {ChannelType::FWD, g3}})); - - gFarm.add_workers({new WrapperINOUT(new Source(2,0), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; - - } else if (atoi(argv[1]) == 2){ - dGroups::Instance()->setRunningGroup("G2"); - gFarm.add_emitter(new ff_dreceiver(g2, 2, {{0, 0}})); - gFarm.add_collector(new ff_dsender({ChannelType::FWD, g4})); - - gFarm.add_workers({new WrapperINOUT(new Sink(0), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; - - } else if (atoi(argv[1]) == 3) { - dGroups::Instance()->setRunningGroup("G3"); - gFarm.add_emitter(new ff_dreceiverH(g3, 2, {{1, 0}}, {1})); - gFarm.add_collector(new ff_dsenderH({{ChannelType::FWD, g2}, {ChannelType::FWD, g4}})); - gFarm.cleanup_emitter(); - gFarm.cleanup_collector(); - - auto s = new Source(2,1); - auto ea = new ff_comb(new WrapperIN(new ForwarderNode(s->deserializeF)), new EmitterAdapter(s, 2, 1, {{1,0}}, true), true, true); - - a2a.add_firstset({ea, new SquareBoxLeft({std::make_pair(1,0)})}, 0, true); - - auto sink = new Sink(1); - a2a.add_secondset({ - new ff_comb(new CollectorAdapter(sink, {1}, true), - new WrapperOUT(new ForwarderNode(sink->serializeF), 1, true), true, true), - new SquareBoxRight - }, true); - - - - } else { - dGroups::Instance()->setRunningGroup("G4"); - gFarm.add_emitter(new ff_dreceiver(g4, 2)); - gFarm.add_workers({new WrapperIN(new StringPrinter(), 1, true)}); - - gFarm.run_and_wait_end(); - return 0; - } - gFarm.add_workers({&a2a}); - gFarm.run_and_wait_end(); -} diff --git a/tests/distributed/test_group26.cpp b/tests/distributed/test_group26.cpp new file mode 100644 index 00000000..9b9eb989 --- /dev/null +++ b/tests/distributed/test_group26.cpp @@ -0,0 +1,189 @@ +/* + * FastFlow concurrent network: + * + * ------------- + * ----|-------------|---- + * | v | | + * Node1 ---> | Node2 ----> Node3 ->| --> Node4 + * | pipe1 | + * ----------------------- + * + * + * /<------------------ pipe ------------------>/ + * + * distributed version: + * + * G1: Node1 + * G2: Node2 + * G3: Node3 + * G4: Node4 + * + */ + + +#include +#include + +using namespace ff; + +struct myTask_t { + myTask_t() {} + myTask_t(myTask_t* t){ + str = std::string(t->str); + S.t = t->S.t; + S.f = t->S.f; + } + + std::string str; + struct S_t { + long t; + float f; + } S; + + template + void serialize(Archive & archive) { + archive(str, S.t, S.f); + } +}; + +struct Node1: ff_monode_t{ + Node1(long ntasks):ntasks(ntasks) {} + int svc_init(){ + ff::cout << "Node1 initialized!\n"; + return 0; + } + myTask_t* svc(myTask_t*in){ + for(long i=0; i < ntasks; i++) { + myTask_t* task = new myTask_t; + task->str="Hello"; + task->S.t = i; + task->S.f = i*1.0; + ff_send_out_to(task, 0); + } + return EOS; + } + + const long ntasks; +}; + + +struct Node2: ff_minode_t{ + Node2(long ntasks):ntasks(ntasks) {} + int svc_init(){ + ff::cout << "Node2 initialized!\n"; + return 0; + } + myTask_t* svc(myTask_t* t){ + if (fromInput()) { + t->str += std::string(" World"); + ff::cout << "Received input task #" << ++frominputtaks << std::endl; + return t; + } + ff::cout << "Received feedback task #" << ++feedbacktasks << " missing " << --ntasks << std::endl; + // --ntasks; + ff_send_out(t); + if (ntasks == 0 && eosreceived) return EOS; + return GO_ON; + } + void eosnotify(ssize_t) { + ff::cout << "[Node2] EOS NOTIFY CALLED\n"; + eosreceived=true; + if (ntasks==0) { + ff_send_out(EOS); + ntasks=-1; + } + } + long ntasks, frominputtaks = 0, feedbacktasks = 0; + bool eosreceived=false; +}; +struct Node3: ff_monode_t{ + int svc_init(){ + ff::cout << "Node3 initialized!\n"; + return 0; + } + myTask_t* svc(myTask_t* t){ + if (t->str == "Hello World") { + ff::cout << "Received input task #" << ++frominputtaks << std::endl; + t->str = "Feedback!"; + t->S.t += 1; + t->S.f += 1.0; + + ff_send_out_to(t, 0); // sends it back + return GO_ON; + } + ff::cout << "Received feedback task #" << ++feedbacktasks << std::endl; + ff_send_out_to(t, 1); // forward + return GO_ON; + } + long frominputtaks = 0, feedbacktasks = 0; +}; + +struct Node4: ff_node_t{ + Node4(long ntasks):ntasks(ntasks) {} + int svc_init(){ + ff::cout << "Node4 initialized!\n"; + return 0; + } + myTask_t* svc(myTask_t* t){ + ff::cout << "Node4: from (" << get_channel_id() << ") " << t->str << " (" << t->S.t << ", " << t->S.f << ")\n"; + ++processed; + return GO_ON; + } + void svc_end() { + if (processed != ntasks) { + std::cerr << "ERROR: processed " << processed << " tasks, expected " << ntasks << "\n"; + exit(-1); + } + std::cout << "RESULT OK, processed " << processed << " tasks\n"; + } + long ntasks; + long processed=0; +}; + + + +int main(int argc, char*argv[]){ + if (DFF_Init(argc, argv)<0 ) { + error("DFF_Init\n"); + return -1; + } + long ntasks = 20; + if (argc>1) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] + << " ntasks\n"; + return -1; + } + ntasks = std::stol(argv[1]); + } + + ff_pipeline pipe; + Node1 n1(ntasks); + Node2 n2(ntasks); + Node3 n3; + Node4 n4(ntasks); + ff_pipeline pipe1; + + pipe1.add_stage(&n2); + pipe1.add_stage(&n3); + pipe1.wrap_around(); + + pipe.add_stage(&n1); + pipe.add_stage(&pipe1); + pipe.add_stage(&n4); + + //----- defining the distributed groups ------ + + auto G1 = n1.createGroup("G1"); + auto G2 = pipe1.createGroup("G2") << &n2; + auto G3 = pipe1.createGroup("G3") << &n3; + auto G4 = n4.createGroup("G4"); + + // ------------------------------------------- + + if (pipe.run_and_wait_end()<0) { + error("running the main pipe\n"); + return -1; + } + return 0; +} diff --git a/tests/distributed/test_group26.json b/tests/distributed/test_group26.json new file mode 100644 index 00000000..1f9d2d6a --- /dev/null +++ b/tests/distributed/test_group26.json @@ -0,0 +1,24 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8000", + "name" : "G1", + "batchSize" : 3 + }, + { + "endpoint" : "localhost:8004", + "name" : "G2", + "batchSize" : 1 + }, + { + "endpoint" : "localhost:8005", + "name" : "G3", + "batchSize" : 1 + }, + { + "endpoint" : "localhost:8006", + "name" : "G4", + "batchSize" : 1 + } + ] +} diff --git a/tests/distributed/test_group7.cpp b/tests/distributed/test_group7.cpp index 79718d30..094373cb 100644 --- a/tests/distributed/test_group7.cpp +++ b/tests/distributed/test_group7.cpp @@ -42,7 +42,7 @@ * G3 */ -#include +#include #include #include @@ -139,12 +139,12 @@ struct S3 : ff_minode_t{ }; int main(int argc, char*argv[]){ -#if 0 + if (DFF_Init(argc, argv) != 0) { error("DFF_Init\n"); return -1; } -#endif + int N=10; if (argc==2) N=std::stol(argv[1]); @@ -202,7 +202,6 @@ int main(int argc, char*argv[]){ pipeMain.add_stage(&pipe0); pipeMain.add_stage(&a2a); pipeMain.add_stage(&s3); -#if 0 //----- defining the distributed groups ------ auto G1 = pipe0.createGroup("G1"); auto G2 = a2a.createGroup("G2"); @@ -213,7 +212,7 @@ int main(int argc, char*argv[]){ G3 << &pipe2 << &pipeA2A2; // ------------------------------------------- -#endif + // running the distributed groups if (pipeMain.run_and_wait_end()<0) { error("running a2a\n"); From 779239c837bbee1b9a41ebf19e955ad296503b7f Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 22 Jun 2022 15:14:21 +0200 Subject: [PATCH 181/202] minor cosmetic changes --- ff/distributed/ff_dintermediate.hpp | 6 ++--- ff/distributed/ff_dreceiver.hpp | 2 +- ff/distributed/ff_wrappers.hpp | 12 ++++----- tests/distributed/dwordcount/dwordcount.cpp | 3 ++- tests/distributed/dwordcount/dwordcount.json | 3 +-- tests/distributed/dwordcount/dwordcountb.cpp | 7 ----- tests/distributed/test_group25.cpp | 28 +++++++++++++++----- 7 files changed, 34 insertions(+), 27 deletions(-) diff --git a/ff/distributed/ff_dintermediate.hpp b/ff/distributed/ff_dintermediate.hpp index 94388197..35214bc5 100644 --- a/ff/distributed/ff_dintermediate.hpp +++ b/ff/distributed/ff_dintermediate.hpp @@ -35,8 +35,8 @@ class ff_IR { n->get_in_nodes(inputs); n->get_out_nodes(outputs); } - for (int i = 0; i < inputs.size(); i++) inputL.push_back(i); - for (int i = 0; i < outputs.size(); i++) outputL.push_back(i); + for (size_t i = 0; i < inputs.size(); i++) inputL.push_back(i); + for (size_t i = 0; i < outputs.size(); i++) outputL.push_back(i); return; } @@ -176,4 +176,4 @@ class ff_IR { } -#endif \ No newline at end of file +#endif diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 456536c7..593c6881 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -92,7 +92,7 @@ class ff_dreceiver: public ff_monode_t { } virtual void registerEOS(int sck){ - for(int i = 0; i < this->get_num_outchannels(); i++) + for(size_t i = 0; i < this->get_num_outchannels(); i++) ff_send_out(new message_t(0,0), i); neos++; } diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index a1ffe1a7..527dcff0 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -82,9 +82,9 @@ class WrapperIN: public internal_mi_transformer { if (!this->fromInput()) return n->svc(in); } - // received a logical EOS ------> must be inserted also in the wrapperIN!!!!! + // received a logical EOS if (msg->data.getLen() == 0){ - this->n->eosnotify(msg->sender); //msg->sender here is not consistent... always 0 + this->n->eosnotify(msg->sender); // TODO: msg->sender here is not consistent... always 0 return GO_ON; } @@ -130,8 +130,6 @@ class WrapperOUT: public internal_mo_transformer { id -= localFeedbacks; } - - message_t* msg = new message_t; msg->feedback = (id < remoteFeedbacks && remoteFeedbacks); if (!msg->feedback) id -= remoteFeedbacks; @@ -159,7 +157,7 @@ class WrapperOUT: public internal_mo_transformer { if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); mo->set_virtual_outchannels(outchannels); - mo->set_virtual_feedbackchannels(feedbackChannels); // maybe... must be checked! + mo->set_virtual_feedbackchannels(feedbackChannels); } return n->svc_init(); @@ -249,9 +247,9 @@ class WrapperINOUT: public internal_mi_transformer { if (in != nullptr) { message_t* msg = (message_t*)in; - // received a logical EOS ------> must be inserted also in the wrapperIN!!!!! + // received a logical EOS if (msg->data.getLen() == 0){ - this->n->eosnotify(msg->sender); //msg->sender here is not consistent... always 0 + this->n->eosnotify(msg->sender); // TODO: msg->sender here is not consistent... always 0 return GO_ON; } diff --git a/tests/distributed/dwordcount/dwordcount.cpp b/tests/distributed/dwordcount/dwordcount.cpp index 191c5499..c3513179 100644 --- a/tests/distributed/dwordcount/dwordcount.cpp +++ b/tests/distributed/dwordcount/dwordcount.cpp @@ -44,7 +44,8 @@ * /<---- pipe1 ---->/ /<--- pipe2 --->/ * /<----------------- a2a ------------------>/ * - * If -g is N then the groups G1...GN will be created. Each group will have n Source-Splitter and m Counter-Sink where n and m are the + * If -g is N then the groups G1...GN will be created. Each group will have n Source-Splitter + * and m Counter-Sink where n and m are the * values set with -p (i.e, -p n,m). This means that the FastFlow graph will have: * n*N Source-Sink replicas and m*N Counter-Sink replicas. * diff --git a/tests/distributed/dwordcount/dwordcount.json b/tests/distributed/dwordcount/dwordcount.json index fab97f4a..79e1b1af 100644 --- a/tests/distributed/dwordcount/dwordcount.json +++ b/tests/distributed/dwordcount/dwordcount.json @@ -2,8 +2,7 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "G1", - "OConn" : ["G2"] + "name" : "G1" }, { "name" : "G2", diff --git a/tests/distributed/dwordcount/dwordcountb.cpp b/tests/distributed/dwordcount/dwordcountb.cpp index 023944e7..a7e90edd 100644 --- a/tests/distributed/dwordcount/dwordcountb.cpp +++ b/tests/distributed/dwordcount/dwordcountb.cpp @@ -399,13 +399,6 @@ int main(int argc, char* argv[]) { a2a.add_secondset(R, true); ff_pipeline pipeMain(false, qlen, qlen, true); pipeMain.add_stage(&a2a); -#if 0 - if (DFF_getMyGroup() == "G1") { - threadMapper::instance()->setMappingList("0,1,2,3,4,5,6,7,8,9,10,11, 24,25,26,27,28,29,30,31,32,33,34,35"); - } else { - threadMapper::instance()->setMappingList("12,13,14,15,16,17,18,19,20,21,22,23, 36,37,38,39,40,41,42,43,44,45,46,47"); - } -#endif std::cout << "Starting " << pipeMain.numThreads() << " threads\n\n"; /// evaluate topology execution time diff --git a/tests/distributed/test_group25.cpp b/tests/distributed/test_group25.cpp index dc9c17fd..46fd48e3 100644 --- a/tests/distributed/test_group25.cpp +++ b/tests/distributed/test_group25.cpp @@ -126,19 +126,35 @@ int main(int argc, char*argv[]){ a2a.add_firstset({new Node2, new Node2}, 0, true); a2a.add_secondset({new Node3, new Node3, new Node3}, true); - pipe.add_stage(&n1); - pipe.add_stage(&a2a); +#if defined(POINTER_BASED_VERSION) + // it creates the distributed groups from level1 nodes of the skeleton tree + + pipe.add_stage(&n1); // the default pointer-based add_stage + pipe.add_stage(&a2a); // the default pointer-based add_stage pipe.wrap_around(); //----- defining the distributed groups ------ - //auto G0 = pipe.createGroup("G1"); - //G0 << pipe.get_firststage(); - //auto G1 = pipe.createGroup("G2"); - //G1 << pipe.get_laststage(); n1.createGroup("G1"); a2a.createGroup("G2"); // ------------------------------------------- +#else + // it creates the distributed groups from the level0 main pipeline + + // this version of add_stage adds the stage to the pipeline by using references to the nodes + // it means the reference cannot used in the '<<' operator to add the node to the distributed group + // because of the copies + pipe.add_stage(n1); // a copy of n1 is added to the pipeline as first stage + pipe.add_stage(a2a); // a copy of the a2a is added to the pipeline as second stage + pipe.wrap_around(); + + //----- defining the distributed groups ------ + auto G0 = pipe.createGroup("G1"); + G0 << pipe.get_firststage(); + auto G1 = pipe.createGroup("G2"); + G1 << pipe.get_laststage(); + // ------------------------------------------- +#endif if (pipe.run_and_wait_end()<0) { error("running the main pipe\n"); From 24a22ed18da8564662d66a15690c63ee2c54f773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 22 Jun 2022 16:41:49 +0200 Subject: [PATCH 182/202] Added propagation of logical EOS to enable eosnotify system to work also in distributed version --- ff/distributed/ff_dreceiver.hpp | 9 +++++++-- ff/distributed/ff_dreceiverMPI.hpp | 20 +++++++++++++++++--- ff/distributed/ff_dsender.hpp | 5 +++++ ff/distributed/ff_dsenderMPI.hpp | 13 +++++++++++-- ff/distributed/ff_wrappers.hpp | 6 +++--- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 456536c7..1069ef5b 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -92,8 +92,6 @@ class ff_dreceiver: public ff_monode_t { } virtual void registerEOS(int sck){ - for(int i = 0; i < this->get_num_outchannels(); i++) - ff_send_out(new message_t(0,0), i); neos++; } @@ -166,7 +164,14 @@ class ff_dreceiver: public ff_monode_t { return 0; } + //logical EOS + if (chid == -2){ + for(int i = 0; i < this->get_num_outchannels(); i++) + ff_send_out_to(new message_t(sender, i), i); + return 0; + } + //pyshical EOS registerEOS(sck); return -1; } diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index 68de2368..d8e1881e 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -44,10 +44,12 @@ class ff_dreceiverMPI: public ff_monode_t { return 0; } - virtual void registerEOS(int rank){ - // logical EOS sent to nodes + virtual void registerLogicalEOS(int sender){ for(int i = 0; i < this->get_num_outchannels(); i++) - ff_send_out(new message_t(0,0), i); + ff_send_out_to(new message_t(sender, i), i); + } + + virtual void registerEOS(int rank){ neos++; } @@ -93,6 +95,10 @@ class ff_dreceiverMPI: public ff_monode_t { size_t sz = headers[3]; if (sz == 0){ + if (headers[2] == -2){ + registerLogicalEOS(headers[1]); + continue; + } registerEOS(status.MPI_SOURCE); continue; } @@ -117,6 +123,10 @@ class ff_dreceiverMPI: public ff_monode_t { for (size_t i = 0; i < (size_t)headers[0]; i++){ size_t sz = headers[3*i+3]; if (sz == 0){ + if (headers[3*i+2] == -2){ + registerLogicalEOS(headers[3*i+1]); + continue; + } registerEOS(status.MPI_SOURCE); assert(i+1 == (size_t)headers[0]); break; @@ -153,6 +163,10 @@ class ff_dreceiverHMPI : public ff_dreceiverMPI { size_t internalNEos = 0, externalNEos = 0; int next_rr_destination = 0; + virtual void registerLogicalEOS(int sender){ + for(int i = 0; i < this->get_num_outchannels()-1; i++) + ff_send_out_to(new message_t(sender, i), i); + } virtual void registerEOS(int rank){ neos++; diff --git a/ff/distributed/ff_dsender.hpp b/ff/distributed/ff_dsender.hpp index 91f30f8a..c3fc2b4f 100644 --- a/ff/distributed/ff_dsender.hpp +++ b/ff/distributed/ff_dsender.hpp @@ -296,6 +296,9 @@ class ff_dsender: public ff_minode_t { } void eosnotify(ssize_t id) { + for (const auto& sck : sockets) + batchBuffers[sck].push(new message_t(id, -2)); + if (++neos >= this->get_num_inchannels()) { // all input EOS received, now sending the EOS to all connections for(const auto& sck : sockets) { @@ -482,6 +485,8 @@ class ff_dsenderH : public ff_dsender { shutdown(sck, SHUT_WR); } } + + } void svc_end() { diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index a0cf36af..1006cbfc 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -192,7 +192,12 @@ class ff_dsenderMPI: public ff_minode_t { return this->GO_ON; } - void eosnotify(ssize_t) { + void eosnotify(ssize_t id) { + for (auto& [rank, _] : ranks){ + auto& buffs = buffers[rank]; + buffs.second[buffs.first]->push(new message_t(id, -2)); + } + if (++neos >= this->get_num_inchannels()) for(auto& [rank, ct] : ranks){ auto& buffs = buffers[rank]; @@ -289,7 +294,11 @@ class ff_dsenderHMPI : public ff_dsenderMPI { buffs.second[buffs.first]->pushEOS(); } } - ff_dsenderMPI::eosnotify(id); + if (++neos >= this->get_num_inchannels()) + for(auto& [rank, ct] : ranks){ + auto& buffs = buffers[rank]; + buffs.second[buffs.first]->pushEOS(); + } } }; diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index a1ffe1a7..1ca6e3cd 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -70,7 +70,7 @@ class WrapperIN: public internal_mi_transformer { } void * svc(void* in) { - // with feedback channels in might be null + // with feedback channels it might be null if (in == nullptr) return n->svc(nullptr); message_t* msg = (message_t*)in; @@ -82,9 +82,9 @@ class WrapperIN: public internal_mi_transformer { if (!this->fromInput()) return n->svc(in); } - // received a logical EOS ------> must be inserted also in the wrapperIN!!!!! + // received a logical EOS if (msg->data.getLen() == 0){ - this->n->eosnotify(msg->sender); //msg->sender here is not consistent... always 0 + this->n->eosnotify(msg->sender); return GO_ON; } From 047333157ad68d47a567102c91773668f8d59804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 22 Jun 2022 16:44:25 +0200 Subject: [PATCH 183/202] Added propagation of logical EOS to enable eosnotify system to work also in distributed version --- ff/distributed/ff_dreceiver.hpp | 13 +++++++++++-- ff/distributed/ff_dsenderMPI.hpp | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 1069ef5b..4b4a00c7 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -91,6 +91,11 @@ class ff_dreceiver: public ff_monode_t { return 0; //this->sendRoutingTable(sck, reachableDestinations); } + virtual void registerLogicalEOS(int sender){ + for(int i = 0; i < this->get_num_outchannels(); i++) + ff_send_out_to(new message_t(sender, i), i); + } + virtual void registerEOS(int sck){ neos++; } @@ -166,8 +171,7 @@ class ff_dreceiver: public ff_monode_t { } //logical EOS if (chid == -2){ - for(int i = 0; i < this->get_num_outchannels(); i++) - ff_send_out_to(new message_t(sender, i), i); + registerLogicalEOS(sender); return 0; } @@ -311,6 +315,11 @@ class ff_dreceiverH : public ff_dreceiver { size_t internalNEos = 0, externalNEos = 0; long next_rr_destination = 0; + void registerLogicalEOS(int sender){ + for(int i = 0; i < this->get_num_outchannels()-1; i++) + ff_send_out_to(new message_t(sender, i), i); + } + void registerEOS(int sck){ neos++; size_t internalConn = std::count_if(std::begin(sck2ChannelType), diff --git a/ff/distributed/ff_dsenderMPI.hpp b/ff/distributed/ff_dsenderMPI.hpp index 1006cbfc..c16f72ea 100644 --- a/ff/distributed/ff_dsenderMPI.hpp +++ b/ff/distributed/ff_dsenderMPI.hpp @@ -90,7 +90,8 @@ class ff_dsenderMPI: public ff_minode_t { void waitCompletion(){ if (blocked){ MPI_Wait(&headersR, MPI_STATUS_IGNORE); - MPI_Wait(&datasR, MPI_STATUS_IGNORE); + if (currData->data.getLen() > 0) + MPI_Wait(&datasR, MPI_STATUS_IGNORE); if (currData) delete currData; blocked = false; } @@ -99,7 +100,8 @@ class ff_dsenderMPI: public ff_minode_t { waitCompletion(); currHeader[1] = m->sender; currHeader[2] = m->chid; currHeader[3] = m->data.getLen(); MPI_Isend(currHeader, 4, MPI_LONG, this->rank, DFF_HEADER_TAG, MPI_COMM_WORLD, &this->headersR); - MPI_Isend(m->data.getPtr(), m->data.getLen(), MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD, &datasR); + if (m->data.getLen() > 0) + MPI_Isend(m->data.getPtr(), m->data.getLen(), MPI_BYTE, rank, DFF_TASK_TAG, MPI_COMM_WORLD, &datasR); currData = m; blocked = true; return 1; From d3481349c6fe211d17409ef443f9848608b4ebad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 22 Jun 2022 16:51:51 +0200 Subject: [PATCH 184/202] Fixed some warnings of the last commit --- ff/distributed/ff_dreceiver.hpp | 4 ++-- ff/distributed/ff_dreceiverMPI.hpp | 4 ++-- ff/distributed/ff_wrappers.hpp | 4 ---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ff/distributed/ff_dreceiver.hpp b/ff/distributed/ff_dreceiver.hpp index 4b4a00c7..5bc15399 100644 --- a/ff/distributed/ff_dreceiver.hpp +++ b/ff/distributed/ff_dreceiver.hpp @@ -92,7 +92,7 @@ class ff_dreceiver: public ff_monode_t { } virtual void registerLogicalEOS(int sender){ - for(int i = 0; i < this->get_num_outchannels(); i++) + for(size_t i = 0; i < this->get_num_outchannels(); i++) ff_send_out_to(new message_t(sender, i), i); } @@ -316,7 +316,7 @@ class ff_dreceiverH : public ff_dreceiver { long next_rr_destination = 0; void registerLogicalEOS(int sender){ - for(int i = 0; i < this->get_num_outchannels()-1; i++) + for(size_t i = 0; i < this->get_num_outchannels()-1; i++) ff_send_out_to(new message_t(sender, i), i); } diff --git a/ff/distributed/ff_dreceiverMPI.hpp b/ff/distributed/ff_dreceiverMPI.hpp index d8e1881e..fcd4c593 100644 --- a/ff/distributed/ff_dreceiverMPI.hpp +++ b/ff/distributed/ff_dreceiverMPI.hpp @@ -45,7 +45,7 @@ class ff_dreceiverMPI: public ff_monode_t { } virtual void registerLogicalEOS(int sender){ - for(int i = 0; i < this->get_num_outchannels(); i++) + for(size_t i = 0; i < this->get_num_outchannels(); i++) ff_send_out_to(new message_t(sender, i), i); } @@ -164,7 +164,7 @@ class ff_dreceiverHMPI : public ff_dreceiverMPI { int next_rr_destination = 0; virtual void registerLogicalEOS(int sender){ - for(int i = 0; i < this->get_num_outchannels()-1; i++) + for(size_t i = 0; i < this->get_num_outchannels()-1; i++) ff_send_out_to(new message_t(sender, i), i); } diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 010f55e1..55b02ede 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -84,11 +84,7 @@ class WrapperIN: public internal_mi_transformer { // received a logical EOS if (msg->data.getLen() == 0){ -<<<<<<< HEAD this->n->eosnotify(msg->sender); -======= - this->n->eosnotify(msg->sender); // TODO: msg->sender here is not consistent... always 0 ->>>>>>> 779239c837bbee1b9a41ebf19e955ad296503b7f return GO_ON; } From 2f7b6b699c5f660887bfaeb01937cb1bce4bec73 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 22 Jun 2022 18:18:28 +0200 Subject: [PATCH 185/202] by-node mapping in MPI --- ff/distributed/loader/dff_run.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index cde238c4..7a4f707c 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -306,7 +306,7 @@ int main(int argc, char** argv) { char command[350]; - sprintf(command, "mpirun -np %lu --hostfile %s %s --DFF_Config=%s", parsedGroups.size(), hostFile.c_str(), executable.c_str(), configFile.c_str()); + sprintf(command, "mpirun -np %lu --hostfile %s --map-by node -%s --DFF_Config=%s", parsedGroups.size(), hostFile.c_str(), executable.c_str(), configFile.c_str()); std::cout << "mpicommand: " << command << "\n"; From 1fe47531dd5367d0e3977273ade20293f6028235 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Wed, 22 Jun 2022 19:31:55 +0200 Subject: [PATCH 186/202] typo correction --- ff/distributed/loader/dff_run.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 7a4f707c..567cf4ac 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -306,7 +306,7 @@ int main(int argc, char** argv) { char command[350]; - sprintf(command, "mpirun -np %lu --hostfile %s --map-by node -%s --DFF_Config=%s", parsedGroups.size(), hostFile.c_str(), executable.c_str(), configFile.c_str()); + sprintf(command, "mpirun -np %lu --hostfile %s --map-by node %s --DFF_Config=%s", parsedGroups.size(), hostFile.c_str(), executable.c_str(), configFile.c_str()); std::cout << "mpicommand: " << command << "\n"; From 47d3b12ce95e3541c470e17aa38368fb10d3467d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 27 Jun 2022 17:35:49 +0200 Subject: [PATCH 187/202] Added traffic monitor application benchmark test --- tests/distributed/dtrafficmonitoring/Makefile | 71 +++ .../dtrafficmonitoring/geo_model.hpp | 349 +++++++++++ .../dtrafficmonitoring/trafficmonitoring.cpp | 592 ++++++++++++++++++ 3 files changed, 1012 insertions(+) create mode 100644 tests/distributed/dtrafficmonitoring/Makefile create mode 100644 tests/distributed/dtrafficmonitoring/geo_model.hpp create mode 100644 tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp diff --git a/tests/distributed/dtrafficmonitoring/Makefile b/tests/distributed/dtrafficmonitoring/Makefile new file mode 100644 index 00000000..4829ed2b --- /dev/null +++ b/tests/distributed/dtrafficmonitoring/Makefile @@ -0,0 +1,71 @@ + +CXXFLAGS += -std=c++20 +ifdef DEBUG + OPTIMIZE_FLAGS += -g -fno-inline-functions +else + OPTIMIZE_FLAGS += -O3 -finline-functions -DNDEBUG +endif +ifdef DISABLE_FF_DISTRIBUTED + CXXFLAGS += -DDISABLE_FF_DISTRIBUTED +endif +ifdef EXCLUDE_BLOCKING + CXXFLAGS += -DDFF_EXCLUDE_BLOCKING +endif +ifdef PRINT_IR + CXXFLAGS += -DPRINT_IR +endif +ifdef EXCLUDE_MPI + CXXFLAGS += -DDFF_EXCLUDE_MPI +else +ifdef MPI_HOME + INCS += `pkg-config --cflags-only-I $(MPI_HOME)/lib/pkgconfig/ompi-cxx.pc` + LIBS += `pkg-config --libs $(MPI_HOME)/lib/pkgconfig/ompi-cxx.pc` +else + CXXFLAGS += -DDFF_EXCLUDE_MPI +endif +endif +ifdef FF_HOME + INCS += -I$(FF_HOME) +else + INCS += -I ~/fastflow +endif +ifdef CEREAL_HOME + INCS += -I$(CEREAL_HOME) +else + INCS += -I ~/cereal +endif + +#gdal include +INCS += -I /usr/local/include + +CXXFLAGS += -Wall +LIBS += -pthread -L /usr/local/Cellar/gdal/3.5.0_1/lib/ -lgdal +INCLUDES = $(INCS) + +SOURCES = $(wildcard *.cpp) +TARGET = $(SOURCES:.cpp=) + +.PHONY: all clean cleanall +.SUFFIXES: .c .cpp .o + +%.d: %.cpp + set -e; $(CXX) -MM $(INCLUDES) $(CXXFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.d: %.c + set -e; $(CC) -MM $(INCLUDES) $(CFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< +%: %.cpp + $(CXX) $(INCLUDES) $(CXXFLAGS) $(OPTIMIZE_FLAGS) -o $@ $< $(LDFLAGS) $(LIBS) + +all: $(TARGET) + +clean: + -rm -fr *.o *~ +cleanall: clean + -rm -fr $(TARGET) *.d ./socket* + +include $(OBJS:.o=.d) diff --git a/tests/distributed/dtrafficmonitoring/geo_model.hpp b/tests/distributed/dtrafficmonitoring/geo_model.hpp new file mode 100644 index 00000000..7bad4988 --- /dev/null +++ b/tests/distributed/dtrafficmonitoring/geo_model.hpp @@ -0,0 +1,349 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "ogrsf_frmts.h" +#include "ogr_geometry.h" +#include "ogr_feature.h" + +using namespace std; + +class Polygon { +private: + vector points; + double x_min; + double x_max; + double y_min; + double y_max; + + /** + * Check if the point p is contained in the polygon. + * @param p point + * @return true if p is inside the polygon, false otherwise + */ + bool contains(OGRPoint& p) { + int counter = 0; + + if (p.getX() < x_min || p.getX() > x_max || p.getY() < y_min || p.getY() > y_max) + return false; + + for (int i = 0; i < points.size() - 1; i++) { + if ((points.at(i)->getY() != points.at(i + 1)->getY()) && + ((p.getY() < points.at(i)->getY()) || (p.getY() < points.at(i + 1)->getY())) && + ((p.getY() > points.at(i)->getY()) || (p.getY() > points.at(i + 1)->getY()))) + { + double ux = 0; + double uy = 0; + double dx = 0; + double dy = 0; + int dir = 0; + + if (points.at(i)->getY() > points.at(i + 1)->getY()) { + uy = points.at(i)->getY(); + dy = points.at(i + 1)->getY(); + ux = points.at(i)->getX(); + dx = points.at(i + 1)->getX(); + dir = 0; // downward direction + } else { + uy = points.at(i + 1)->getY(); + dy = points.at(i)->getY(); + ux = points.at(i + 1)->getX(); + dx = points.at(i)->getX(); + dir = 1; // upward direction + } + + double tx = 0; + if (ux != dx){ + double k = (uy - dy) / (ux - dx); + double b = ((uy - k * ux) + (dy - k * dx)) / 2; + tx = (p.getY() - b) / k; + } else { + tx = ux; + } + + if (tx > p.getX()) { + if(dir == 1 && p.getY() != points.at(i + 1)->getY()) + counter++; + else if(p.getY() != points.at(i)->getY()) + counter++; + } + } + } + return (counter % 2) != 0; // (counter % 2) == 0 means point is outside the polygon + } + + /** + * Compute the distance between 2 points (x1, y1) and (x2, y2). + * @param p1 x point 1 of coordinates (x1, y1) + * @param p2 y point 2 of coordinates (x2, y2) + * @return the distance between point 1 and point 2 + */ + double points_distance(OGRPoint& p1, OGRPoint& p2) { + double x1 = p1.getX(); + double y1 = p1.getY(); + double x2 = p2.getX(); + double y2 = p2.getY(); + return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } + + /** + * Compute the distance between point p0 of coordinates (x0, y0) and the + * line where p1 of coordinates (x1, y1) and p2 of coordinates (x2, y2) lay. + * @param p1 point of the line + * @param p2 point of the line + * @param p0 point we want to measure the distance from the line + * @return the distance between point p0 and the line + */ + double point_to_line_distance(OGRPoint& p1, OGRPoint& p2, OGRPoint& p0) { + double a, b, c; // coefficients of the line ax + by + c = 0 + double distance; + + a = points_distance(p1, p2); + b = points_distance(p1, p0); + c = points_distance(p2, p0); + + if (c <= 0.000001 || b <= 0.000001) { // p0 lays on the line + distance = 0; + } else if (a <= 0.000001) { // p1 and p2 are equal, the distance is b or c (equal) + distance = b; + } else if (c * c >= a * a + b * b) { // Pythagoras' theorem + distance = b; + } else if (b * b >= a * a + c * c) { // Pythagoras' theorem + distance = c; + } else { + double p = (a + b + c) / 2; + double s = sqrt(p * (p - a) * (p - b) * (p - c)); + distance = 2 * s / a; + } + return distance; + } + +public: + + /** + * Constructor. + * @param _points vector of points that define the polygon + */ + Polygon(vector& _points): points(_points), + x_min(DBL_MAX), x_max(DBL_MIN), y_min(DBL_MAX), y_max(DBL_MIN) + { + // update min/max x and min/max y values among all the points + for (auto& p : points) { + if (p->getX() < x_min) + x_min = p->getX(); + if (p->getX() > x_max) + x_max = p->getX(); + if (p->getY() < y_min) + y_min = p->getY(); + if (p->getY() > y_max) + y_max = p->getY(); + } + } + + /** + * Check if the point p lies within the road (the polygon). + * @param p point + * @param road_width width of the road + * @param points points defining the road polygon + * @return true if the point falls inside the road polygon area, false otherwise + */ + bool match_to_road_line(OGRPoint& p, int road_width, double* last_min_dist, int road_id, int* last_road_id) { + bool match = false; + for (int i = 0; i < points.size() - 1 && !match; i++) { + double distance = point_to_line_distance(*points.at(i), *points.at(i + 1), p) * 111.2 * 1000; + if (distance < *last_min_dist) { + *last_min_dist = distance; + *last_road_id = road_id; + } + + if (distance < road_width / 2.0) // * sqrt(2.0)) + match = true; + } + return match; + } + + /** + * Check if the point p lies within the road (the polygon). + * @param p point + * @param road_width width of the road + * @param points points defining the road polygon + * @return true if the point falls inside the road polygon area, false otherwise + */ + bool match_to_road_point(OGRPoint& p, int road_width, double* last_min_dist, int road_id, int* last_road_id) { + *last_min_dist = DBL_MAX; + bool match = false; + for (int i = 0; i < points.size() - 1 && !match; i++) { + double distance = points_distance(*points.at(i), p); + if (distance < *last_min_dist) { + *last_min_dist = distance; + *last_road_id = road_id; + } + + if (distance < road_width / 2.0) // * sqrt(2.0)) + match = true; + } + return match; + } + + /** + * Destructor. + */ + ~Polygon() {} +}; + + + + +class Road_Grid_List { +private: + unordered_map> grid_list; + +public: + + /** + * @brief Constructor. + */ + Road_Grid_List() {} + + /** + * @brief Method that reads the shapefile. + * + * Read the shapefile, process the road layer and construct a hash map where a key represents the coordinates of a + * central point and a value is a list of features corresponding to that point. + * @return 0 if the shapefile has been successfully read and the grid_list map created, -1 if an error occurred + */ + int read_shapefile(const string& shapefile_path) { + GDALAllRegister(); // registers all format drivers built into GDAL/OGR + + // open the input OGR data source (in this case the shapefile) and use a vector driver + GDALDataset *dataset = static_cast(GDALOpenEx(shapefile_path.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr)); + if (dataset == nullptr) { + cout << "Failed opening GDAL dataset " << shapefile_path << endl; + return -1; + } + //cout << "Successfully opened GDAL dataset " << dataset->GetDescription() << endl; + + // GDALDataset can potentially have many layers associated with it, in this case we have only one layer "roads" + OGRLayer *roads_layer = dataset->GetLayerByName("roads"); + roads_layer->ResetReading(); // ensure we are starting at the beginning of the "roads" layer + + // feature definition object associated with the layer contains the definitions of all the fields + OGRFeatureDefn *feature_def = roads_layer->GetLayerDefn(); + OGRFeature *feature = roads_layer->GetNextFeature(); + + // iterate through all the features in the "roads layer (return NULL when no more features are available) + while (feature != nullptr) { + // extract the geometry from the feature + OGRGeometry *geometry = feature->GetGeometryRef(); + if (geometry != nullptr) { + if (geometry->getGeometryType() == 2) { // GeometryType LINE, GeometryName LINESTRING + OGRLineString *line = geometry->toLineString(); + int length = line->getNumPoints(); + + unique_ptr p1(new OGRPoint()); + unique_ptr p2(new OGRPoint()); + line->getPoint(0, p1.get()); + line->getPoint(length - 1, p2.get()); + double center_x = (p1->getX() + p2->getX()) / 2 * 10; + double center_y = (p1->getY() + p2->getY()) / 2 * 10; + ostringstream map_ID; + map_ID << fixed << setprecision(0) << center_y << "_" << fixed << setprecision(0) << center_x; + // cout << "Point p1: <" << p1->getX() << ", " << p1->getY() << ">" << endl; + // cout << "Point p2: <" << p2->getX() << ", " << p2->getY() << ">" << endl; + // cout << "MapID: " << map_ID.str() << endl; + + if (grid_list.find(map_ID.str()) == grid_list.end()) + grid_list.emplace(make_pair(map_ID.str(), vector())); + else + grid_list.at(map_ID.str()).push_back(feature); + } + } + feature = roads_layer->GetNextFeature(); + } + OGRFeature::DestroyFeature(feature); // method GetNextFeature() returns a copy of the feature that must be freed + GDALClose(dataset); + return 0; + } + + /** + * @brief Method that compute a road ID for each GPS position. + * + * Evaluate if there exists a road IDs corresponding to the GPS coordinates contained in point. + * @param point GPS coordinates (longitude and latitude) generated by a vehicle + * @return the road ID if a match is found, -1 otherwise + */ + int fetch_road_ID(OGRPoint point) { + double map_ID_lon = point.getX() * 10; + double map_ID_lat = point.getY() * 10; + ostringstream map_ID; + map_ID << fixed << setprecision(0) << map_ID_lat << "_" << fixed << setprecision(0) << map_ID_lon; + // cout << "Point point: <" << point.getX() << ", " << point.getY() << ">" << endl; + // cout << "MapID: " << map_ID.str() << endl; + + int width = 5; + int last_min_road_ID = -2; + double last_min_distance = DBL_MAX; + int grid_count = 0; + int road_count = 0; + + for (auto entry : grid_list) { + grid_count++; + string key = entry.first; + // cout << "Grid list entry " << grid_count << " key " << key << " vs " << map_ID.str() << endl; + + if (key == map_ID.str()) { + for (auto feature : entry.second) { // entry.second is a vector (the road_list) + road_count++; + // retrieve the attribute field road_id of the feature + uint64_t road_ID = feature->GetFieldAsInteger64("osm_id"); + + OGRGeometry* geometry = feature->GetGeometryRef(); + vector points; + if (geometry != nullptr) { + // wkbFlatten() macro is used to convert the type for a wkbPoint25D (a point with a z coordinate) into + // the base 2D geometry type code (wkbPoint); for each 2D geometry type there is a corresponding 2.5D type code; + // since the 2D and 2.5D geometry cases are handled by the same C++ class, this code handles 2D or 3D cases properly + /*if(wkbFlatten(geometry->getGeometryType()) == wkbPoint) { // GeometryType POINT + cout << "Geometry type POINT" << endl; + OGRPoint* p = geometry->toPoint(); + points.push_back(p); + cout << "Point " << p->getX() << ", " << p->getY() << ">" << endl; + } else */ + + if (geometry->getGeometryType() == 2) { // GeometryType LINE, GeometryName LINESTRING + OGRLineString* line = geometry->toLineString(); + for (int i = 0; i < line->getNumPoints(); i++) { + OGRPoint* p = new OGRPoint(); + line->getPoint(i, p); + points.push_back(p); + } + } + } + + Polygon road(points); + if (road.match_to_road_line(point, width, &last_min_distance, road_ID, &last_min_road_ID)) + return road_ID; + + for (auto p : points) + delete p; + } + if (last_min_distance < sqrt((width * width) + (10 * 10))) + return last_min_road_ID; + else + return -1; + } + + } + return -1; + } + + /** + * Destructor. + */ + ~Road_Grid_List() {} +}; \ No newline at end of file diff --git a/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp new file mode 100644 index 00000000..95a714b5 --- /dev/null +++ b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp @@ -0,0 +1,592 @@ + +#include +#include +#include +#include +#include +#include +#include "geo_model.hpp" + + +using namespace std; +using namespace ff; + +/// application run time (source generates the stream for app_run_time seconds, then sends out EOS) +unsigned long app_run_time = 1 * 1000000000L; // 60 seconds +const size_t qlen = 2048; + + +/// components and topology name +const string topology_name = "TrafficMonitoring"; +const string source_name = "source"; +const string map_match_name = "map_matcher"; +const string speed_calc_name = "speed_calculator"; +const string sink_name = "sink"; + +typedef enum { BEIJING, DUBLIN } city; + +/// information contained in each record in the Beijing dataset +typedef enum { TAXI_ID_FIELD, NID_FIELD, DATE_FIELD, TAXI_LATITUDE_FIELD, TAXI_LONGITUDE_FIELD, + TAXI_SPEED_FIELD, TAXI_DIRECTION_FIELD } beijing_record_field; + +/// information contained in each record in the Dublin dataset +typedef enum { TIMESTAMP_FIELD, LINE_ID_FIELD, BUS_DIRECTION_FIELD, JOURNEY_PATTERN_ID_FIELD, TIME_FRAME_FIELD, + VEHICLE_JOURNEY_ID_FIELD, OPERATOR_FIELD, CONGESTION_FIELD, BUS_LONGITUDE_FIELD, BUS_LATITUDE_FIELD, + DELAY_FIELD, BLOCK_ID_FIELD, BUS_ID_FIELD, STOP_ID_FIELD, AT_STOP_ID_FIELD } dublin_record_field; + +/// Beijing bounding box +const double beijing_lat_min = 39.689602; +const double beijing_lat_max = 40.122410; +const double beijing_lon_min = 116.105789; +const double beijing_lon_max = 116.670021; + +/// Dublin bounding box +const double dublin_lat_min = 53.28006; +const double dublin_lat_max = 53.406071; +const double dublin_lon_min = -6.381911; +const double dublin_lon_max = -6.141994; + +/// application parameters +city _monitored_city = BEIJING; // user can choose between two options: BEIJING and DUBLIN + +const string _beijing_input_file = "dataset/taxi-traces.csv"; // path of the Beijing dataset to be used +const string _dublin_input_file = "../data/bus-traces_20130101.csv"; // path of the Dublin dataset to be used + +const string _beijing_shapefile = "dataset/beijing/roads.shp"; // path of the Beijing shape file +const string _dublin_shapefile = "../data/dublin/roads.shp"; // path of the Dublin shape file + +size_t _road_win_size = 1000; + +struct tuple_t { + double latitude; // vehicle latitude + double longitude; // vehicle longitude + double speed; // vehicle speed + int direction; // vehicle direction + size_t key; // vehicle_id that identifies the vehicle (taxi or bus) + uint64_t ts; + + // default constructor + tuple_t() : latitude(0.0), longitude(0.0), speed(0.0), direction(0), key(0) {} + + // constructor + tuple_t(double _latitude, double _longitude, double _speed, int _direction, size_t _key) : + latitude(_latitude), longitude(_longitude), speed(_speed), direction(_direction), key(_key) {} +}; + +struct result_t { + double speed; // vehicle speed + size_t key; // road ID corresponding to latitude and longitude coordinates of the vehicle + uint64_t ts; + + // default constructor + result_t(): speed(0.0), key(0) {} + + // constructor + result_t(double _speed, size_t _key, uint64_t _id, uint64_t _ts): speed(_speed), key(_key) {} +}; + +static inline unsigned long current_time_usecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000L + (t.tv_nsec / 1000); +} +static inline unsigned long current_time_nsecs() { + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (t.tv_sec)*1000000000L + t.tv_nsec; +} + +// Type of Beijing input records: +// < vehicle_ID_value, n_ID_value, date_value, latitude_value, longitude_value, speed_value, direction_value > +using beijing_record_t = tuple; + +// Type of Dublin input records: +// < timestamp_value, line_ID_value, direction_value, journey_pattern_ID_value, time_frame_value, vehicle_journey_ID_value, operator_value, +// congestion_value, longitude_value, latitude_value, delay_value, block_ID_value, bus_ID_value, stop_ID_value, at_stop_value > +using dublin_record_t = tuple; + +// global variables +vector beijing_parsed_file; // contains data extracted from the Beijing input file +vector dublin_parsed_file; // contains data extracted from the Dublin input file +vector dataset; // contains all the tuples in memory +unordered_map key_occ; // contains the number of occurrences of each key vehicle_id +Road_Grid_List road_grid_list; // contains data extracted from the city shapefile +atomic sent_tuples; // total number of tuples sent by all the sources + +/** + * @brief Parse the input file. + * + * The file is parsed and saved in memory. + * @param file_path the path of the input dataset file + */ +void parse_dataset(const string& file_path) { + ifstream file(file_path); + if (file.is_open()) { + size_t all_records = 0; // counter of all records (dataset line) read + size_t incomplete_records = 0; // counter of the incomplete records + string line; + while (getline(file, line)) { + // process file line + int token_count = 0; + vector tokens; + size_t last = 0; + size_t next = 0; + while ((next = line.find(',', last)) != string::npos) { + tokens.push_back(line.substr(last, next - last)); + last = next + 1; + token_count++; + } + tokens.push_back(line.substr(last)); + token_count++; + // A record is valid if it contains at least 7 values (one for each field of interest) + // in the case in which the application analyzes data coming from Beijing taxi-traces. + if (_monitored_city == BEIJING) { + if (token_count >= 7) { + // save parsed file + beijing_record_t r(atoi(tokens.at(TAXI_ID_FIELD).c_str()), + atoi(tokens.at(NID_FIELD).c_str()), + tokens.at(DATE_FIELD), + atof(tokens.at(TAXI_LATITUDE_FIELD).c_str()), + atof(tokens.at(TAXI_LONGITUDE_FIELD).c_str()), + atof(tokens.at(TAXI_SPEED_FIELD).c_str()), + atoi(tokens.at(TAXI_DIRECTION_FIELD).c_str())); + beijing_parsed_file.push_back(r); + + // insert the key device_id in the map (if it is not present) + if (key_occ.find(get(r)) == key_occ.end()) { + key_occ.insert(make_pair(get(r), 0)); + } + } + else + incomplete_records++; + } + else if (_monitored_city == DUBLIN) { + // A record is valid if it contains at least 15 values (one for each field of interest) + // in the case in which the application analyzes data coming from Dublin bus-traces. + if (token_count >= 15) { + // save parsed file + dublin_record_t r(atol(tokens.at(TIMESTAMP_FIELD).c_str()), + atoi(tokens.at(LINE_ID_FIELD).c_str()), + atoi(tokens.at(BUS_DIRECTION_FIELD).c_str()), + tokens.at(JOURNEY_PATTERN_ID_FIELD), + tokens.at(TIME_FRAME_FIELD), + atoi(tokens.at(VEHICLE_JOURNEY_ID_FIELD).c_str()), + tokens.at(OPERATOR_FIELD), + atoi(tokens.at(CONGESTION_FIELD).c_str()), + atof(tokens.at(BUS_LONGITUDE_FIELD).c_str()), + atof(tokens.at(BUS_LATITUDE_FIELD).c_str()), + atoi(tokens.at(DELAY_FIELD).c_str()), + atoi(tokens.at(BLOCK_ID_FIELD).c_str()), + atoi(tokens.at(BUS_ID_FIELD).c_str()), + atoi(tokens.at(STOP_ID_FIELD).c_str()), + atoi(tokens.at(AT_STOP_ID_FIELD).c_str())); + dublin_parsed_file.push_back(r); + + // insert the key device_id in the map (if it is not present) + if (key_occ.find(get(r)) == key_occ.end()) { + key_occ.insert(make_pair(get(r), 0)); + } + } + else + incomplete_records++; + } + + all_records++; + } + file.close(); + //if (_monitored_city == BEIJING) print_taxi_parsing_info(beijing_parsed_file, all_records, incomplete_records); + //else if (_monitored_city == DUBLIN) print_bus_parsing_info(dublin_parsed_file, all_records, incomplete_records); + } +} + +/** + * @brief Process parsed data and create all the tuples. + * + * The created tuples are maintained in memory. The source node will generate the stream by + * reading all the tuples from main memory. + */ +void create_tuples() { + if (_monitored_city == BEIJING) { + for (int next_tuple_idx = 0; next_tuple_idx < beijing_parsed_file.size(); next_tuple_idx++) { + // create tuple + beijing_record_t record = beijing_parsed_file.at(next_tuple_idx); + tuple_t t; + t.latitude = get(record); + t.longitude = get(record); + t.speed = get(record); + t.direction = get(record); + t.key = get(record); + //t.id = (key_occ.find(get(record)))->second++; + //t.ts = 0L; + dataset.insert(dataset.end(), t); + } + } + else if (_monitored_city == DUBLIN) { + for (int next_tuple_idx = 0; next_tuple_idx < dublin_parsed_file.size(); next_tuple_idx++) { + // create tuple + dublin_record_t record = dublin_parsed_file.at(next_tuple_idx); + tuple_t t; + t.latitude = get(record); + t.longitude = get(record); + t.speed = 0.0; // speed values are not present in the used dataset + t.direction = get(record); + t.key = get(record); + //t.id = (key_occ.find(get(record)))->second++; + //t.ts = 0L; + dataset.insert(dataset.end(), t); + } + } +} + +/** + * @brief Parse the shapefile and create a the Road_Grid_List data structure. + * + * The data structure containing the processed information about the roads of the city + * is passed to the MapMatcher node and use to implement the map matching logic. + */ +void read_shapefile() { + string shapefile_path = (_monitored_city == DUBLIN) ? _dublin_shapefile : _beijing_shapefile; + if (road_grid_list.read_shapefile(shapefile_path) == -1) + __throw_invalid_argument("Failed reading shapefile"); +} + +/** + * SOURCE NODE + **/ + +struct Source : ff_node_t { + int rate = 0; + size_t next_tuple_idx = 0; // index of the next tuple to be sent + int generations = 0; // counts the times the file is generated + long generated_tuples = 0; // tuples counter + + // time variables + unsigned long app_start_time; // application start time + unsigned long current_time; + + void active_delay(unsigned long waste_time) { + auto start_time = current_time_nsecs(); + bool end = false; + while (!end) { + auto end_time = current_time_nsecs(); + end = (end_time - start_time) >= waste_time; + } + } + + int svc_init(){ + app_start_time = current_time_nsecs(); + return 0; + } + + tuple_t* svc(tuple_t*){ + current_time = current_time_nsecs(); // get the current time + // generation loop + while (current_time - app_start_time <= app_run_time){ + if (next_tuple_idx == 0) { + generations++; + } + tuple_t* t = new tuple_t(dataset.at(next_tuple_idx)); + t->ts = current_time_nsecs(); + ff_send_out(t); // send the next tuple + generated_tuples++; + next_tuple_idx = (next_tuple_idx + 1) % dataset.size(); // index of the next tuple to be sent (if any) + if (rate != 0) { // active waiting to respect the generation rate + long delay_nsec = (long) ((1.0 / rate) * 1e9); + active_delay(delay_nsec); + } + current_time = current_time_nsecs(); // get the new current time + } + std::cout << "Sent EOS from Source!\n"; + return EOS; + } + + void svc_end(){ + sent_tuples.fetch_add(generated_tuples); // save the number of generated tuples + } + +}; + +/** + * MAP MATCHER + **/ + + +struct MapMatcher : ff_node_t { + size_t processed = 0; // counter of processed tuples + size_t valid_points = 0; // counter of tuples containing GPS coordinates (points) laying inside the city bounding box + size_t emitted = 0; // counter of tuples containing points that correspond to a valid road_id + Road_Grid_List road_grid_list; // object containing all the geometric features of the shapefile and used to do map matching + unordered_map key_occ; // contains the number of occurrences of each key road_id + + // city bounding box + double max_lon; + double min_lon; + double max_lat; + double min_lat; + + MapMatcher(Road_Grid_List& _road_grid_list) : road_grid_list(_road_grid_list){ + max_lon = (_monitored_city == DUBLIN) ? dublin_lon_max : beijing_lon_max; + min_lon = (_monitored_city == DUBLIN) ? dublin_lon_min : beijing_lon_min; + max_lat = (_monitored_city == DUBLIN) ? dublin_lat_max : beijing_lat_max; + min_lat = (_monitored_city == DUBLIN) ? dublin_lat_min : beijing_lat_min; + } + + void eosnotify(ssize_t){ + cout << "[MapMatcher] EOS notify called\n"; + } + + result_t* svc(tuple_t* t){ + cout << "[MapMatcher] recdived task! " << processed << std::endl; + if (t->speed >= 0 && t->longitude <= max_lon && t->longitude >= min_lon && t->latitude <= max_lat && t->latitude >= min_lat){ + OGRPoint p(t->longitude, t->latitude); + int road_id = road_grid_list.fetch_road_ID(p); + if (road_id != -1) { + // road_id keys + if (key_occ.find(road_id) == key_occ.end()) + key_occ.insert(make_pair(road_id, 0)); + + result_t* r = new result_t; + r->speed = t->speed; + r->key = road_id; + r->ts = t->ts; + ff_send_out(r); + emitted++; + + } + valid_points++; + } + processed++; + delete t; + return GO_ON; + } + + void svc_end(){ + cout << "[MapMatcher] Ended\n"; + } +}; + +/** + * SPEED CALCULATOR + **/ + +struct SpeedCalculator : ff_node_t { + + struct Road_Speed { + int road_id; + deque road_speeds; + size_t win_size; + double current_sum; + double incremental_average; + double squared_sum; + double incremental_variance; + size_t count_absolute; + + Road_Speed(int _road_id, double _speed): road_id(_road_id), current_sum(_speed), incremental_average(_speed), squared_sum(_speed * _speed), incremental_variance(0.0), win_size(_road_win_size), count_absolute(0){ + road_speeds.push_back(_speed); + } + + void update_average_speed(double speed) { + // control window size + if (road_speeds.size() > win_size - 1) { + current_sum -= road_speeds.at(0); + road_speeds.pop_front(); + } + + // update average speed value + if (road_speeds.size() == 1) { + road_speeds.push_back(speed); + current_sum += speed; + incremental_average = current_sum / road_speeds.size(); + squared_sum += (speed * speed); + incremental_variance = squared_sum - (road_speeds.size() * incremental_average * incremental_average); + } else { + double cur_avg = (current_sum + speed) / road_speeds.size() + 1; + double cur_var = (squared_sum + speed * speed) - (road_speeds.size() + 1) * cur_avg * cur_avg; + double standard_deviation = sqrt(cur_var / road_speeds.size() + 1); + + if (abs(speed - cur_avg) <= 2 * standard_deviation) { + road_speeds.push_back(speed); + current_sum += speed; + squared_sum += (speed * speed); + incremental_average = cur_avg; + incremental_variance = cur_var; + } + } + } + + ~Road_Speed() {} + }; + + size_t processed = 0; // tuples counter + unordered_map roads; + + result_t* svc(result_t* r){ + + if (roads.find(r->key) == roads.end()) { + Road_Speed rs(r->key, r->speed); + roads.insert(make_pair(r->key, rs)); + } else { + roads.at(r->key).update_average_speed(r->speed); + } + + r->speed = roads.at(r->key).incremental_average; + processed++; + return r; + } + + void svc_end(){ + cout << "[SpeedClaculator] Ended\n"; + } +}; + +/** + * SINK + **/ + +struct Sink : ff_node_t { + size_t processed = 0; + result_t* svc(result_t* r){ + processed++; // tuples counter + delete r; + return GO_ON; + } + + void svc_end(){ + cout << "[Sink] Ended\n"; + } +}; + + + + +//opt + +typedef enum { NONE, REQUIRED } opt_arg; // an option can require one argument or none + +const struct option long_opts[] = { + {"help", NONE, 0, 'h'}, + {"rate", REQUIRED, 0, 'r'}, // pipe start (source) parallelism degree + {"sampling", REQUIRED, 0, 's'}, // predictor parallelism degree + {"batch", REQUIRED, 0, 'b'}, // pipe end (sink) parallelism degree + {"parallelism", REQUIRED, 0, 'p'}, // pipe end (sink) parallelism degree + {"chaining", NONE, 0, 'c'}, + {0, 0, 0, 0} +}; + +// Main +int main(int argc, char* argv[]) { + /// parse arguments from command line + int option = 0; + int index = 0; + + string file_path; + size_t source_par_deg = 0; + size_t matcher_par_deg = 0; + size_t calculator_par_deg = 0; + size_t sink_par_deg = 0; + int rate = 0; + sent_tuples = 0; + long sampling = 0; + bool chaining = false; + size_t batch_size = 0; + if (argc == 9 || argc == 10) { + while ((option = getopt_long(argc, argv, "r:s:p:b:c:", long_opts, &index)) != -1) { + file_path = _beijing_input_file; + switch (option) { + case 'r': { + rate = atoi(optarg); + break; + } + case 's': { + sampling = atoi(optarg); + break; + } + case 'b': { + batch_size = atoi(optarg); + break; + } + case 'p': { + vector par_degs; + string pars(optarg); + stringstream ss(pars); + for (size_t i; ss >> i;) { + par_degs.push_back(i); + if (ss.peek() == ',') + ss.ignore(); + } + if (par_degs.size() != 4) { + printf("Error in parsing the input arguments\n"); + exit(EXIT_FAILURE); + } + else { + source_par_deg = par_degs[0]; + matcher_par_deg = par_degs[1]; + calculator_par_deg = par_degs[2]; + sink_par_deg = par_degs[3]; + } + break; + } + case 'c': { + chaining = true; + break; + } + default: { + printf("Error in parsing the input arguments\n"); + exit(EXIT_FAILURE); + } + } + } + } + else if (argc == 2) { + while ((option = getopt_long(argc, argv, "h", long_opts, &index)) != -1) { + switch (option) { + case 'h': { + printf("Parameters: --rate --sampling --batch --parallelism [--chaining]\n"); + exit(EXIT_SUCCESS); + } + } + } + } + else { + printf("Error in parsing the input arguments\n"); + exit(EXIT_FAILURE); + } + /// data pre-processing + parse_dataset(file_path); + create_tuples(); + read_shapefile(); + /// application starting time + unsigned long app_start_time = current_time_nsecs(); + cout << "Executing TrafficMonitoring with parameters:" << endl; + if (rate != 0) { + cout << " * rate: " << rate << " tuples/second" << endl; + } + else { + cout << " * rate: full_speed tupes/second" << endl; + } + cout << " * batch size: " << batch_size << endl; + cout << " * sampling: " << sampling << endl; + cout << " * source: " << source_par_deg << endl; + cout << " * map-matcher: " << matcher_par_deg << endl; + cout << " * speed-calculator: " << calculator_par_deg << endl; + cout << " * sink: " << sink_par_deg << endl; + cout << " * topology: source -> map-matcher -> speed-calculator -> sink" << endl; + + ff_pipeline p(false, qlen, qlen, true); + p.add_stage(new Source); + p.add_stage(new MapMatcher(road_grid_list)); + p.add_stage(new SpeedCalculator); + p.add_stage(new Sink); + + cout << "Executing topology" << endl; + /// evaluate topology execution time + volatile unsigned long start_time_main_usecs = current_time_usecs(); + p.run_and_wait_end(); + volatile unsigned long end_time_main_usecs = current_time_usecs(); + cout << "Exiting" << endl; + double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); + double throughput = sent_tuples / elapsed_time_seconds; + cout << "Measured throughput: " << (int) throughput << " tuples/second" << endl; + + return 0; +} \ No newline at end of file From e61d8785e46c0b92ca1eb2eacabd56c7a0d67138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 27 Jun 2022 17:40:10 +0200 Subject: [PATCH 188/202] Fixed some prints on traffic monitoring --- .../dtrafficmonitoring/trafficmonitoring.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp index 95a714b5..9ff76d40 100644 --- a/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp +++ b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp @@ -296,7 +296,6 @@ struct Source : ff_node_t { } current_time = current_time_nsecs(); // get the new current time } - std::cout << "Sent EOS from Source!\n"; return EOS; } @@ -331,12 +330,7 @@ struct MapMatcher : ff_node_t { min_lat = (_monitored_city == DUBLIN) ? dublin_lat_min : beijing_lat_min; } - void eosnotify(ssize_t){ - cout << "[MapMatcher] EOS notify called\n"; - } - result_t* svc(tuple_t* t){ - cout << "[MapMatcher] recdived task! " << processed << std::endl; if (t->speed >= 0 && t->longitude <= max_lon && t->longitude >= min_lon && t->latitude <= max_lat && t->latitude >= min_lat){ OGRPoint p(t->longitude, t->latitude); int road_id = road_grid_list.fetch_road_ID(p); @@ -360,9 +354,6 @@ struct MapMatcher : ff_node_t { return GO_ON; } - void svc_end(){ - cout << "[MapMatcher] Ended\n"; - } }; /** @@ -433,10 +424,6 @@ struct SpeedCalculator : ff_node_t { processed++; return r; } - - void svc_end(){ - cout << "[SpeedClaculator] Ended\n"; - } }; /** @@ -451,9 +438,6 @@ struct Sink : ff_node_t { return GO_ON; } - void svc_end(){ - cout << "[Sink] Ended\n"; - } }; From 3ec70b36e3e3590f2828fa73da3b9ed963cad6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Tue, 28 Jun 2022 18:30:14 +0200 Subject: [PATCH 189/202] Added rankfile fix for mpi execution on loader --- ff/distributed/loader/dff_run.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 567cf4ac..9971b7b2 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -41,6 +41,10 @@ #include namespace n_fs = std::filesystem; +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + enum Proto {TCP = 1 , MPI}; Proto usedProtocol; @@ -137,13 +141,15 @@ static inline void usage(char* progname) { } -std::string generateHostFile(std::vector& parsedGroups){ - std::string name = "/tmp/dffHostfile" + std::to_string(getpid()); +std::string generateRankFile(std::vector& parsedGroups){ + std::string name = "/tmp/dffRankfile" + std::to_string(getpid()); std::ofstream tmpFile(name, std::ofstream::out); - - for (const G& group : parsedGroups) - tmpFile << group.host << std::endl; + + for(int i = 0; i < parsedGroups.size(); i++) + tmpFile << "rank " << i << "=" << parsedGroups[i].host << " slot=0"; + /*for (const G& group : parsedGroups) + tmpFile << group.host << std::endl;*/ tmpFile.close(); // return the name of the temporary file just created; remember to remove it after the usage @@ -300,13 +306,13 @@ int main(int argc, char** argv) { } if (usedProtocol == Proto::MPI){ - std::string hostFile = generateHostFile(parsedGroups); - std::cout << "Hostfile: " << hostFile << std::endl; + std::string rankFile = generateRankFile(parsedGroups); + std::cout << "RankFile: " << rankFile << std::endl; // invoke mpirun using the just created hostfile char command[350]; - sprintf(command, "mpirun -np %lu --hostfile %s --map-by node %s --DFF_Config=%s", parsedGroups.size(), hostFile.c_str(), executable.c_str(), configFile.c_str()); + sprintf(command, "mpirun -np %lu --rankfile %s %s --DFF_Config=%s", parsedGroups.size(), rankFile.c_str(), executable.c_str(), configFile.c_str()); std::cout << "mpicommand: " << command << "\n"; From 3eeb28a591e0b4a4ab3ee65381b32ca464b7de43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Tue, 28 Jun 2022 18:33:24 +0200 Subject: [PATCH 190/202] Added rankfile fix for mpi execution on loader --- ff/distributed/loader/dff_run.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index 9971b7b2..ac9d9ec1 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -146,8 +146,8 @@ std::string generateRankFile(std::vector& parsedGroups){ std::ofstream tmpFile(name, std::ofstream::out); - for(int i = 0; i < parsedGroups.size(); i++) - tmpFile << "rank " << i << "=" << parsedGroups[i].host << " slot=0"; + for(size_t i = 0; i < parsedGroups.size(); i++) + tmpFile << "rank " << i << "=" << parsedGroups[i].host << " slot=0\n"; /*for (const G& group : parsedGroups) tmpFile << group.host << std::endl;*/ From 8b257575af9b61ff9fd0aeba01a45b021c30f0a1 Mon Sep 17 00:00:00 2001 From: Massimo Torquati Date: Tue, 28 Jun 2022 18:46:51 +0200 Subject: [PATCH 191/202] removed a tiny memory leak --- ff/distributed/ff_wrappers.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ff/distributed/ff_wrappers.hpp b/ff/distributed/ff_wrappers.hpp index 55b02ede..c5e45562 100644 --- a/ff/distributed/ff_wrappers.hpp +++ b/ff/distributed/ff_wrappers.hpp @@ -85,6 +85,7 @@ class WrapperIN: public internal_mi_transformer { // received a logical EOS if (msg->data.getLen() == 0){ this->n->eosnotify(msg->sender); + delete msg; return GO_ON; } @@ -250,6 +251,7 @@ class WrapperINOUT: public internal_mi_transformer { // received a logical EOS if (msg->data.getLen() == 0){ this->n->eosnotify(msg->sender); // TODO: msg->sender here is not consistent... always 0 + delete msg; return GO_ON; } From 7c769bb16689a4fbab58052f77a6e5782ffe6148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Wed, 29 Jun 2022 11:56:28 +0200 Subject: [PATCH 192/202] Distributed porting of traffic monitoring test --- .../dtrafficmonitoring/geo_model.hpp | 2 +- tests/distributed/dtrafficmonitoring/tm.json | 15 +++ .../dtrafficmonitoring/trafficmonitoring.cpp | 105 ++++++++++++------ 3 files changed, 89 insertions(+), 33 deletions(-) create mode 100644 tests/distributed/dtrafficmonitoring/tm.json diff --git a/tests/distributed/dtrafficmonitoring/geo_model.hpp b/tests/distributed/dtrafficmonitoring/geo_model.hpp index 7bad4988..d4480223 100644 --- a/tests/distributed/dtrafficmonitoring/geo_model.hpp +++ b/tests/distributed/dtrafficmonitoring/geo_model.hpp @@ -223,7 +223,7 @@ class Road_Grid_List { // open the input OGR data source (in this case the shapefile) and use a vector driver GDALDataset *dataset = static_cast(GDALOpenEx(shapefile_path.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr)); if (dataset == nullptr) { - cout << "Failed opening GDAL dataset " << shapefile_path << endl; + ff::cout << "Failed opening GDAL dataset " << shapefile_path << ff::endl; return -1; } //cout << "Successfully opened GDAL dataset " << dataset->GetDescription() << endl; diff --git a/tests/distributed/dtrafficmonitoring/tm.json b/tests/distributed/dtrafficmonitoring/tm.json new file mode 100644 index 00000000..2d5c01fa --- /dev/null +++ b/tests/distributed/dtrafficmonitoring/tm.json @@ -0,0 +1,15 @@ +{ + "groups" : [ + { + "endpoint" : "localhost:8004", + "name" : "Source" + },{ + "endpoint" : "localhost:8008", + "name" : "A2A" + }, + { + "name" : "Sink", + "endpoint": "localhost:8005" + } + ] +} diff --git a/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp index 9ff76d40..77b84a53 100644 --- a/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp +++ b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include "geo_model.hpp" @@ -12,7 +12,7 @@ using namespace std; using namespace ff; /// application run time (source generates the stream for app_run_time seconds, then sends out EOS) -unsigned long app_run_time = 1 * 1000000000L; // 60 seconds +unsigned long app_run_time = 1 * 10000000L; // 60 seconds const size_t qlen = 2048; @@ -71,6 +71,11 @@ struct tuple_t { // constructor tuple_t(double _latitude, double _longitude, double _speed, int _direction, size_t _key) : latitude(_latitude), longitude(_longitude), speed(_speed), direction(_direction), key(_key) {} + + template + void serialize(Archive & archive) { + archive(latitude,longitude,speed, direction, key, ts); + } }; struct result_t { @@ -83,6 +88,11 @@ struct result_t { // constructor result_t(double _speed, size_t _key, uint64_t _id, uint64_t _ts): speed(_speed), key(_key) {} + + template + void serialize(Archive & archive) { + archive(speed,key,ts); + } }; static inline unsigned long current_time_usecs() { @@ -254,7 +264,7 @@ void read_shapefile() { * SOURCE NODE **/ -struct Source : ff_node_t { +struct Source : ff_monode_t { int rate = 0; size_t next_tuple_idx = 0; // index of the next tuple to be sent int generations = 0; // counts the times the file is generated @@ -263,6 +273,8 @@ struct Source : ff_node_t { // time variables unsigned long app_start_time; // application start time unsigned long current_time; + + Source(int rate) : rate(rate) {} void active_delay(unsigned long waste_time) { auto start_time = current_time_nsecs(); @@ -310,7 +322,7 @@ struct Source : ff_node_t { **/ -struct MapMatcher : ff_node_t { +struct MapMatcher : ff_monode_t { size_t processed = 0; // counter of processed tuples size_t valid_points = 0; // counter of tuples containing GPS coordinates (points) laying inside the city bounding box size_t emitted = 0; // counter of tuples containing points that correspond to a valid road_id @@ -323,6 +335,8 @@ struct MapMatcher : ff_node_t { double max_lat; double min_lat; + int next_stage_par_deg = 0; + MapMatcher(Road_Grid_List& _road_grid_list) : road_grid_list(_road_grid_list){ max_lon = (_monitored_city == DUBLIN) ? dublin_lon_max : beijing_lon_max; min_lon = (_monitored_city == DUBLIN) ? dublin_lon_min : beijing_lon_min; @@ -330,6 +344,11 @@ struct MapMatcher : ff_node_t { min_lat = (_monitored_city == DUBLIN) ? dublin_lat_min : beijing_lat_min; } + int svc_init(){ + next_stage_par_deg = this->get_num_outchannels(); + return 0; + } + result_t* svc(tuple_t* t){ if (t->speed >= 0 && t->longitude <= max_lon && t->longitude >= min_lon && t->latitude <= max_lat && t->latitude >= min_lat){ OGRPoint p(t->longitude, t->latitude); @@ -343,7 +362,7 @@ struct MapMatcher : ff_node_t { r->speed = t->speed; r->key = road_id; r->ts = t->ts; - ff_send_out(r); + ff_send_out_to(r, road_id % next_stage_par_deg); emitted++; } @@ -360,7 +379,7 @@ struct MapMatcher : ff_node_t { * SPEED CALCULATOR **/ -struct SpeedCalculator : ff_node_t { +struct SpeedCalculator : ff_minode_t { struct Road_Speed { int road_id; @@ -430,14 +449,13 @@ struct SpeedCalculator : ff_node_t { * SINK **/ -struct Sink : ff_node_t { +struct Sink : ff_minode_t { size_t processed = 0; result_t* svc(result_t* r){ processed++; // tuples counter delete r; return GO_ON; } - }; @@ -459,6 +477,9 @@ const struct option long_opts[] = { // Main int main(int argc, char* argv[]) { + + DFF_Init(argc, argv); + /// parse arguments from command line int option = 0; int index = 0; @@ -539,38 +560,58 @@ int main(int argc, char* argv[]) { parse_dataset(file_path); create_tuples(); read_shapefile(); - /// application starting time - unsigned long app_start_time = current_time_nsecs(); - cout << "Executing TrafficMonitoring with parameters:" << endl; - if (rate != 0) { - cout << " * rate: " << rate << " tuples/second" << endl; - } - else { - cout << " * rate: full_speed tupes/second" << endl; + + if (DFF_getMyGroup() == "Source"){ + ff::cout << "Executing TrafficMonitoring with parameters:" << ff::endl; + if (rate != 0) { + ff::cout << " * rate: " << rate << " tuples/second" << ff::endl; + } + else { + ff::cout << " * rate: full_speed tupes/second" << ff::endl; + } + ff::cout << " * batch size: " << batch_size << ff::endl; + ff::cout << " * sampling: " << sampling << ff::endl; + ff::cout << " * source: " << source_par_deg << ff::endl; + ff::cout << " * map-matcher: " << matcher_par_deg << ff::endl; + ff::cout << " * speed-calculator: " << calculator_par_deg << ff::endl; + ff::cout << " * sink: " << sink_par_deg << ff::endl; + ff::cout << " * topology: source -> map-matcher -> speed-calculator -> sink" << ff::endl; } - cout << " * batch size: " << batch_size << endl; - cout << " * sampling: " << sampling << endl; - cout << " * source: " << source_par_deg << endl; - cout << " * map-matcher: " << matcher_par_deg << endl; - cout << " * speed-calculator: " << calculator_par_deg << endl; - cout << " * sink: " << sink_par_deg << endl; - cout << " * topology: source -> map-matcher -> speed-calculator -> sink" << endl; - + ff_pipeline p(false, qlen, qlen, true); - p.add_stage(new Source); - p.add_stage(new MapMatcher(road_grid_list)); - p.add_stage(new SpeedCalculator); - p.add_stage(new Sink); + Source source(rate); + source.createGroup("Source"); + p.add_stage(&source); + ff_a2a internalA2A(false, qlen, qlen, true); + vector matchers; + for(size_t i = 0; i < matcher_par_deg; i++) + matchers.push_back(new MapMatcher(road_grid_list)); + + vector calculators; + for(size_t i = 0; i < calculator_par_deg; i++) + calculators.push_back(new SpeedCalculator); + + internalA2A.add_firstset(matchers, 0, true); // ondemand???? + internalA2A.add_secondset(calculators, true); + + internalA2A.createGroup("A2A"); + + p.add_stage(&internalA2A); + Sink sink; + sink.createGroup("Sink"); + p.add_stage(&sink); - cout << "Executing topology" << endl; /// evaluate topology execution time volatile unsigned long start_time_main_usecs = current_time_usecs(); p.run_and_wait_end(); volatile unsigned long end_time_main_usecs = current_time_usecs(); - cout << "Exiting" << endl; + double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); double throughput = sent_tuples / elapsed_time_seconds; - cout << "Measured throughput: " << (int) throughput << " tuples/second" << endl; - + ff::cout << "Actual execution time (s): " << elapsed_time_seconds << ff::endl; + if (DFF_getMyGroup() == "Source"){ + ff::cout << "Tuples sent by source: " << sent_tuples << ff::endl; + ff::cout << "Measured throughput: " << (int) throughput << " tuples/second" << ff::endl; + } return 0; } \ No newline at end of file From 340d6a4819bb8020f6c516af1975fd00e29eee78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 30 Jun 2022 09:57:12 +0200 Subject: [PATCH 193/202] Refactored traffic monitoring test to be similar to the flink version --- tests/distributed/dtrafficmonitoring/tm.json | 6 +-- .../dtrafficmonitoring/trafficmonitoring.cpp | 50 +++++++++---------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/tests/distributed/dtrafficmonitoring/tm.json b/tests/distributed/dtrafficmonitoring/tm.json index 2d5c01fa..b08fe007 100644 --- a/tests/distributed/dtrafficmonitoring/tm.json +++ b/tests/distributed/dtrafficmonitoring/tm.json @@ -2,13 +2,13 @@ "groups" : [ { "endpoint" : "localhost:8004", - "name" : "Source" + "name" : "G0" },{ "endpoint" : "localhost:8008", - "name" : "A2A" + "name" : "G1" }, { - "name" : "Sink", + "name" : "G2", "endpoint": "localhost:8005" } ] diff --git a/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp index 77b84a53..ce41718a 100644 --- a/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp +++ b/tests/distributed/dtrafficmonitoring/trafficmonitoring.cpp @@ -12,7 +12,7 @@ using namespace std; using namespace ff; /// application run time (source generates the stream for app_run_time seconds, then sends out EOS) -unsigned long app_run_time = 1 * 10000000L; // 60 seconds +unsigned long app_run_time = 60 * 1000000000L; // 60 seconds const size_t qlen = 2048; @@ -561,7 +561,7 @@ int main(int argc, char* argv[]) { create_tuples(); read_shapefile(); - if (DFF_getMyGroup() == "Source"){ + if (DFF_getMyGroup() == "G0"){ ff::cout << "Executing TrafficMonitoring with parameters:" << ff::endl; if (rate != 0) { ff::cout << " * rate: " << rate << " tuples/second" << ff::endl; @@ -578,40 +578,38 @@ int main(int argc, char* argv[]) { ff::cout << " * topology: source -> map-matcher -> speed-calculator -> sink" << ff::endl; } - ff_pipeline p(false, qlen, qlen, true); - Source source(rate); - source.createGroup("Source"); - p.add_stage(&source); + ff_a2a internalA2A(false, qlen, qlen, true); - vector matchers; - for(size_t i = 0; i < matcher_par_deg; i++) - matchers.push_back(new MapMatcher(road_grid_list)); - - vector calculators; - for(size_t i = 0; i < calculator_par_deg; i++) - calculators.push_back(new SpeedCalculator); - - internalA2A.add_firstset(matchers, 0, true); // ondemand???? - internalA2A.add_secondset(calculators, true); + vector sx; + vector dx; + for(size_t i = 0; i < matcher_par_deg; i++){ + auto* sxp = new ff_pipeline(false, qlen, qlen, true); + sxp->add_stage(new Source(rate)); + sxp->add_stage(new MapMatcher(road_grid_list)); + sx.push_back(sxp); + + auto* dxp = new ff_pipeline(false, qlen, qlen, true); + dxp->add_stage(new SpeedCalculator); + dxp->add_stage(new Sink); + + dx.push_back(dxp); + internalA2A.createGroup("G"+std::to_string(i)) << sxp << dxp; + } - internalA2A.createGroup("A2A"); + internalA2A.add_firstset(sx, 0, true); // ondemand???? + internalA2A.add_secondset(dx, true); - p.add_stage(&internalA2A); - Sink sink; - sink.createGroup("Sink"); - p.add_stage(&sink); /// evaluate topology execution time volatile unsigned long start_time_main_usecs = current_time_usecs(); - p.run_and_wait_end(); + internalA2A.run_and_wait_end(); volatile unsigned long end_time_main_usecs = current_time_usecs(); double elapsed_time_seconds = (end_time_main_usecs - start_time_main_usecs) / (1000000.0); double throughput = sent_tuples / elapsed_time_seconds; ff::cout << "Actual execution time (s): " << elapsed_time_seconds << ff::endl; - if (DFF_getMyGroup() == "Source"){ - ff::cout << "Tuples sent by source: " << sent_tuples << ff::endl; - ff::cout << "Measured throughput: " << (int) throughput << " tuples/second" << ff::endl; - } + ff::cout << "Tuples sent by source: " << sent_tuples << ff::endl; + ff::cout << "Measured throughput: " << (int) throughput << " tuples/second" << ff::endl; + return 0; } \ No newline at end of file From 031103b9733e959511998afb5d6a44d19f9e3d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 4 Jul 2022 12:55:56 +0200 Subject: [PATCH 194/202] Fixed some typos on loader --- ff/dff.hpp | 11 +++++++++++ ff/distributed/loader/dff_run.cpp | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ff/dff.hpp b/ff/dff.hpp index ec7b1ae5..d6ff83aa 100644 --- a/ff/dff.hpp +++ b/ff/dff.hpp @@ -59,12 +59,23 @@ #include #endif /* FF_DFF_HPP */ + #else /* DISABLE_FF_DISTRIBUTED */ + +#if !defined(DFF_EXCLUDE_BLOCKING) +#define BLOCKING_MODE +#else +#undef BLOCKING_MODE +#endif + #include #include #include namespace ff { std::ostream& cout = std::cout; + + template + auto& endl(std::basic_ostream& os){return std::endl(os);} } static inline int DFF_Init(int& argc, char**& argv){ return 0; } #endif /* DISABLE_FF_DISTRIBUTED */ diff --git a/ff/distributed/loader/dff_run.cpp b/ff/distributed/loader/dff_run.cpp index ac9d9ec1..0e62f52b 100644 --- a/ff/distributed/loader/dff_run.cpp +++ b/ff/distributed/loader/dff_run.cpp @@ -308,7 +308,7 @@ int main(int argc, char** argv) { if (usedProtocol == Proto::MPI){ std::string rankFile = generateRankFile(parsedGroups); std::cout << "RankFile: " << rankFile << std::endl; - // invoke mpirun using the just created hostfile + // invoke mpirun using the just created rankfile char command[350]; @@ -331,7 +331,7 @@ int main(int argc, char** argv) { pclose(fp); - //std::remove(hostFile.c_str()); + std::remove(rankFile.c_str()); } From 083a3577e3ce270687cbbe0443d5514b151db815 Mon Sep 17 00:00:00 2001 From: Marco Aldinucci Date: Mon, 1 Aug 2022 14:44:28 +0200 Subject: [PATCH 195/202] Support for Apple silicon --- ff/cycle.h | 9 +++++++-- tests/Makefile | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ff/cycle.h b/ff/cycle.h index 107b8a4f..c2396a48 100644 --- a/ff/cycle.h +++ b/ff/cycle.h @@ -192,14 +192,19 @@ INLINE_ELAPSED(__inline__) #endif /* MacOS/Mach (Darwin) time-base register interface (unlike UpTime, - from Carbon, requires no additional libraries to be linked). */ -#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && !defined(HAVE_TICK_COUNTER) + from Carbon, requires no additional libraries to be linked). + 13 July 2022 (MarcoA): Reviewed for M1 Macs + */ +#if defined(__APPLE__) #include +//#if defined(_MACH_ABSOLUTE_TIME_H_) && defined(_MACH_MACH_TIME_H_) +#if !defined(HAVE_TICK_COUNTER) typedef uint64_t ticks; #define getticks mach_absolute_time INLINE_ELAPSED(__inline__) #define HAVE_TICK_COUNTER #endif +#endif /*----------------------------------------------------------------*/ /* diff --git a/tests/Makefile b/tests/Makefile index fcea60f2..795e42c5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -73,7 +73,7 @@ endif #CFLAGS = #LDFLAGS = TRACE_FASTFLOW =1 -CXXFLAGS += -std=c++17 +CXXFLAGS += -std=c++20 ifdef DEBUG OPTIMIZE_FLAGS += -g -fno-inline-functions else From 7e09a9ff4cf8e0581eb4437b5c2f793423b7c785 Mon Sep 17 00:00:00 2001 From: Marco Aldinucci Date: Fri, 12 Aug 2022 18:11:31 +0200 Subject: [PATCH 196/202] minor fixes --- tests/Makefile | 3 +++ tests/runtests.sh | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 795e42c5..c5d914b9 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -119,8 +119,11 @@ ifeq ($(ARCH),-march=x86_64) endif ifeq ($(strip $(OS)),Darwin) + CXXFLAGS += -DNO_DEFAULT_MAPPING -DBLOCKING_MODE ifeq ($(strip $(ARCH)),x86_64 ) ARCH = -march=core2 + else ifeq ($(strip $(ARCH)),arm64 ) + ARCH = -arch arm64 else ARCH = -arch ppc endif diff --git a/tests/runtests.sh b/tests/runtests.sh index ada82de7..cc99cf38 100755 --- a/tests/runtests.sh +++ b/tests/runtests.sh @@ -9,8 +9,8 @@ # # -system=`uname -mo` -arch=`uname -o` +system=`uname -m` +arch=`uname` if [ $arch = "Darwin" ]; then execfiles="$(find . -maxdepth 1 -type f -perm -a=x| grep -v runtests.sh| grep -v mytime.h)" else From 7eb4f13eea1237ea617edb3112850edf97fae2bf Mon Sep 17 00:00:00 2001 From: Marco Aldinucci Date: Fri, 12 Aug 2022 20:37:29 +0200 Subject: [PATCH 197/202] Few fixes for arm64/M1 --- ff/cycle.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ff/cycle.h b/ff/cycle.h index 107b8a4f..98ea3ccb 100644 --- a/ff/cycle.h +++ b/ff/cycle.h @@ -193,13 +193,16 @@ INLINE_ELAPSED(__inline__) /* MacOS/Mach (Darwin) time-base register interface (unlike UpTime, from Carbon, requires no additional libraries to be linked). */ -#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && !defined(HAVE_TICK_COUNTER) +#if defined(__APPLE__) +//#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && +#if !defined(HAVE_TICK_COUNTER) #include typedef uint64_t ticks; #define getticks mach_absolute_time INLINE_ELAPSED(__inline__) #define HAVE_TICK_COUNTER #endif +#endif /*----------------------------------------------------------------*/ /* From b3916c9d964c74c6285f2cca745e7f0cdd102977 Mon Sep 17 00:00:00 2001 From: Marco Aldinucci Date: Fri, 12 Aug 2022 20:43:15 +0200 Subject: [PATCH 198/202] arm64 fixes --- ff/sysdep.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ff/sysdep.h b/ff/sysdep.h index a753f5cc..6b88f43c 100644 --- a/ff/sysdep.h +++ b/ff/sysdep.h @@ -161,16 +161,17 @@ static inline int xchg(volatile int *ptr, int x) static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size) { unsigned long ret; - unsigned int tmp; + // MA: updated 12/08/22 unsigned int ==> unsignet long + unsigned long tmp; smp_mb(); - + // MA: updated 12/08/22 teq %1, #0 ==> teq %w1, #0 switch (size) { case 1: asm volatile("@ __xchg1\n" "1: ldrexb %0, [%3]\n" " strexb %1, %2, [%3]\n" - " teq %1, #0\n" + " teq %w1, #0\n" " bne 1b" : "=&r" (ret), "=&r" (tmp) : "r" (x), "r" (ptr) From 2f33c388650b3200a667511985b0d36bdb0b216d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Thu, 30 Mar 2023 15:00:12 +0200 Subject: [PATCH 199/202] Fixed compiling issue on GCC-12 and above versions. --- ff/ff.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/ff.hpp b/ff/ff.hpp index 4431f1ea..0aa5ddaa 100644 --- a/ff/ff.hpp +++ b/ff/ff.hpp @@ -21,7 +21,7 @@ #ifndef FF_FF_HPP #define FF_FF_HPP - +#include #include #include #include From cf835a6ac1be52f081d7455572f9529fa063773b Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Mon, 12 Feb 2024 15:11:59 +0100 Subject: [PATCH 200/202] update README, minor fixes for gcc-12, fix for RISC-V (thanks to Marco Danelutto), new test related to changing the queue size --- README.md | 205 +++++++++++++++-------------- ff/cycle.h | 5 +- ff/distributed/ff_dadapters.hpp | 4 +- ff/ff.hpp | 1 - ff/make_unique.hpp | 5 +- ff/ordering_policies.hpp | 17 +-- tests/test_changesize3.cpp | 223 ++++++++++++++++++++++++++++++++ 7 files changed, 347 insertions(+), 113 deletions(-) create mode 100644 tests/test_changesize3.cpp diff --git a/README.md b/README.md index 53c6f74b..4227e780 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,106 @@ -[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![GitHub tag](https://img.shields.io/github/tag/fastflow/fastflow.svg)](http://github.com/fastflow/fastflow/releases) -[![GitHub Issues](https://img.shields.io/github/issues/fastflow/fastflow.svg)](http://github.com/fastflow/fastflow/issues) - -# FastFlow: high-performance parallel patterns and building blocks in C++ - -FastFlow is a programming library implemented in modern C++ targeting -multi/many-cores and distributed systems (the distributed run-time is experimental). -It offers both a set of high-level ready-to-use parallel patterns and a set -of mechanisms and composable components (called building blocks) to support low-latency and high-throughput data-flow streaming networks. - -FastFlow simplifies the development of parallel applications modelled as a -structured directed graph of processing nodes. -The graph of concurrent nodes is constructed by the assembly of sequential -and parallel building blocks as well as higher-level parallel patterns modelling typical schemas of parallel computations (e.g., pipeline, task-farm, parallel-for, etc.). -FastFlow efficiency stems from the optimized implementation of the base communication and synchronization mechanisms and from its layered software design. - -## FastFlow's Building Blocks - -FastFlow nodes represent sequential computations executed by a dedicated thread. -A node can have zero, one or more input channels and zero, one or more output channels. -As typical is in streaming applications, communication channels are unidirectional and -asynchronous. They are implemented through Single-Producer Single-Consumer -(SPSC) FIFO queues carrying memory pointers. Operations on such queues (that can have either -bounded or unbounded capacity) are based on non-blocking lock-free synchronization protocol. -To promote power-efficiency vs responsiveness of the nodes, a blocking concurrency -control operation mode is also available. - -The semantics of sending data references over a communication channel is that of transferring -the ownership of the data pointed by the reference from the sender node (producer) to the -receiver node (consumer) according to the producer-consumer model. -The data reference is de facto a capability, i.e. a logical token that grants access to a given -data or to a portion of a larger data structure. Based on this reference-passing semantics, -the receiver is expected to have exclusive access to the data reference received from one of -the input channels, while the producer is expected not to use the reference anymore. - -The set of FastFlow building blocks is: - -**node**. This is the basic abstraction of the building blocks. It defines the unit of sequential execution in the FastFlow library. A node encapsulates either user’s code (i.e. business logic) or RTS code. User’s code can also be wrapped by a FastFlow node executing RTS code to manipulate and filter input and output data before and after the execution of the business logic code. Based on the number of input/output channels it is possible to distinguish three different kinds of sequential nodes: *standard node* with one input and one output channel, *multi-input* with many inputs and one output channel, and finally *multi-output* with one input and many outputs. -A generic node performs a loop that: i) gets a data item (through a memory reference to a data structure) from one of its input queues; ii) executes a functional code working on the data item and possibly on a state maintained by the node itself by calling its service method svc(); iii) puts a memory reference to the resulting item(s) into one or multiple output queues selected according to a predefined or user-defined policy. - -**node combiner**. It allows the user to combine two nodes into one single sequential node. Conceptually, the operation of combining sequential nodes is similar to the composition of two functions. In this case, the functions are the service functions of the two nodes (e.g., the *svc* method). This building block promotes code reuse through fusion of already implemented nodes and it can also be used to reduce the threads used to run the data-flow network by executing the functions of multiple nodes by a single thread. - -**pipeline**. The pipeline allows building blocks to be connected in a linear chain. It is used both as a container of building blocks as well as an application topology builder. At execution time, the pipeline building block models the data-flow execution of its building blocks on data elements flowing in a streamed fashion. - -**farm**. It models functional replication of building blocks coordinated by a master node called Emitter. The simplest form is composed of two computing entities executed in parallel: a multi-output master node (the *Emitter*), and a pool of pipeline building blocks called *Workers*. The Emitter node schedules the data elements received in input to the Workers using either a default policy (i.e. *round-robin* or *on-demand*) or according to the algorithm implemented by the user code defined in its service method. In this second scenario, the stream elements scheduling is controlled by the user through a custom policy. - -**All-to-All** The All-to-All (briefly **A2A**) building block defines two distinct sets of Workers connected accordig to the *shuffle communication pattern*. This means that each Worker in the first set (called *L-Worker*) is connected to all the Workers in the second set (called *R-Workers*). The user may implement any custom distribution policy in the L-Workers (e.g., sending each data item to a specific worker of the R-Worker set, broadcasting data elements, executing a *by-key* routing, etc). The default distribution policy is *round-robin*. - -A brief description of the FastFlow building block software layer can be found [here](https://docs.google.com/presentation/d/1mCJ9Bf4zo3MX2DFGG0zfbJ2URdCIJoECt87-Rkt2swc/edit?usp=sharing). - -## Available Parallel Patterns - -In FastFlow, all parallel patterns available are implemented on top of building blocks. -Parallel Patterns are parametric implementations of well-known structures suitable -for parallelism exploitation. The high-level patterns currently available in FastFlow library are: -**ff_Pipe**, **ff_Farm/ff_OFarm**, **ParallelFor/ParallelForReduce/ParallelForPipeReduce**, **poolEvolution**, -**ff_Map**, **ff_mdf**, **ff_DC**, **ff_stencilReduce**. - -Differenting from the building block layer, the parallel patterns layer is in continuous evolution. -As soon as new patterns are recognized or new smart implementations are available for the existing patterns, -they are added to the high-level layer and provided to the user. - - -## Building the library - -FastFlow is a header-only library, for the shared-memory run-time, there are basically no dependencies. -For the distributed-memory run-time, you need to install: - - Cereal for (automatic) serialization/deserialization purposes (https://uscilab.github.io/cereal/) - - OpenMPI for experimenting with the MPI communication back-end (https://www.open-mpi.org/software/ompi) - -While Cereal is mandatory, OpenMPI installation is optional and can be disabled at compile-time by compiling the -code with '-DDFF_EXCLUDE_MPI'. To compile tests with the distributed run-time you need a recent compiler -supporting the -std=c++20 standard. - -See the [BUILD.ME](BUILD.ME) file for instructions about building unit tests and examples. -NOTES: currently, the cmake-based compilation of distributed tests has been disabled. - -## Supported Platforms -FastFlow is currently actively supported for Linux with gcc >4.8, x86_64 and ARM -Since version 2.0.4, FastFlow is expected to work on any platform with a C++11 compiler. - -## FastFlow Maintainer -Massimo Torquati (University of Pisa) - - -## FastFlow History -The FastFlow project started in the beginning of 2010 by Massimo Torquati (University of Pisa) and -Marco Aldinucci (University of Turin). -Over the years several other people (mainly from the Parallel Computing Groups of the University of Pisa and Turin) contributed with ideas and code to the development of the project. FastFlow has been used as -run-time system in three EU founded research projects: ParaPhrase, REPARA and RePhrase. - -## About the License -From version 3.0.1, FastFlow is released with a dual license: LGPL-3 and MIT. - -## How to cite FastFlow -Aldinucci, M. , Danelutto, M. , Kilpatrick, P. and Torquati, M. (2017). Fastflow: High‐Level and Efficient Streaming on Multicore. In Programming multi‐core and many‐core computing systems (eds S. Pllana and F. Xhafa). -[![FF_DOI_badge](https://img.shields.io/badge/DOI-https%3A%2F%2Fdoi.org%2F10.1002%2F9781119332015.ch13-blue.svg)](https://doi.org/10.1002/9781119332015.ch13) +[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![GitHub tag](https://img.shields.io/github/tag/fastflow/fastflow.svg)](http://github.com/fastflow/fastflow/releases) +[![GitHub Issues](https://img.shields.io/github/issues/fastflow/fastflow.svg)](http://github.com/fastflow/fastflow/issues) + +# FastFlow: high-performance parallel patterns and building blocks in C++ + +FastFlow is a programming library implemented in modern C++ targeting +multi/many-cores and distributed systems (the distributed run-time is experimental). +It offers both a set of high-level ready-to-use parallel patterns and a set +of mechanisms and composable components (called building blocks) to support low-latency and high-throughput data-flow streaming networks. + +FastFlow simplifies the development of parallel applications modelled as a +structured directed graph of processing nodes. +The graph of concurrent nodes is constructed by the assembly of sequential +and parallel building blocks as well as higher-level parallel patterns modelling typical schemas of parallel computations (e.g., pipeline, task-farm, parallel-for, etc.). +FastFlow efficiency stems from the optimized implementation of the base communication and synchronization mechanisms and from its layered software design. + +## FastFlow's Building Blocks + +FastFlow nodes represent sequential computations executed by a dedicated thread. +A node can have zero, one or more input channels and zero, one or more output channels. +As typical is in streaming applications, communication channels are unidirectional and +asynchronous. They are implemented through Single-Producer Single-Consumer +(SPSC) FIFO queues carrying memory pointers. Operations on such queues (that can have either +bounded or unbounded capacity) are based on non-blocking lock-free synchronization protocol. +To promote power-efficiency vs responsiveness of the nodes, a blocking concurrency +control operation mode is also available. + +The semantics of sending data references over a communication channel is that of transferring +the ownership of the data pointed by the reference from the sender node (producer) to the +receiver node (consumer) according to the producer-consumer model. +The data reference is de facto a capability, i.e. a logical token that grants access to a given +data or to a portion of a larger data structure. Based on this reference-passing semantics, +the receiver is expected to have exclusive access to the data reference received from one of +the input channels, while the producer is expected not to use the reference anymore. + +The set of FastFlow building blocks is: + +**node**. This is the basic abstraction of the building blocks. It defines the unit of sequential execution in the FastFlow library. A node encapsulates either user’s code (i.e. business logic) or RTS code. User’s code can also be wrapped by a FastFlow node executing RTS code to manipulate and filter input and output data before and after the execution of the business logic code. Based on the number of input/output channels it is possible to distinguish three different kinds of sequential nodes: *standard node* with one input and one output channel, *multi-input* with many inputs and one output channel, and finally *multi-output* with one input and many outputs. +A generic node performs a loop that: i) gets a data item (through a memory reference to a data structure) from one of its input queues; ii) executes a functional code working on the data item and possibly on a state maintained by the node itself by calling its service method svc(); iii) puts a memory reference to the resulting item(s) into one or multiple output queues selected according to a predefined or user-defined policy. + +**node combiner**. It allows the user to combine two nodes into one single sequential node. Conceptually, the operation of combining sequential nodes is similar to the composition of two functions. In this case, the functions are the service functions of the two nodes (e.g., the *svc* method). This building block promotes code reuse through fusion of already implemented nodes and it can also be used to reduce the threads used to run the data-flow network by executing the functions of multiple nodes by a single thread. + +**pipeline**. The pipeline allows building blocks to be connected in a linear chain. It is used both as a container of building blocks as well as an application topology builder. At execution time, the pipeline building block models the data-flow execution of its building blocks on data elements flowing in a streamed fashion. + +**farm**. It models functional replication of building blocks coordinated by a master node called Emitter. The simplest form is composed of two computing entities executed in parallel: a multi-output master node (the *Emitter*), and a pool of pipeline building blocks called *Workers*. The Emitter node schedules the data elements received in input to the Workers using either a default policy (i.e. *round-robin* or *on-demand*) or according to the algorithm implemented by the user code defined in its service method. In this second scenario, the stream elements scheduling is controlled by the user through a custom policy. + +**All-to-All** The All-to-All (briefly **A2A**) building block defines two distinct sets of Workers connected accordig to the *shuffle communication pattern*. This means that each Worker in the first set (called *L-Worker*) is connected to all the Workers in the second set (called *R-Workers*). The user may implement any custom distribution policy in the L-Workers (e.g., sending each data item to a specific worker of the R-Worker set, broadcasting data elements, executing a *by-key* routing, etc). The default distribution policy is *round-robin*. + +A brief description of the FastFlow building block software layer can be found [here](https://docs.google.com/presentation/d/1mCJ9Bf4zo3MX2DFGG0zfbJ2URdCIJoECt87-Rkt2swc/edit?usp=sharing). + +## Available Parallel Patterns + +In FastFlow, all parallel patterns available are implemented on top of building blocks. +Parallel Patterns are parametric implementations of well-known structures suitable +for parallelism exploitation. The high-level patterns currently available in FastFlow library are: +**ff_Pipe**, **ff_Farm/ff_OFarm**, **ParallelFor/ParallelForReduce/ParallelForPipeReduce**, **poolEvolution**, +**ff_Map**, **ff_mdf**, **ff_DC**, **ff_stencilReduce**. + +Differenting from the building block layer, the parallel patterns layer is in continuous evolution. +As soon as new patterns are recognized or new smart implementations are available for the existing patterns, +they are added to the high-level layer and provided to the user. + + +## Building the library + +FastFlow is a header-only library, for the shared-memory run-time, there are basically no dependencies +(but remember to run the script mapping_string.sh in the ff directory!). +For the distributed-memory run-time, you need to install: + - Cereal for (automatic) serialization/deserialization purposes (https://uscilab.github.io/cereal/) + - OpenMPI for experimenting with the MPI communication back-end (https://www.open-mpi.org/software/ompi) + +While Cereal is mandatory, OpenMPI installation is optional and can be disabled at compile-time by compiling the +code with '-DDFF_EXCLUDE_MPI' (or make EXCLUDE_MPI=1). To compile the tests with the distributed run-time you need a +recent compiler supporting the -std=c++20 standard (e.g., gcc 10 or above). +In addition, by default the *shared-memory* version uses the non-blocking concurrency control mode, wherease the +*distributed version* uses the blocking mode for its run-time system. You can control the concurrency control mode +either at compile time (see the config.hpp file) or at run-time by calling the proper methods before running the application. + +See the [BUILD.ME](BUILD.ME) file for instructions about building unit tests and examples. +NOTES: currently, the cmake-based compilation of distributed tests has been disabled. + +## Supported Platforms +FastFlow is currently actively supported for Linux with gcc >4.8, x86_64 and ARM +Since version 2.0.4, FastFlow is expected to work on any platform with a C++11 compiler. + +## FastFlow Maintainer +Massimo Torquati (University of Pisa) + + +## FastFlow History +The FastFlow project started in the beginning of 2010 by Massimo Torquati (University of Pisa) and +Marco Aldinucci (University of Turin). +Over the years several other people (mainly from the Parallel Computing Groups of the University of Pisa and Turin) contributed with ideas and code to the development of the project. FastFlow has been used +as run-time system in three EU founded research projects: ParaPhrase, REPARA and RePhrase. Currently is one of the tools used in the Euro-HPC project TEXTAROSSA. + +More info about FastFlow and its parallel building blocks can be found here: +Massimo Torquati (Pisa, PhD Thesis) "Harnessing Parallelism in Multi/Many-Cores with Streams and Parallel Patterns" + +## About the License +From version 3.0.1, FastFlow is released with a dual license: LGPL-3 and MIT. + +## How to cite FastFlow +Aldinucci, M. , Danelutto, M. , Kilpatrick, P. and Torquati, M. (2017). Fastflow: High‐Level and Efficient Streaming on Multicore. In Programming multi‐core and many‐core computing systems (eds S. Pllana and F. Xhafa). +[![FF_DOI_badge](https://img.shields.io/badge/DOI-https%3A%2F%2Fdoi.org%2F10.1002%2F9781119332015.ch13-blue.svg)](https://doi.org/10.1002/9781119332015.ch13) diff --git a/ff/cycle.h b/ff/cycle.h index c2396a48..4446a760 100644 --- a/ff/cycle.h +++ b/ff/cycle.h @@ -92,7 +92,10 @@ rdcycle(void) { unsigned long dst; // output into any register, likely a0 // regular instruction: - asm volatile ("csrrs %0, 0xc00, x0" : "=r" (dst) ); + // - the following (commented) instruction doesn't work on U74 SiFive + // asm volatile ("csrrs %0, 0xc00, x0" : "=r" (dst) ); + // Marco Danelutto Jan 2024 : this works on the U74 micro arch RiscV + asm volatile {"rdtime %0" : "=r" (dst) ); // regular instruction with symbolic csr and register names // asm volatile ("csrrs %0, cycle, zero" : "=r" (dst) ); // pseudo-instruction: diff --git a/ff/distributed/ff_dadapters.hpp b/ff/distributed/ff_dadapters.hpp index 0cb1f161..abacfc91 100644 --- a/ff/distributed/ff_dadapters.hpp +++ b/ff/distributed/ff_dadapters.hpp @@ -20,9 +20,9 @@ class SquareBoxRight : public ff_minode { int svc_init() { // change the size of the queue towards the Sender - // forcing the queue to be bounded of capacity 1 + // forcing the queue to be bounded of capacity equal to inchannels (excluded squareboxleft) size_t oldsz; - change_outputqueuesize(1, oldsz); + change_outputqueuesize(this->get_num_inchannels()-1, oldsz); assert(oldsz != 0); return 0; } diff --git a/ff/ff.hpp b/ff/ff.hpp index bfba6068..39095bf3 100644 --- a/ff/ff.hpp +++ b/ff/ff.hpp @@ -23,7 +23,6 @@ #ifndef FF_FF_HPP #define FF_FF_HPP -#include #include #include #include diff --git a/ff/make_unique.hpp b/ff/make_unique.hpp index 4428ee01..ab919819 100644 --- a/ff/make_unique.hpp +++ b/ff/make_unique.hpp @@ -23,11 +23,12 @@ #define FF_MAKEUNIQUE_HPP #include + +#if __cplusplus < 201400L // to check + #include #include - -#if __cplusplus < 201400L // to check // C++11 implementation of make_unique #if (__cplusplus >= 201103L) || (defined __GXX_EXPERIMENTAL_CXX0X__) || (defined(HAS_CXX11_VARIADIC_TEMPLATES)) diff --git a/ff/ordering_policies.hpp b/ff/ordering_policies.hpp index 76bc984e..5e3d5b9b 100644 --- a/ff/ordering_policies.hpp +++ b/ff/ordering_policies.hpp @@ -138,7 +138,7 @@ struct ordered_gt: ff_gatherer { size_t MemSize; }; // Worker wrapper to be used when ordering_pair_t is added to the data elements -class OrderedWorkerWrapper: public ff_node_t { +class OrderedWorkerWrapper: public ff_node { public: OrderedWorkerWrapper(ff_node* worker, bool cleanup=false): worker(worker),cleanup(cleanup) { @@ -148,7 +148,8 @@ class OrderedWorkerWrapper: public ff_node_t { ~OrderedWorkerWrapper() { if (cleanup) delete worker; } - ordering_pair_t *svc(ordering_pair_t *in) { + void *svc(void *t) { + ordering_pair_t *in = reinterpret_cast(t); auto out=worker->svc(in->second.first); in->second.first=out; return in; @@ -160,9 +161,8 @@ class OrderedWorkerWrapper: public ff_node_t { ff_node* worker; bool cleanup; }; -// A node that removes the ordering_pair_t around the data element -template -class OrderedEmitterWrapper: public ff_node_t { +// A node that removes the ordering_pair_t around the data element +class OrderedEmitterWrapper: public ff_node { public: OrderedEmitterWrapper(ordering_pair_t*const m, const size_t size): idx(0),cnt(0),Memory(m), MemSize(size) {} @@ -171,7 +171,7 @@ class OrderedEmitterWrapper: public ff_node_t { idx=0; return 0; } - inline ordering_pair_t* svc(IN_t* in) { + inline void* svc(void *in) { Memory[idx].first=cnt; Memory[idx].second.first = in; this->ff_send_out(&Memory[idx]); @@ -185,7 +185,7 @@ class OrderedEmitterWrapper: public ff_node_t { }; // A node that removes the ordering_pair_t around the data element -class OrderedCollectorWrapper: public ff_node_t { +class OrderedCollectorWrapper: public ff_node { public: struct PairCmp { bool operator()(const ordering_pair_t* lhs, const ordering_pair_t* rhs) const { @@ -197,7 +197,8 @@ class OrderedCollectorWrapper: public ff_node_t { cnt=0; return 0; } - inline void* svc(ordering_pair_t* in) { + inline void* svc(void *t) { + ordering_pair_t *in = reinterpret_cast(t); if (cnt == in->first) { // it's the next to send out ff_send_out(in->second.first); cnt++; diff --git a/tests/test_changesize3.cpp b/tests/test_changesize3.cpp new file mode 100644 index 00000000..584be340 --- /dev/null +++ b/tests/test_changesize3.cpp @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* *************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + **************************************************************************** + * + * + */ + +/* farm( First, feedback(A2A), Last ) + * + * ____________________________________________ + * | | + * | ----------------------------------- | + * | | | | + * | | |-> Worker1 ->| | | + * --->| | | -> Worker2 -->| |--- + * | |-> Worker1 ->| | | + * First ---->| | | -> Worker2 -->| |-----> Last + * | |-> Worker1 ->| | | + * | | | -> Worker2 -->| | + * | |-> Worker1 ->| | + * | | + * ----------------------------------- + * + */ + +/* Author: Massimo Torquati + * + */ + +#include +#include +using namespace ff; + +// NOTE: (NTASKS mod NWORKER1) must be 0! +const int NTASKS = 400; +const int NWORKER1 = 4; +const int NWORKER2 = 3; + +std::atomic feedbackcounter{0}; +std::atomic stopsendingback{false}; + +struct First: ff_monode_t { + int svc_init() { + svector w; + this->get_out_nodes(w); + size_t oldsz; + for(size_t i=0;ichange_inputqueuesize(1, oldsz); + return 0; + } + + long* svc(long*) { + for(int k=0;k { + long *svc(long *in) { + return in; + } + +}; + +struct Worker1: ff_monode_t { + int svc_init() { + svector w; + this->get_out_nodes(w); + size_t oldsz; + for(size_t i=0;ichange_inputqueuesize(1, oldsz); + return 0; + } + long *svc(long *in) { + if (get_my_id() == 0) usleep(5000); + ff_send_out(in); + return GO_ON; + } +}; + +struct MultiInputHelper2: ff_minode_t { + long *svc(long *in) { return in; } +}; + +struct Worker2: ff_monode_t { + int svc_init() { + svector w; + this->get_out_nodes(w); + + assert(w.size()==1); + + size_t oldsz, _osz = w[0]->get_in_buffer()->buffersize(); + w[0]->change_inputqueuesize(1, oldsz); + std::printf("Worker2 %ld init, oldsz=%ld, (real old size=%lu), new size=%ld\n", get_my_id(), oldsz, _osz, w[0]->get_in_buffer()->buffersize()); + return 0; + } + void sendEOS() { + for(int i=0;i { + long* svc(long* in) { + std::printf("Last received something %ld\n", *in); + ++cnt; + delete in; + return GO_ON; + } + void eosnotify(ssize_t id) { + std::printf("Last received EOS from %ld\n", id); + } + + void svc_end() { + if (cnt != NWORKER2) { + std::cerr << "Error received " << cnt << " instead of " << NWORKER2 << "\n"; + exit(-1); + } + } + + long cnt=0; +}; + +int main() { + // ---- first stage (Emitter) + First first; + + // ----- building all-to-all + + const MultiInputHelper1 helper1; + const MultiInputHelper2 helper2; + const Worker1 w1; + auto comb1 = combine_nodes(helper1, w1); + auto comb2 = combine_nodes(helper1, w1); + auto comb3 = combine_nodes(helper1, w1); + auto comb4 = combine_nodes(helper1, w1); + + std::vector firstSet={ &comb1, &comb2, &comb3, &comb4 }; + + assert(firstSet.size() == NWORKER1); + + const Worker2 w2; + std::vector secondSet={ + new ff_comb(helper2,w2), + new ff_comb(helper2,w2), + new ff_comb(helper2,w2) + }; + + assert(secondSet.size() == NWORKER2); + ff_a2a a2a; + a2a.add_firstset(firstSet, 1000, false); + a2a.add_secondset(secondSet, true); + + a2a.wrap_around(); // adding feedback channels + + + // ---- last stage (Collector) + Last last; + + // ---- building the topology + ff_farm farm; + farm.add_emitter(&first); + farm.add_collector(&last); + farm.add_workers({&a2a}); + + if (farm.run_and_wait_end() <0) { + error("running pipe\n"); + return -1; + } + return 0; +} From b77131cff1b40cf83ff5f8f2628f816c6cd7a459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Tonci?= Date: Mon, 8 Apr 2024 16:47:34 +0200 Subject: [PATCH 201/202] Update cycle.h Fixed typo in rdcycle implementation for RISC-V --- ff/cycle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/cycle.h b/ff/cycle.h index 4446a760..c9e95eff 100644 --- a/ff/cycle.h +++ b/ff/cycle.h @@ -95,7 +95,7 @@ rdcycle(void) { // - the following (commented) instruction doesn't work on U74 SiFive // asm volatile ("csrrs %0, 0xc00, x0" : "=r" (dst) ); // Marco Danelutto Jan 2024 : this works on the U74 micro arch RiscV - asm volatile {"rdtime %0" : "=r" (dst) ); + asm volatile ("rdtime %0" : "=r" (dst) ); // regular instruction with symbolic csr and register names // asm volatile ("csrrs %0, cycle, zero" : "=r" (dst) ); // pseudo-instruction: From eec006fb1af5c690bdacecf889e95aa25c5c7419 Mon Sep 17 00:00:00 2001 From: massimotorquati Date: Tue, 23 Apr 2024 08:03:54 +0200 Subject: [PATCH 202/202] fixed a startup problem when adding a multi-input/output combine node to the farm emitter in a farm with feedback channels --- ff/lb.hpp | 1 + tests/Makefile | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/lb.hpp b/ff/lb.hpp index 4a730296..d96feede 100644 --- a/ff/lb.hpp +++ b/ff/lb.hpp @@ -565,6 +565,7 @@ class ff_loadbalancer: public ff_thread { return -1; } filter = f; + if (skip1pop) filter->skipfirstpop(true); return 0; } diff --git a/tests/Makefile b/tests/Makefile index c5d914b9..4bdf62f4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -72,7 +72,6 @@ endif #VERSION = #CFLAGS = #LDFLAGS = -TRACE_FASTFLOW =1 CXXFLAGS += -std=c++20 ifdef DEBUG OPTIMIZE_FLAGS += -g -fno-inline-functions