/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /*! * \link * \file mdf.hpp * \ingroup high_level_patterns * * \brief This file implements the macro dataflow pattern. */ #ifndef FF_MDF_HPP #define FF_MDF_HPP /* *************************************************************************** * * 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 (October 2013) * * * Acknowledgement: * This implementation is a refinement of the first implementation developed * at the Computer Science Department of University of Pisa in the early 2013 * together with: * - Daniele Buono (d.buono@di.unipi.it) * - Tiziano De Matteis (dematteis@di.unipi.it) * - Gabriele Mencagli (mencagli@di.unipi.it) * */ //VS12 //#ifndef WIN32 #include #include #include #include #include #include namespace ff { /** * \class ff_mdf * \ingroup high_level_patterns * * \brief Macro Data Flow executor */ class ff_mdf:public ff_pipeline { public: enum {DEFAULT_OUTSTANDING_TASKS = 2048}; protected: /* -------------- graph descriptor ---------------------- */ struct base_gd: public ff_node { 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 int wait_freezing() { return 0; }; }; template class GD: public base_gd { public: GD(void(*F)(T*const), T*const args): active(false),F(F),args(args),ntasks(0),maxMsgs(DEFAULT_OUTSTANDING_TASKS),TASKS(maxMsgs) {} void setMaxTasks(size_t maxtasks) { maxMsgs = maxtasks; TASKS.resize(maxMsgs); } void activate(bool a) { active=a;} void thaw(bool freeze=false,ssize_t=-1) { ff_node::thaw(freeze); }; int wait_freezing() { return ff_node::wait_freezing(); }; int wait() { return ff_node::wait(); } inline void alloc_and_send(std::vector &P, base_f_t *wtask) { task_f_t *task = &(TASKS[ntasks++ % maxMsgs]); task->P = P; task->wtask = wtask; while(!ff_send_out(task, -1, 1)) ff_relax(1); } void *svc(void *) { if (!active) return EOS; F(args); std::vector useless; alloc_and_send(useless, nullptr); // END task return EOS; } protected: bool active; void(*F)(T*const); // user's function T*const args; // F's arguments unsigned long ntasks, maxMsgs; std::vector TASKS; // FIX: svector should be used here }; /* -------------- scheduler ----------------------------- */ template class Scheduler: public TaskFScheduler { using baseSched = TaskFScheduler; using baseSched::lb; using baseSched::insertTask; using baseSched::schedule_task; using baseSched::handleCompletedTask; enum { RELAX_MIN_BACKOFF=1, RELAX_MAX_BACKOFF=32}; public: Scheduler(ff_loadbalancer* lb, const int maxnw, void (*schedRelaxF)(unsigned long)): TaskFScheduler(lb,maxnw), task_numb(0),task_completed(0),bk_count(0),schedRelaxF(schedRelaxF), gd_ended(false) { } virtual ~Scheduler() {} int svc_init() { if (baseSched::svc_init()<0) return -1; ff_node::input_active(true); task_numb = task_completed = 0, bk_count = 0; m=0; gd_ended = false; return 0; } task_f_t* svc(task_f_t* task) { if (!task) { if (!gd_ended && (task_numb-task_completed)<(unsigned long)baseSched::LOWER_TH) ff_node::input_active(true); // start receiveing from input channel again else if (schedRelaxF) schedRelaxF(++bk_count); return baseSched::GO_ON; } bk_count = 0; if (baseSched::fromInput()) { task_f_t *const msg = task; if (msg->wtask == nullptr) { gd_ended = true; ff_node::input_active(false); // we don't want to read FF_EOS return ((task_numb!=task_completed) ? baseSched::GO_ON: baseSched::EOS); } ++task_numb; insertTask(msg); schedule_task(0); if ((task_numb-task_completed)>(unsigned long)baseSched::LOWER_TH) { ff_node::input_active(false); // stop receiving from input channel } return baseSched::GO_ON; } hash_task_t * t = (hash_task_t *)task; ++task_completed; handleCompletedTask(t,lb->get_channel_id()); schedule_task(1); // try once more if(task_numb==task_completed && gd_ended) return baseSched::EOS; return baseSched::GO_ON; } void eosnotify(ssize_t /*id*/=-1) { lb->broadcast_task(FF_EOS); } int wait_freezing() { return lb->wait_lb_freezing(); } private: size_t task_numb, task_completed, bk_count,m; void (*schedRelaxF)(unsigned long); bool gd_ended; }; inline void reset() { gd->reset(); farm->reset(); sched->reset(); } /* -------------- worker ------------------------------- */ struct TaskFWorker: ff_node_t { inline hash_task_t *svc(hash_task_t *task) { task->wtask->call(); return task; } }; /// task function template struct ff_mdf_f_t: public base_f_t { ff_mdf_f_t(const F_t F, Param&... a):F(F) { args = std::make_tuple(a...);} inline void call() { ffapply(F, args); } F_t F; std::tuple args; }; public: /** * \brief Constructor * * \param F = is the user's function * \param args = is the argument of the function F * \param maxnw = is the maximum number of farm's workers that can be used * \param schedRelaxF = is a function for managing busy-waiting in the farm scheduler */ template ff_mdf(void (*F)(T1*const), T1*const args, size_t outstandingTasks=DEFAULT_OUTSTANDING_TASKS, int maxnw=ff_realNumCores(), void (*schedRelaxF)(unsigned long)=NULL): ff_pipeline(false,outstandingTasks), farmworkers(maxnw) { //NOTE: pipe has fixed size queue by default GD *_gd = new GD(F,args); _gd->setMaxTasks(outstandingTasks+16); // NOTE: TASKS must be greater than pipe's queue! farm = new ff_farm(false,640*maxnw,1024*maxnw,true,maxnw,true); std::vector w; // NOTE: Worker objects are going to be destroyed by the farm destructor for(int i=0;iadd_workers(w); farm->add_emitter(sched = new Scheduler(farm->getlb(), maxnw, schedRelaxF)); farm->wrap_around(); ff_pipeline::add_stage(_gd); ff_pipeline::add_stage(farm); if (ff_pipeline::run_then_freeze()<0) { error("ff_mdf: running pipeline\n"); } else { ff_pipeline::wait_freezing(); _gd->activate(true); gd = _gd; reset(); } } virtual ~ff_mdf() { if (gd) delete gd; if (sched) delete sched; if (farm) delete farm; } template inline void AddTask(std::vector &P, const F_t F, Param... args) { ff_mdf_f_t *wtask = new ff_mdf_f_t(F, args...); gd->alloc_and_send(P,wtask); } void setNumWorkers(ssize_t nw) { if (nw > ff_numCores()) // TODO: use the mapper to get the number of cores error("ff_mdf: setNumWorkers: too much workers, setting num worker to %d\n", ff_numCores()); farmworkers=(std::min)(ff_numCores(),nw); } void setThreshold(size_t /*th*/=0) {} // FIX: // FIX: TODO void *svc(void*) { // FIX: (IDEA) hashing tables received as input. return NULL; } virtual inline int run_and_wait_end() { ff_pipeline::thaw(true, farmworkers); if (ff_pipeline::wait_freezing() <0) return -1; return ff_pipeline::wait(); } virtual inline int run_then_freeze(ssize_t nw=-1) { if (nw>0) setNumWorkers(nw); ff_pipeline::thaw(true, farmworkers); return ff_pipeline::wait_freezing(); } double ffTime() { return ff_pipeline::ffTime(); } double ffwTime() { return ff_pipeline::ffwTime(); } protected: int farmworkers; // n. of workers in the farm base_gd *gd; // first stage ff_farm *farm; // second stage ff_node *sched; // farm's scheduler }; } // namespace //#endif //VS12 #endif /* FF_MDF_HPP */