47 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>* =
nullptr>
75 template <
typename I,
typename C>
98 std::enable_if_t<std::is_arithmetic_v<I>,
void>* =
nullptr 101 I beg, I end, I step, C&& callable,
size_t chunk = 1
121 template <
typename I,
typename T,
typename B>
139 template <
typename I,
typename T>
157 template <
typename I,
typename T>
181 template <
typename I,
typename T,
typename B,
typename U>
208 template <
typename I,
typename T,
typename B,
typename P,
typename U>
210 I beg, I end, T& result, B&& bop1, P&& bop2, U&& uop
278 template <
typename L>
301 template <
typename... Args>
317 bool detached()
const;
326 bool _detached {
false};
330 template <
typename... Args>
360 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>*>
362 return std::make_tuple(
emplace(std::forward<C>(cs))...);
366 template <
typename C>
370 if constexpr(std::is_invocable_v<C, Subflow&>) {
371 auto n = _graph.emplace_back(std::in_place_type_t<Node::DynamicWork>{},
372 [c=std::forward<C>(c)] (
Subflow& fb)
mutable {
374 if(fb._graph.empty()) {
381 else if constexpr(std::is_same_v<
typename function_traits<C>::return_type,
int>) {
382 auto n = _graph.emplace_back(
383 std::in_place_type_t<Node::ConditionWork>{}, std::forward<C>(c)
388 else if constexpr(std::is_same_v<
typename function_traits<C>::return_type,
void>) {
389 auto n = _graph.emplace_back(
390 std::in_place_type_t<Node::StaticWork>{}, std::forward<C>(c)
395 else if constexpr(std::is_same_v<C, std::monostate>) {
396 auto n = _graph.emplace_back();
400 static_assert(dependent_false_v<C>,
"invalid task work type");
406 auto node = _graph.emplace_back();
407 node->_module = &taskflow;
413 from._node->_precede(to._node);
432 for(
auto from : froms) {
439 for(
auto from : froms) {
446 auto node = _graph.emplace_back();
451 template <
typename I,
typename C>
453 I beg, I end, C&& c,
size_t chunk
472 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
473 size_t x = std::distance(beg, end);
474 std::advance(e, std::min(x, chunk));
478 for(
size_t i=0; i<chunk && e != end; ++e, ++i);
482 auto task =
emplace([beg, e, c] ()
mutable {
483 std::for_each(beg, e, c);
494 if(S.num_successors() == 0) {
498 return std::make_pair(S, T);
555 std::enable_if_t<std::is_arithmetic_v<I>,
void>*
559 using T = std::decay_t<I>;
561 if((s == 0) || (beg < end && s <= 0) || (beg > end && s >=0) ) {
562 TF_THROW(Error::TASKFLOW,
563 "invalid range [", beg,
", ", end,
") with step size ", s
576 if constexpr(std::is_integral_v<T>) {
580 auto o =
static_cast<T
>(chunk) * s;
581 auto e = std::min(beg + o, end);
582 auto task =
emplace([=] ()
mutable {
583 for(
auto i=beg; i<e; i+=s) {
595 auto o =
static_cast<T
>(chunk) * s;
596 auto e = std::max(beg + o, end);
597 auto task =
emplace([=] ()
mutable {
598 for(
auto i=beg; i>e; i+=s) {
609 else if constexpr(std::is_floating_point_v<T>) {
615 for(I e=beg; e<end; e+=s) {
617 auto task =
emplace([=] ()
mutable {
618 for(
size_t i=0; i<N; ++i, b+=s) {
630 auto task =
emplace([=] ()
mutable {
631 for(
size_t i=0; i<N; ++i, b+=s) {
642 for(I e=beg; e>end; e+=s) {
644 auto task =
emplace([=] ()
mutable {
645 for(
size_t i=0; i<N; ++i, b+=s) {
657 auto task =
emplace([=] ()
mutable {
658 for(
size_t i=0; i<N; ++i, b+=s) {
684 if(source.num_successors() == 0) {
688 return std::make_pair(source, target);
693 template <
typename I,
typename T>
695 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
696 return std::min(l, r);
702 template <
typename I,
typename T>
704 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
705 return std::max(l, r);
710 template <
typename I,
typename T,
typename B,
typename U>
712 I beg, I end, T& result, B&& bop, U&& uop
718 size_t d = std::distance(beg, end);
720 size_t g = std::max((d + w - 1) / w,
size_t{2});
726 auto g_results = std::make_unique<T[]>(w);
734 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
735 size_t r = std::distance(beg, end);
736 std::advance(e, std::min(r, g));
740 for(
size_t i=0; i<g && e != end; ++e, ++i);
744 auto task =
emplace([beg, e, bop, uop, res=&(g_results[
id])] ()
mutable {
746 for(++beg; beg != e; ++beg) {
747 *res = bop(std::move(*res), uop(*beg));
760 target.
work([&result, bop, res=MoC{std::move(g_results)}, w=id] () {
761 for(
auto i=0u; i<w; i++) {
762 result = bop(std::move(result), res.object[i]);
766 return std::make_pair(source, target);
770 template <
typename I,
typename T,
typename B,
typename P,
typename U>
772 I beg, I end, T& result, B&& bop, P&& pop, U&& uop
778 size_t d = std::distance(beg, end);
780 size_t g = std::max((d + w - 1) / w,
size_t{2});
785 auto g_results = std::make_unique<T[]>(w);
793 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
794 size_t r = std::distance(beg, end);
795 std::advance(e, std::min(r, g));
799 for(
size_t i=0; i<g && e != end; ++e, ++i);
803 auto task =
emplace([beg, e, uop, pop, res= &g_results[
id]] ()
mutable {
805 for(++beg; beg != e; ++beg) {
806 *res = pop(std::move(*res), *beg);
826 target.
work([&result, bop, g_results=MoC{std::move(g_results)}, w=id] () {
827 for(
auto i=0u; i<w; i++) {
828 result = bop(std::move(result), std::move(g_results.object[i]));
837 return std::make_pair(source, target);
869 template <
typename L>
870 void FlowBuilder::_linearize(L& keys) {
872 auto itr = keys.begin();
873 auto end = keys.end();
881 for(++nxt; nxt != end; ++nxt, ++itr) {
882 itr->_node->_precede(nxt->_node);
897 template <
typename I,
typename T,
typename B>
902 size_t d = std::distance(beg, end);
904 size_t g = std::max((d + w - 1) / w,
size_t{2});
910 auto g_results = std::make_unique<T[]>(w);
919 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
920 size_t r = std::distance(beg, end);
921 std::advance(e, std::min(r, g));
925 for(
size_t i=0; i<g && e != end; ++e, ++i);
930 auto task =
emplace([beg, e, op, res = &g_results[
id]] ()
mutable {
932 for(++beg; beg != e; ++beg) {
933 *res = op(std::move(*res), *beg);
956 target.
work([g_results=MoC{std::move(g_results)}, &result, op, w=id] () {
957 for(
auto i=0u; i<w; i++) {
958 result = op(std::move(result), g_results.object[i]);
962 return std::make_pair(source, target);
970 template <
typename C>
974 TF_THROW(Error::TASKFLOW,
"can't assign work to a module task");
978 if constexpr(std::is_same_v<
typename function_traits<C>::return_type,
void>) {
979 _node->_work.emplace<Node::StaticWork>(std::forward<C>(c));
982 else if constexpr(std::is_same_v<
typename function_traits<C>::return_type,
int>) {
983 _node->_work.emplace<Node::ConditionWork>(std::forward<C>(c));
986 else if constexpr(std::is_invocable_v<C, Subflow&>) {
987 _node->_work.emplace<Node::DynamicWork>(
988 [c=std::forward<C>(c)] (
Subflow& fb)
mutable {
990 if(fb._graph.empty()) {
996 else if constexpr(std::is_same_v<C, std::monostate>) {
997 _node->_work.emplace<std::monostate>();
1000 static_assert(dependent_false_v<C>,
"invalid task work type");
void linearize(std::vector< Task > &tasks)
adds adjacent dependency links to a linear list of tasks
Definition: flow_builder.hpp:887
Task emplace(C &&callable)
creates a task from a given callable object
Definition: flow_builder.hpp:367
void broadcast(Task A, std::vector< Task > &others)
adds dependency links from one task A to many tasks
Definition: flow_builder.hpp:417
std::pair< Task, Task > transform_reduce(I beg, I end, T &result, B &&bop, U &&uop)
constructs a task dependency graph of parallel transformation and reduction
Definition: flow_builder.hpp:711
Definition: taskflow.hpp:5
T hardware_concurrency(T... args)
Task placeholder()
creates an empty task
Definition: flow_builder.hpp:445
Subflow(Args &&... args)
constructs a subflow builder object
Definition: flow_builder.hpp:331
void detach()
enables the subflow to detach from its parent task
Definition: flow_builder.hpp:341
bool detached() const
queries if the subflow will be detached from its parent task
Definition: flow_builder.hpp:346
std::pair< Task, Task > reduce_max(I beg, I end, T &result)
constructs a task dependency graph of parallel reduction through std::max
Definition: flow_builder.hpp:703
Task & succeed(Ts &&... tasks)
adds precedence links from other tasks to this
Definition: task.hpp:197
std::pair< Task, Task > parallel_for(I beg, I end, C &&callable, size_t chunk=1)
constructs a task dependency graph of range-based parallel_for
Definition: flow_builder.hpp:452
Task composed_of(Taskflow &taskflow)
creates a module task from a taskflow
Definition: flow_builder.hpp:405
void precede(Task A, Task B)
adds a dependency link from task A to task B
Definition: flow_builder.hpp:412
the class to create a task dependency graph
Definition: core/taskflow.hpp:18
void gather(std::vector< Task > &others, Task A)
adds dependency links from many tasks to one task A
Definition: flow_builder.hpp:431
FlowBuilder(Graph &graph)
construct a flow builder object
Definition: flow_builder.hpp:283
Building blocks of a task dependency graph.
Definition: flow_builder.hpp:13
bool joined() const
queries if the subflow will join its parent task
Definition: flow_builder.hpp:351
task handle to a node in a task dependency graph
Definition: task.hpp:22
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition: task.hpp:190
std::pair< Task, Task > reduce(I beg, I end, T &result, B &&bop)
construct a task dependency graph of parallel reduction
Definition: flow_builder.hpp:898
Task & work(C &&callable)
assigns a new callable object to the task
Definition: flow_builder.hpp:971
The building blocks of dynamic tasking.
Definition: flow_builder.hpp:294
void join()
enables the subflow to join its parent task
Definition: flow_builder.hpp:336
std::pair< Task, Task > reduce_min(I beg, I end, T &result)
constructs a task dependency graph of parallel reduction through std::min
Definition: flow_builder.hpp:694