Taskflow  2.6.0
flow_builder.hpp
1 #pragma once
2 
3 #include "task.hpp"
4 
5 namespace tf {
6 
13 class FlowBuilder {
14 
15  friend class Executor;
16 
17  public:
18 
28  template <typename C>
29  std::enable_if_t<is_static_task_v<C>, Task> emplace(C&& callable);
30 
40  template <typename C>
41  std::enable_if_t<is_dynamic_task_v<C>, Task> emplace(C&& callable);
42 
52  template <typename C>
53  std::enable_if_t<is_condition_task_v<C>, Task> emplace(C&& callable);
54 
55 #ifdef TF_ENABLE_CUDA
56 
65  template <typename C>
66  std::enable_if_t<is_cudaflow_task_v<C>, Task> emplace(C&& callable);
67 #endif
68 
78  template <typename... C, std::enable_if_t<(sizeof...(C)>1), void>* = nullptr>
79  auto emplace(C&&... callables);
80 
87  Task composed_of(Taskflow& taskflow);
88 
94  Task placeholder();
95 
102  void precede(Task A, Task B);
103 
109  void linearize(std::vector<Task>& tasks);
110 
117 
124  void broadcast(Task A, std::vector<Task>& others);
125 
133 
140  void succeed(std::vector<Task>& others, Task A);
141 
148  void succeed(std::initializer_list<Task> others, Task A);
149 
150  // ------------------------------------------------------------------------
151  // parallel iterations
152  // ------------------------------------------------------------------------
153 
181  template <typename B, typename E, typename C>
182  Task for_each(B&& first, E&& last, C&& callable);
183 
205  template <typename B, typename E, typename C, typename H = size_t>
206  Task for_each_guided(B&& beg, E&& end, C&& callable, H&& chunk_size = 1);
207 
229  template <typename B, typename E, typename C, typename H = size_t>
230  Task for_each_dynamic(B&& beg, E&& end, C&& callable, H&& chunk_size = 1);
231 
253  template <typename B, typename E, typename C, typename H = size_t>
255  B&& beg, E&& end, C&& callable, H&& chunk_size = 0
256  );
257 
294  template <typename B, typename E, typename S, typename C>
295  Task for_each_index(B&& first, E&& last, S&& step, C&& callable);
296 
320  template <typename B, typename E, typename S, typename C, typename H = size_t>
322  B&& beg, E&& end, S&& step, C&& callable, H&& chunk_size = 1
323  );
324 
348  template <typename B, typename E, typename S, typename C, typename H = size_t>
350  B&& beg, E&& end, S&& step, C&& callable, H&& chunk_size = 1
351  );
352 
376  template <typename B, typename E, typename S, typename C, typename H = size_t>
378  B&& beg, E&& end, S&& step, C&& callable, H&& chunk_size = 0
379  );
380 
381  // ------------------------------------------------------------------------
382  // reduction
383  // ------------------------------------------------------------------------
384 
412  template <typename B, typename E, typename T, typename O>
413  Task reduce(B&& first, E&& last, T& init, O&& bop);
414 
436  template <typename B, typename E, typename T, typename O, typename H = size_t>
438  B&& first, E&& last, T& init, O&& bop, H&& chunk_size = 1
439  );
440 
462  template <typename B, typename E, typename T, typename O, typename H = size_t>
464  B&& first, E&& last, T& init, O&& bop, H&& chunk_size = 1
465  );
466 
488  template <typename B, typename E, typename T, typename O, typename H = size_t>
490  B&& first, E&& last, T& init, O&& bop, H&& chunk_size = 0
491  );
492 
493  // ------------------------------------------------------------------------
494  // transfrom and reduction
495  // ------------------------------------------------------------------------
496 
526  template <typename B, typename E, typename T, typename BOP, typename UOP>
527  Task transform_reduce(B&& first, E&& last, T& init, BOP&& bop, UOP&& uop);
528 
552  template <typename B, typename E, typename T, typename BOP, typename UOP, typename H = size_t>
554  B&& first, E&& last, T& init, BOP&& bop, UOP&& uop, H&& chunk_size = 1
555  );
556 
580  template <typename B, typename E, typename T, typename BOP, typename UOP, typename H = size_t>
582  B&& first, E&& last, T& init, BOP&& bop, UOP&& uop, H&& chunk_size = 0
583  );
584 
608  template <typename B, typename E, typename T, typename BOP, typename UOP, typename H = size_t>
610  B&& first, E&& last, T& init, BOP&& bop, UOP&& uop, H&& chunk_size = 1
611  );
612 
613 
614  protected:
615 
619  FlowBuilder(Graph& graph);
620 
624  Graph& _graph;
625 
626  private:
627 
628  template <typename L>
629  void _linearize(L&);
630 };
631 
632 // Constructor
633 inline FlowBuilder::FlowBuilder(Graph& graph) :
634  _graph {graph} {
635 }
636 
637 // Function: emplace
638 template <typename... C, std::enable_if_t<(sizeof...(C)>1), void>*>
639 auto FlowBuilder::emplace(C&&... cs) {
640  return std::make_tuple(emplace(std::forward<C>(cs))...);
641 }
642 
643 // Function: emplace
644 // emplaces a static task
645 template <typename C>
646 std::enable_if_t<is_static_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
647  auto n = _graph.emplace_back(
648  nstd::in_place_type_t<Node::StaticWork>{}, std::forward<C>(c)
649  );
650  return Task(n);
651 }
652 
653 // Function: emplace
654 // emplaces a dynamic task
655 template <typename C>
656 std::enable_if_t<is_dynamic_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
657  auto n = _graph.emplace_back(
658  nstd::in_place_type_t<Node::DynamicWork>{}, std::forward<C>(c)
659  );
660  return Task(n);
661 }
662 
663 // Function: emplace
664 // emplaces a condition task
665 template <typename C>
666 std::enable_if_t<is_condition_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
667  auto n = _graph.emplace_back(
668  nstd::in_place_type_t<Node::ConditionWork>{}, std::forward<C>(c)
669  );
670  return Task(n);
671 }
672 
673 #ifdef TF_ENABLE_CUDA
674 // Function: emplace
675 // emplaces a cudaflow task
676 template <typename C>
677 std::enable_if_t<is_cudaflow_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
678  auto n = _graph.emplace_back(
679  nstd::in_place_type_t<Node::cudaFlowWork>{}, std::forward<C>(c)
680  );
681  return Task(n);
682 }
683 #endif
684 
685 // Function: composed_of
687  auto node = _graph.emplace_back(
688  nstd::in_place_type_t<Node::ModuleWork>{}, &taskflow
689  );
690  return Task(node);
691 }
692 
693 // Procedure: precede
694 inline void FlowBuilder::precede(Task from, Task to) {
695  from._node->_precede(to._node);
696 }
697 
698 // Procedure: broadcast
700  for(auto to : tos) {
701  from.precede(to);
702  }
703 }
704 
705 // Procedure: broadcast
707  for(auto to : tos) {
708  from.precede(to);
709  }
710 }
711 
712 // Function: succeed
713 inline void FlowBuilder::succeed(std::vector<Task>& froms, Task to) {
714  for(auto from : froms) {
715  to.succeed(from);
716  }
717 }
718 
719 // Function: succeed
721  for(auto from : froms) {
722  to.succeed(from);
723  }
724 }
725 
726 // Function: placeholder
728  auto node = _graph.emplace_back();
729  return Task(node);
730 }
731 
732 /*// Function: for_each
733 template <typename I, typename C>
734 std::pair<Task, Task> FlowBuilder::for_each(
735  I beg, I end, C&& c, size_t chunk
736 ){
737 
738  //using category = typename std::iterator_traits<I>::iterator_category;
739 
740  auto S = placeholder();
741  auto T = placeholder();
742 
743  // default partition equals to the worker count
744  if(chunk == 0) {
745  chunk = 1;
746  }
747 
748  size_t remain = std::distance(beg, end);
749 
750  while(beg != end) {
751 
752  auto e = beg;
753 
754  auto x = std::min(remain, chunk);
755  std::advance(e, x);
756  remain -= x;
757 
758  // Create a task
759  auto task = emplace([beg, e, c] () mutable {
760  std::for_each(beg, e, c);
761  });
762 
763  S.precede(task);
764  task.precede(T);
765 
766  // adjust the pointer
767  beg = e;
768  }
769 
770  // special case
771  if(S.num_successors() == 0) {
772  S.precede(T);
773  }
774 
775  return std::make_pair(S, T);
776 }
777 
778 // Function: for_each
779 template <
780  typename I,
781  typename C,
782  std::enable_if_t<std::is_integral<std::decay_t<I>>::value, void>*
783 >
784 std::pair<Task, Task> FlowBuilder::for_each(I beg, I end, I s, C&& c, size_t chunk) {
785 
786  if((s == 0) || (beg < end && s <= 0) || (beg > end && s >=0) ) {
787  TF_THROW("invalid range [", beg, ", ", end, ") with step size ", s);
788  }
789 
790  // source and target
791  auto source = placeholder();
792  auto target = placeholder();
793 
794  if(chunk == 0) {
795  chunk = 1;
796  }
797 
798  // positive case
799  if(beg < end) {
800  while(beg != end) {
801  auto o = static_cast<I>(chunk) * s;
802  auto e = std::min(beg + o, end);
803  auto task = emplace([=] () mutable {
804  for(auto i=beg; i<e; i+=s) {
805  c(i);
806  }
807  });
808  source.precede(task);
809  task.precede(target);
810  beg = e;
811  }
812  }
813  // negative case
814  else if(beg > end) {
815  while(beg != end) {
816  auto o = static_cast<I>(chunk) * s;
817  auto e = std::max(beg + o, end);
818  auto task = emplace([=] () mutable {
819  for(auto i=beg; i>e; i+=s) {
820  c(i);
821  }
822  });
823  source.precede(task);
824  task.precede(target);
825  beg = e;
826  }
827  }
828 
829  if(source.num_successors() == 0) {
830  source.precede(target);
831  }
832 
833  return std::make_pair(source, target);
834 }
835 
836 // Function: for_each
837 template <typename I, typename C,
838  std::enable_if_t<std::is_floating_point<std::decay_t<I>>::value, void>*
839 >
840 std::pair<Task, Task> FlowBuilder::for_each(I beg, I end, I s, C&& c, size_t chunk) {
841 
842  if((s == 0) || (beg < end && s <= 0) || (beg > end && s >=0) ) {
843  TF_THROW("invalid range [", beg, ", ", end, ") with step size ", s);
844  }
845 
846  // source and target
847  auto source = placeholder();
848  auto target = placeholder();
849 
850  if(chunk == 0) {
851  chunk = 1;
852  }
853 
854  // positive case
855  if(beg < end) {
856  size_t N=0;
857  I b = beg;
858  for(I e=beg; e<end; e+=s) {
859  if(++N == chunk) {
860  auto task = emplace([=] () mutable {
861  for(size_t i=0; i<N; ++i, b+=s) {
862  c(b);
863  }
864  });
865  source.precede(task);
866  task.precede(target);
867  N = 0;
868  b = e;
869  }
870  }
871 
872  if(N) {
873  auto task = emplace([=] () mutable {
874  for(size_t i=0; i<N; ++i, b+=s) {
875  c(b);
876  }
877  });
878  source.precede(task);
879  task.precede(target);
880  }
881  }
882  else if(beg > end) {
883  size_t N=0;
884  I b = beg;
885  for(I e=beg; e>end; e+=s) {
886  if(++N == chunk) {
887  auto task = emplace([=] () mutable {
888  for(size_t i=0; i<N; ++i, b+=s) {
889  c(b);
890  }
891  });
892  source.precede(task);
893  task.precede(target);
894  N = 0;
895  b = e;
896  }
897  }
898 
899  if(N) {
900  auto task = emplace([=] () mutable {
901  for(size_t i=0; i<N; ++i, b+=s) {
902  c(b);
903  }
904  });
905  source.precede(task);
906  task.precede(target);
907  }
908  }
909 
910  if(source.num_successors() == 0) {
911  source.precede(target);
912  }
913 
914  return std::make_pair(source, target);
915 } */
916 
917 // Procedure: _linearize
918 template <typename L>
919 void FlowBuilder::_linearize(L& keys) {
920 
921  auto itr = keys.begin();
922  auto end = keys.end();
923 
924  if(itr == end) {
925  return;
926  }
927 
928  auto nxt = itr;
929 
930  for(++nxt; nxt != end; ++nxt, ++itr) {
931  itr->_node->_precede(nxt->_node);
932  }
933 }
934 
935 // Procedure: linearize
937  _linearize(keys);
938 }
939 
940 // Procedure: linearize
942  _linearize(keys);
943 }
944 
945 // ----------------------------------------------------------------------------
946 
956 class Subflow : public FlowBuilder {
957 
958  friend class Executor;
959  friend class FlowBuilder;
960 
961  public:
962 
969  void join();
970 
977  void detach();
978 
984  bool joinable() const;
985 
986  private:
987 
988  Subflow(Executor&, Node*, Graph&);
989 
990  Executor& _executor;
991  Node* _parent;
992 
993  bool _joinable {true};
994 };
995 
996 // Constructor
997 inline Subflow::Subflow(Executor& executor, Node* parent, Graph& graph) :
998  FlowBuilder {graph},
999  _executor {executor},
1000  _parent {parent} {
1001 }
1002 
1003 // Function: joined
1004 inline bool Subflow::joinable() const {
1005  return _joinable;
1006 }
1007 
1008 // ----------------------------------------------------------------------------
1009 // Legacy code
1010 // ----------------------------------------------------------------------------
1011 
1012 using SubflowBuilder = Subflow;
1013 
1014 } // end of namespace tf. ---------------------------------------------------
1015 
1016 
void linearize(std::vector< Task > &tasks)
adds adjacent dependency links to a linear list of tasks
Definition: flow_builder.hpp:936
bool joinable() const
queries if the subflow is joinable
Definition: flow_builder.hpp:1004
Task for_each_index_guided(B &&beg, E &&end, S &&step, C &&callable, H &&chunk_size=1)
constructs an index-based parallel-for task using the guided partition algorithm. ...
Task transform_reduce_guided(B &&first, E &&last, T &init, BOP &&bop, UOP &&uop, H &&chunk_size=1)
constructs a STL-styled parallel transform-reduce task using the guided partition algorithm ...
Task transform_reduce_static(B &&first, E &&last, T &init, BOP &&bop, UOP &&uop, H &&chunk_size=0)
constructs a STL-styled parallel transform-reduce task using the static partition algorithm ...
void broadcast(Task A, std::vector< Task > &others)
adds dependency links from one task A to many tasks
Definition: flow_builder.hpp:699
Task for_each_guided(B &&beg, E &&end, C &&callable, H &&chunk_size=1)
constructs a STL-styled parallel-for task using the guided partition algorithm
Task transform_reduce_dynamic(B &&first, E &&last, T &init, BOP &&bop, UOP &&uop, H &&chunk_size=1)
constructs a STL-styled parallel transform-reduce task using the dynamic partition algorithm ...
Definition: error.hpp:9
Task for_each_static(B &&beg, E &&end, C &&callable, H &&chunk_size=0)
constructs a STL-styled parallel-for task using the dynamic partition algorithm
Graph & _graph
associated graph object
Definition: flow_builder.hpp:624
void succeed(std::vector< Task > &others, Task A)
adds dependency links from many tasks to one task A
Definition: flow_builder.hpp:713
Task placeholder()
creates an empty task
Definition: flow_builder.hpp:727
void detach()
enables the subflow to detach from its parent task
Definition: executor.hpp:1276
Task reduce_dynamic(B &&first, E &&last, T &init, O &&bop, H &&chunk_size=1)
constructs a STL-styled parallel-reduce task using the dynamic partition algorithm ...
Task reduce_static(B &&first, E &&last, T &init, O &&bop, H &&chunk_size=0)
constructs a STL-styled parallel-reduce task using the static partition algorithm ...
Task for_each_index_static(B &&beg, E &&end, S &&step, C &&callable, H &&chunk_size=0)
constructs an index-based parallel-for task using the static partition algorithm. ...
Task for_each_index(B &&first, E &&last, S &&step, C &&callable)
constructs an index-based parallel-for task
Task & succeed(Ts &&... tasks)
adds precedence links from other tasks to this
Definition: task.hpp:362
std::enable_if_t< is_static_task_v< C >, Task > emplace(C &&callable)
creates a static task from a given callable object
Definition: flow_builder.hpp:646
Task composed_of(Taskflow &taskflow)
creates a module task from a taskflow
Definition: flow_builder.hpp:686
Task reduce(B &&first, E &&last, T &init, O &&bop)
constructs a STL-styled parallel-reduce task
void precede(Task A, Task B)
adds a dependency link from task A to task B
Definition: flow_builder.hpp:694
main entry to create a task dependency graph
Definition: core/taskflow.hpp:18
Task for_each_dynamic(B &&beg, E &&end, C &&callable, H &&chunk_size=1)
constructs a STL-styled parallel-for task using the dynamic partition algorithm
FlowBuilder(Graph &graph)
constructs a flow builder with a graph
Definition: flow_builder.hpp:633
Task for_each(B &&first, E &&last, C &&callable)
constructs a STL-styled parallel-for task
building methods of a task dependency graph
Definition: flow_builder.hpp:13
handle to a node in a task dependency graph
Definition: task.hpp:113
Task for_each_index_dynamic(B &&beg, E &&end, S &&step, C &&callable, H &&chunk_size=1)
constructs an index-based parallel-for task using the dynamic partition algorithm.
Task transform_reduce(B &&first, E &&last, T &init, BOP &&bop, UOP &&uop)
constructs a STL-styled parallel transform-reduce task
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition: task.hpp:339
execution interface for running a taskflow graph
Definition: executor.hpp:24
building methods of a subflow graph in dynamic tasking
Definition: flow_builder.hpp:956
void join()
enables the subflow to join its parent task
Definition: executor.hpp:1266
Task reduce_guided(B &&first, E &&last, T &init, O &&bop, H &&chunk_size=1)
constructs a STL-styled parallel-reduce task using the guided partition algorithm ...