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>
275 template <
typename I>
276 size_t _estimate_chunk_size(I, I, I);
286 from._node->precede(*(to._node));
311 auto& node = _graph.emplace_back();
316 template <
typename I,
typename C>
322 auto d = std::distance(beg, end);
335 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
336 size_t r = std::distance(beg, end);
337 std::advance(e, std::min(r, g));
341 for(
size_t i=0; i<g && e != end; ++e, ++i);
345 auto task =
emplace([beg, e, c] ()
mutable {
346 std::for_each(beg, e, c);
355 return std::make_pair(source, target);
362 std::enable_if_t<std::is_arithmetic_v<I>,
void>*
366 using T = std::decay_t<I>;
368 if((s == 0) || (beg < end && s <= 0) || (beg > end && s >=0) ) {
369 TF_THROW(Error::TASKFLOW,
370 "invalid range [", beg,
", ", end,
") with step size ", s
378 g = _estimate_chunk_size(beg, end, s);
382 if constexpr(std::is_integral_v<T>) {
384 auto offset =
static_cast<T
>(g) * s;
389 auto e = std::min(beg + offset, end);
390 auto task =
emplace([=] ()
mutable {
391 for(
auto i=beg; i<e; i+=s) {
403 auto e = std::max(beg + offset, end);
404 auto task =
emplace([=] ()
mutable {
405 for(
auto i=beg; i>e; i+=s) {
416 else if constexpr(std::is_floating_point_v<T>) {
419 for(
auto i=beg; (beg<end ? i<end : i>end); i+=s, ++N) {
421 auto task =
emplace([=] ()
mutable {
423 for(
size_t n=0; n<N; ++n) {
437 auto task =
emplace([=] ()
mutable {
439 for(
size_t n=0; n<N; ++n) {
449 return std::make_pair(source, target);
454 template <
typename I,
typename T>
456 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
457 return std::min(l, r);
463 template <
typename I,
typename T>
465 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
466 return std::max(l, r);
471 template <
typename I,
typename T,
typename B,
typename U>
477 size_t d = std::distance(beg, end);
479 size_t g = std::max((d + w - 1) / w,
size_t{2});
485 auto g_results = std::make_unique<T[]>(w);
493 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
494 size_t r = std::distance(beg, end);
495 std::advance(e, std::min(r, g));
499 for(
size_t i=0; i<g && e != end; ++e, ++i);
503 auto task =
emplace([beg, e, bop, uop, res=&(g_results[
id])] ()
mutable {
505 for(++beg; beg != e; ++beg) {
506 *res = bop(std::move(*res), uop(*beg));
519 target.
work([&result, bop, res=MoC{std::move(g_results)}, w=id] () {
520 for(
auto i=0u; i<w; i++) {
521 result = bop(std::move(result), res.object[i]);
525 return std::make_pair(source, target);
529 template <
typename I,
typename T,
typename B,
typename P,
typename U>
531 I beg, I end, T& result, B&& bop, P&& pop, U&& uop
537 size_t d = std::distance(beg, end);
539 size_t g = std::max((d + w - 1) / w,
size_t{2});
544 auto g_results = std::make_unique<T[]>(w);
552 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
553 size_t r = std::distance(beg, end);
554 std::advance(e, std::min(r, g));
558 for(
size_t i=0; i<g && e != end; ++e, ++i);
562 auto task =
emplace([beg, e, uop, pop, res= &g_results[
id]] ()
mutable {
564 for(++beg; beg != e; ++beg) {
565 *res = pop(std::move(*res), *beg);
585 target.
work([&result, bop, g_results=MoC{std::move(g_results)}, w=id] () {
586 for(
auto i=0u; i<w; i++) {
587 result = bop(std::move(result), std::move(g_results.object[i]));
596 return std::make_pair(source, target);
600 template <
typename I>
601 size_t FlowBuilder::_estimate_chunk_size(I beg, I end, I step) {
603 using T = std::decay_t<I>;
608 if constexpr(std::is_integral_v<T>) {
610 N = (end - beg + step - 1) / step;
613 N = (end - beg + step + 1) / step;
616 else if constexpr(std::is_floating_point_v<T>) {
617 N =
static_cast<size_t>(std::ceil((end - beg) / step));
620 static_assert(dependent_false_v<T>,
"can't deduce chunk size");
623 return (N + w - 1) / w;
628 template <
typename L>
629 void FlowBuilder::_linearize(L& keys) {
631 auto itr = keys.begin();
632 auto end = keys.end();
640 for(++nxt; nxt != end; ++nxt, ++itr) {
641 itr->_node->precede(*(nxt->_node));
656 template <
typename I,
typename T,
typename B>
661 size_t d = std::distance(beg, end);
663 size_t g = std::max((d + w - 1) / w,
size_t{2});
669 auto g_results = std::make_unique<T[]>(w);
678 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
679 size_t r = std::distance(beg, end);
680 std::advance(e, std::min(r, g));
684 for(
size_t i=0; i<g && e != end; ++e, ++i);
689 auto task =
emplace([beg, e, op, res = &g_results[
id]] ()
mutable {
691 for(++beg; beg != e; ++beg) {
692 *res = op(std::move(*res), *beg);
715 target.
work([g_results=MoC{std::move(g_results)}, &result, op, w=id] () {
716 for(
auto i=0u; i<w; i++) {
717 result = op(std::move(result), g_results.object[i]);
721 return std::make_pair(source, target);
738 template <
typename... Args>
754 bool detached()
const;
763 bool _detached {
false};
767 template <
typename... Args>
795 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>*>
797 return std::make_tuple(
emplace(std::forward<C>(cs))...);
801 template <
typename C>
804 if constexpr(std::is_invocable_v<C, Subflow&>) {
805 auto& n = _graph.emplace_back(
806 [c=std::forward<C>(c)] (
Subflow& fb)
mutable {
808 if(fb._graph.empty()) {
815 else if constexpr(std::is_invocable_v<C>) {
816 auto& n = _graph.emplace_back(std::forward<C>(c));
820 static_assert(dependent_false_v<C>,
"invalid task work type");
825 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>*>
827 return std::make_tuple(
emplace(std::forward<C>(cs))...);
831 template <
typename C>
833 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:646
Task emplace(C &&callable)
creates a task from a given callable object
Definition: flow_builder.hpp:802
void broadcast(Task A, std::vector< Task > &others)
adds dependency links from one task A to many tasks
Definition: flow_builder.hpp:290
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:472
Task & gather(Ts &&... tasks)
adds precedence links from other tasks to this
Definition: task.hpp:201
Definition: taskflow.hpp:5
T hardware_concurrency(T... args)
Task placeholder()
creates an empty task
Definition: flow_builder.hpp:310
Subflow(Args &&... args)
constructs a subflow builder object
Definition: flow_builder.hpp:768
void detach()
enables the subflow to detach from its parent task
Definition: flow_builder.hpp:778
bool detached() const
queries if the subflow will be detached from its parent task
Definition: flow_builder.hpp:783
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:464
std::pair< Task, Task > parallel_for(I beg, I end, C &&callable, size_t chunk=0)
constructs a task dependency graph of range-based parallel_for
Definition: flow_builder.hpp:317
void precede(Task A, Task B)
adds a dependency link from task A to task B
Definition: flow_builder.hpp:285
void gather(std::vector< Task > &others, Task A)
adds dependency links from many tasks to one task A
Definition: flow_builder.hpp:300
FlowBuilder(Graph &graph)
construct a flow builder object
Definition: flow_builder.hpp:280
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:788
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:832
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition: task.hpp:182
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:657
Task & work(C &&callable)
assigns a new callable object to the task
Definition: task.hpp:248
The building blocks of dynamic tasking.
Definition: flow_builder.hpp:731
void join()
enables the subflow to join its parent task
Definition: flow_builder.hpp:773
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:455