45 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>* =
nullptr>
57 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>* =
nullptr>
77 template <
typename I,
typename C>
97 template <
typename I,
typename C, std::enable_if_t<std::is_arithmetic_v<I>,
void>* =
nullptr >
117 template <
typename I,
typename T,
typename B>
135 template <
typename I,
typename T>
153 template <
typename I,
typename T>
177 template <
typename I,
typename T,
typename B,
typename U>
204 template <
typename I,
typename T,
typename B,
typename P,
typename U>
272 template <
typename L>
283 from._node->precede(*(to._node));
308 auto& node = _graph.emplace_back();
313 template <
typename I,
typename C>
320 auto D = std::distance(beg, end);
325 return std::make_pair(S, T);
333 size_t b = (D + p - 1) / p;
334 size_t r = (D % p) ? D % p : p;
340 size_t g = (w++ >= r) ? b - 1 : b;
343 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
344 size_t x = std::distance(beg, end);
345 std::advance(e, std::min(x, g));
349 for(
size_t i=0; i<g && e != end; ++e, ++i);
353 auto task =
emplace([beg, e, c] ()
mutable {
354 std::for_each(beg, e, c);
363 return std::make_pair(S, T);
413 std::enable_if_t<std::is_arithmetic_v<I>,
void>*
417 using T = std::decay_t<I>;
419 if((s == 0) || (beg < end && s <= 0) || (beg > end && s >=0) ) {
420 TF_THROW(Error::TASKFLOW,
421 "invalid range [", beg,
", ", end,
") with step size ", s
428 if constexpr(std::is_integral_v<T>) {
430 D = (end - beg + s - 1) / s;
433 D = (end - beg + s + 1) / s;
436 else if constexpr(std::is_floating_point_v<T>) {
437 D =
static_cast<size_t>(std::ceil((end - beg) / s));
440 static_assert(dependent_false_v<T>,
"can't deduce distance");
449 source.precede(target);
450 return std::make_pair(source, target);
458 size_t b = (D + p - 1) / p;
459 size_t r = (D % p) ? D % p : p;
463 if constexpr(std::is_integral_v<T>) {
467 auto g = (w++ >= r) ? b - 1 : b;
468 auto o =
static_cast<T
>(g) * s;
469 auto e = std::min(beg + o, end);
470 auto task =
emplace([=] ()
mutable {
471 for(
auto i=beg; i<e; i+=s) {
483 auto g = (w++ >= r) ? b - 1 : b;
484 auto o =
static_cast<T
>(g) * s;
485 auto e = std::max(beg + o, end);
486 auto task =
emplace([=] ()
mutable {
487 for(
auto i=beg; i>e; i+=s) {
498 else if constexpr(std::is_floating_point_v<T>) {
502 for(
auto i=beg; (beg<end ? i<end : i>end); i+=s, ++N) {
504 auto task =
emplace([=] ()
mutable {
506 for(
size_t n=0; n<N; ++n) {
523 auto task =
emplace([=] ()
mutable {
525 for(
size_t n=0; n<N; ++n) {
535 return std::make_pair(source, target);
540 template <
typename I,
typename T>
542 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
543 return std::min(l, r);
549 template <
typename I,
typename T>
551 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
552 return std::max(l, r);
557 template <
typename I,
typename T,
typename B,
typename U>
563 size_t d = std::distance(beg, end);
565 size_t g = std::max((d + w - 1) / w,
size_t{2});
571 auto g_results = std::make_unique<T[]>(w);
579 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
580 size_t r = std::distance(beg, end);
581 std::advance(e, std::min(r, g));
585 for(
size_t i=0; i<g && e != end; ++e, ++i);
589 auto task =
emplace([beg, e, bop, uop, res=&(g_results[
id])] ()
mutable {
591 for(++beg; beg != e; ++beg) {
592 *res = bop(std::move(*res), uop(*beg));
605 target.
work([&result, bop, res=MoC{std::move(g_results)}, w=id] () {
606 for(
auto i=0u; i<w; i++) {
607 result = bop(std::move(result), res.object[i]);
611 return std::make_pair(source, target);
615 template <
typename I,
typename T,
typename B,
typename P,
typename U>
617 I beg, I end, T& result, B&& bop, P&& pop, U&& uop
623 size_t d = std::distance(beg, end);
625 size_t g = std::max((d + w - 1) / w,
size_t{2});
630 auto g_results = std::make_unique<T[]>(w);
638 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
639 size_t r = std::distance(beg, end);
640 std::advance(e, std::min(r, g));
644 for(
size_t i=0; i<g && e != end; ++e, ++i);
648 auto task =
emplace([beg, e, uop, pop, res= &g_results[
id]] ()
mutable {
650 for(++beg; beg != e; ++beg) {
651 *res = pop(std::move(*res), *beg);
671 target.
work([&result, bop, g_results=MoC{std::move(g_results)}, w=id] () {
672 for(
auto i=0u; i<w; i++) {
673 result = bop(std::move(result), std::move(g_results.object[i]));
682 return std::make_pair(source, target);
714 template <
typename L>
715 void FlowBuilder::_linearize(L& keys) {
717 auto itr = keys.begin();
718 auto end = keys.end();
726 for(++nxt; nxt != end; ++nxt, ++itr) {
727 itr->_node->precede(*(nxt->_node));
742 template <
typename I,
typename T,
typename B>
747 size_t d = std::distance(beg, end);
749 size_t g = std::max((d + w - 1) / w,
size_t{2});
755 auto g_results = std::make_unique<T[]>(w);
764 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
765 size_t r = std::distance(beg, end);
766 std::advance(e, std::min(r, g));
770 for(
size_t i=0; i<g && e != end; ++e, ++i);
775 auto task =
emplace([beg, e, op, res = &g_results[
id]] ()
mutable {
777 for(++beg; beg != e; ++beg) {
778 *res = op(std::move(*res), *beg);
801 target.
work([g_results=MoC{std::move(g_results)}, &result, op, w=id] () {
802 for(
auto i=0u; i<w; i++) {
803 result = op(std::move(result), g_results.object[i]);
807 return std::make_pair(source, target);
824 template <
typename... Args>
840 bool detached()
const;
849 bool _detached {
false};
853 template <
typename... Args>
881 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>*>
883 return std::make_tuple(
emplace(std::forward<C>(cs))...);
887 template <
typename C>
890 if constexpr(std::is_invocable_v<C, Subflow&>) {
891 auto& n = _graph.emplace_back(
892 [c=std::forward<C>(c)] (
Subflow& fb)
mutable {
894 if(fb._graph.empty()) {
901 else if constexpr(std::is_invocable_v<C>) {
902 auto& n = _graph.emplace_back(std::forward<C>(c));
906 static_assert(dependent_false_v<C>,
"invalid task work type");
911 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>*>
913 return std::make_tuple(
emplace(std::forward<C>(cs))...);
917 template <
typename C>
919 return emplace(std::forward<C>(c));
void linearize(std::vector< Task > &tasks)
adds adjacent dependency links to a linear list of tasks
Definition: flow_builder.hpp:732
Task emplace(C &&callable)
creates a task from a given callable object
Definition: flow_builder.hpp:888
std::pair< Task, Task > parallel_for(I beg, I end, C &&callable, size_t partitions=0)
constructs a task dependency graph of range-based parallel_for
Definition: flow_builder.hpp:314
void broadcast(Task A, std::vector< Task > &others)
adds dependency links from one task A to many tasks
Definition: flow_builder.hpp:287
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:558
Task & gather(Ts &&... tasks)
adds precedence links from other tasks to this (same as succeed)
Definition: task.hpp:247
Definition: taskflow.hpp:5
T hardware_concurrency(T... args)
Task placeholder()
creates an empty task
Definition: flow_builder.hpp:307
Subflow(Args &&... args)
constructs a subflow builder object
Definition: flow_builder.hpp:854
void detach()
enables the subflow to detach from its parent task
Definition: flow_builder.hpp:864
bool detached() const
queries if the subflow will be detached from its parent task
Definition: flow_builder.hpp:869
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:550
void precede(Task A, Task B)
adds a dependency link from task A to task B
Definition: flow_builder.hpp:282
void gather(std::vector< Task > &others, Task A)
adds dependency links from many tasks to one task A
Definition: flow_builder.hpp:297
FlowBuilder(Graph &graph)
construct a flow builder object
Definition: flow_builder.hpp:277
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:874
Handle to modify and access a task.
Definition: task.hpp:18
Task silent_emplace(C &&callable)
the same as tf::FlowBuilder::emplace (starting at 2.1.0)
Definition: flow_builder.hpp:918
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition: task.hpp:220
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:743
Task & work(C &&callable)
assigns a new callable object to the task
Definition: task.hpp:313
The building blocks of dynamic tasking.
Definition: flow_builder.hpp:817
void join()
enables the subflow to join its parent task
Definition: flow_builder.hpp:859
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:541