40 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>* =
nullptr>
52 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>* =
nullptr>
72 template <
typename I,
typename C>
92 template <
typename I,
typename C, std::enable_if_t<std::is_arithmetic_v<I>,
void>* =
nullptr >
112 template <
typename I,
typename T,
typename B>
130 template <
typename I,
typename T>
148 template <
typename I,
typename T>
172 template <
typename I,
typename T,
typename B,
typename U>
199 template <
typename I,
typename T,
typename B,
typename P,
typename U>
267 template <
typename L>
270 template <
typename I>
271 size_t _estimate_chunk_size(I, I, I);
275 inline FlowBuilder::FlowBuilder(Graph& graph) :
281 from._node->precede(*(to._node));
306 auto& node = _graph.emplace_back();
311 template <
typename I,
typename C>
317 auto d = std::distance(beg, end);
330 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
331 size_t r = std::distance(beg, end);
332 std::advance(e, std::min(r, g));
336 for(
size_t i=0; i<g && e != end; ++e, ++i);
340 auto task =
emplace([beg, e, c] ()
mutable {
341 std::for_each(beg, e, c);
343 source.precede(task);
344 task.precede(target);
350 return std::make_pair(source, target);
357 std::enable_if_t<std::is_arithmetic_v<I>,
void>*
361 using T = std::decay_t<I>;
363 if((s == 0) || (beg < end && s <= 0) || (beg > end && s >=0) ) {
364 TF_THROW(Error::FLOW_BUILDER,
365 "invalid range [", beg,
", ", end,
") with step size ", s
373 g = _estimate_chunk_size(beg, end, s);
377 if constexpr(std::is_integral_v<T>) {
379 auto offset =
static_cast<T
>(g) * s;
384 auto e = std::min(beg + offset, end);
385 auto task =
emplace([=] ()
mutable {
386 for(
auto i=beg; i<e; i+=s) {
390 source.precede(task);
391 task.precede(target);
398 auto e = std::max(beg + offset, end);
399 auto task =
emplace([=] ()
mutable {
400 for(
auto i=beg; i>e; i+=s) {
404 source.precede(task);
405 task.precede(target);
411 else if constexpr(std::is_floating_point_v<T>) {
414 for(
auto i=beg; (beg<end ? i<end : i>end); i+=s, ++N) {
416 auto task =
emplace([=] ()
mutable {
418 for(
size_t n=0; n<N; ++n) {
425 source.precede(task);
426 task.precede(target);
432 auto task =
emplace([=] ()
mutable {
434 for(
size_t n=0; n<N; ++n) {
439 source.precede(task);
440 task.precede(target);
444 return std::make_pair(source, target);
449 template <
typename I,
typename T>
451 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
452 return std::min(l, r);
458 template <
typename I,
typename T>
460 return reduce(beg, end, result, [] (
const auto& l,
const auto& r) {
461 return std::max(l, r);
466 template <
typename I,
typename T,
typename B,
typename U>
472 size_t d = std::distance(beg, end);
474 size_t g = std::max((d + w - 1) / w,
size_t{2});
480 auto g_results = std::make_unique<T[]>(w);
488 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
489 size_t r = std::distance(beg, end);
490 std::advance(e, std::min(r, g));
494 for(
size_t i=0; i<g && e != end; ++e, ++i);
498 auto task =
emplace([beg, e, bop, uop, res=&(g_results[
id])] ()
mutable {
500 for(++beg; beg != e; ++beg) {
501 *res = bop(std::move(*res), uop(*beg));
505 source.precede(task);
506 task.precede(target);
514 target.work([&result, bop, res=MoC{std::move(g_results)}, w=id] () {
515 for(
auto i=0u; i<w; i++) {
516 result = bop(std::move(result), res.object[i]);
520 return std::make_pair(source, target);
524 template <
typename I,
typename T,
typename B,
typename P,
typename U>
526 I beg, I end, T& result, B&& bop, P&& pop, U&& uop
532 size_t d = std::distance(beg, end);
534 size_t g = std::max((d + w - 1) / w,
size_t{2});
539 auto g_results = std::make_unique<T[]>(w);
547 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
548 size_t r = std::distance(beg, end);
549 std::advance(e, std::min(r, g));
553 for(
size_t i=0; i<g && e != end; ++e, ++i);
557 auto task =
emplace([beg, e, uop, pop, res= &g_results[
id]] ()
mutable {
559 for(++beg; beg != e; ++beg) {
560 *res = pop(std::move(*res), *beg);
570 source.precede(task);
571 task.precede(target);
580 target.work([&result, bop, g_results=MoC{std::move(g_results)}, w=id] () {
581 for(
auto i=0u; i<w; i++) {
582 result = bop(std::move(result), std::move(g_results.object[i]));
591 return std::make_pair(source, target);
595 template <
typename I>
596 size_t FlowBuilder::_estimate_chunk_size(I beg, I end, I step) {
598 using T = std::decay_t<I>;
603 if constexpr(std::is_integral_v<T>) {
605 N = (end - beg + step - 1) / step;
608 N = (
end - beg + step + 1) / step;
611 else if constexpr(std::is_floating_point_v<T>) {
612 N =
static_cast<size_t>(
std::ceil((end - beg) / step));
615 static_assert(dependent_false_v<T>,
"can't deduce chunk size");
618 return (N + w - 1) / w;
623 template <
typename L>
624 void FlowBuilder::_linearize(L& keys) {
626 auto itr = keys.begin();
627 auto end = keys.end();
635 for(++nxt; nxt !=
end; ++nxt, ++itr) {
636 itr->_node->precede(*(nxt->_node));
651 template <
typename I,
typename T,
typename B>
656 size_t d = std::distance(beg, end);
658 size_t g = std::max((d + w - 1) / w,
size_t{2});
664 auto g_results = std::make_unique<T[]>(w);
673 if constexpr(std::is_same_v<category, std::random_access_iterator_tag>) {
674 size_t r = std::distance(beg, end);
675 std::advance(e, std::min(r, g));
679 for(
size_t i=0; i<g && e != end; ++e, ++i);
684 auto task =
emplace([beg, e, op, res = &g_results[
id]] ()
mutable {
686 for(++beg; beg != e; ++beg) {
687 *res = op(std::move(*res), *beg);
695 source.precede(task);
696 task.precede(target);
710 target.work([g_results=MoC{std::move(g_results)}, &result, op, w=id] () {
711 for(
auto i=0u; i<w; i++) {
712 result = op(std::move(result), g_results.object[i]);
716 return std::make_pair(source, target);
730 template <
typename... Args>
755 bool _detached {
false};
759 template <
typename... Args>
760 SubflowBuilder::SubflowBuilder(Args&&... args) :
787 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>*>
789 return std::make_tuple(
emplace(std::forward<C>(cs))...);
793 template <
typename C>
796 if constexpr(std::is_invocable_v<C, SubflowBuilder&>) {
797 auto& n = _graph.emplace_back(
800 if(fb._graph.empty()) {
807 else if constexpr(std::is_invocable_v<C>) {
808 auto& n = _graph.emplace_back(std::forward<C>(c));
812 static_assert(dependent_false_v<C>,
"invalid task work type");
817 template <
typename... C, std::enable_if_t<(
sizeof...(C)>1),
void>*>
819 return std::make_tuple(
emplace(std::forward<C>(cs))...);
823 template <
typename C>
825 return emplace(std::forward<C>(c));
bool joined() const
queries if the subflow will join its parent task
Definition: flow_builder.hpp:780
void linearize(std::vector< Task > &tasks)
adds adjacent dependency links to a linear list of tasks
Definition: flow_builder.hpp:641
bool detached() const
queries if the subflow will be detached from its parent task
Definition: flow_builder.hpp:775
void detach()
enables the subflow to detach from its parent task
Definition: flow_builder.hpp:770
void broadcast(Task A, std::vector< Task > &others)
adds dependency links from one task A to many tasks
Definition: flow_builder.hpp:285
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:467
Task & gather(Ts &&... tasks)
adds precedence links from other tasks to this
Definition: task.hpp:224
Definition: taskflow.hpp:6
auto silent_emplace(C &&callable)
the same as tf::FlowBuilder::emplace (starting at 2.1.0)
Definition: flow_builder.hpp:824
T hardware_concurrency(T... args)
Task placeholder()
creates an empty task
Definition: flow_builder.hpp:305
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:459
The building blocks of dynamic tasking.
Definition: flow_builder.hpp:726
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:312
auto emplace(C &&callable)
creates a task from a given callable object
Definition: flow_builder.hpp:794
void precede(Task A, Task B)
adds a dependency link from task A to task B
Definition: flow_builder.hpp:280
void gather(std::vector< Task > &others, Task A)
adds dependency links from many tasks to one task A
Definition: flow_builder.hpp:295
The building blocks of task dependency graphs.
Definition: flow_builder.hpp:13
void join()
enables the subflow to join its parent task
Definition: flow_builder.hpp:765
Handle to modify and access a task.
Definition: task.hpp:18
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition: task.hpp:205
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:652
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:450