Loading...
Searching...
No Matches
flow_builder.hpp
1#pragma once
2
3#include "task.hpp"
4#include "../algorithm/partitioner.hpp"
5
10
11namespace tf {
12
23
24 friend class Executor;
25
26 public:
27
31 FlowBuilder(Graph& graph);
32
51 template <StaticTaskLike C>
52 Task emplace(C&& callable);
53
72 template <RuntimeTaskLike C>
73 Task emplace(C&& callable);
74
97 template <SubflowTaskLike C>
98 Task emplace(C&& callable);
99
130 template <ConditionTaskLike C>
131 Task emplace(C&& callable);
132
164 template <MultiConditionTaskLike C>
165 Task emplace(C&& callable);
166
191 template <typename... C> requires (sizeof...(C) > 1)
192 auto emplace(C&&... callables);
193
214 void erase(Task task);
215
279 template <GraphLike T>
280 Task composed_of(T& object);
281
303 Task adopt(Graph&& graph);
304
330
348 void linearize(std::vector<Task>& tasks);
349
365 void linearize(std::initializer_list<Task> tasks);
366
367
368 // ------------------------------------------------------------------------
369 // parallel iterations
370 // ------------------------------------------------------------------------
371
404 template <typename B, typename E, typename C, PartitionerLike P = DefaultPartitioner>
405 Task for_each(B first, E last, C callable, P part = P());
406
446 template <typename B, typename E, typename S, typename C, PartitionerLike P = DefaultPartitioner>
447 Task for_each_index(B first, E last, S step, C callable, P part = P());
448
485 template <IndexRange1DLike R, typename C, PartitionerLike P = DefaultPartitioner>
486 Task for_each_by_index(R range, C callable, P part = P());
487
556 template <IndexRangeMDLike R, typename C, PartitionerLike P = DefaultPartitioner>
557 Task for_each_by_index(R range, C callable, P part = P());
558
559 // ------------------------------------------------------------------------
560 // transform
561 // ------------------------------------------------------------------------
562
597 template <typename B, typename E, typename O, typename C,
599 Task transform(B first1, E last1, O d_first, C c, P part = P());
600
637 template <typename B1, typename E1, typename B2, typename O, typename C,
640 Task transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P part = P());
641
642 // ------------------------------------------------------------------------
643 // reduction
644 // ------------------------------------------------------------------------
645
679 template <typename B, typename E, typename T, typename O, PartitionerLike P = DefaultPartitioner>
680 Task reduce(B first, E last, T& init, O bop, P part = P());
681
736 template <IndexRangeLike R, typename T, typename L, typename G, PartitionerLike P = DefaultPartitioner>
737 Task reduce_by_index(R range, T& init, L lop, G gop, P part = P());
738
739 // ------------------------------------------------------------------------
740 // transform and reduction
741 // ------------------------------------------------------------------------
742
778 template <typename B, typename E, typename T, typename BOP, typename UOP,
780 Task transform_reduce(B first, E last, T& init, BOP bop, UOP uop, P part = P());
781
818
819 template <typename B1, typename E1, typename B2, typename T,
820 typename BOP_R, typename BOP_T, PartitionerLike P = DefaultPartitioner>
823 B1 first1, E1 last1, B2 first2, T& init, BOP_R bop_r, BOP_T bop_t, P part = P()
824 );
825
826 // ------------------------------------------------------------------------
827 // scan
828 // ------------------------------------------------------------------------
829
868 template <typename B, typename E, typename D, typename BOP>
869 Task inclusive_scan(B first, E last, D d_first, BOP bop);
870
912 template <typename B, typename E, typename D, typename BOP, typename T>
913 Task inclusive_scan(B first, E last, D d_first, BOP bop, T init);
914
955 template <typename B, typename E, typename D, typename T, typename BOP>
956 Task exclusive_scan(B first, E last, D d_first, T init, BOP bop);
957
958 // ------------------------------------------------------------------------
959 // transform scan
960 // ------------------------------------------------------------------------
961
1003 template <typename B, typename E, typename D, typename BOP, typename UOP>
1004 Task transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop);
1005
1050 template <typename B, typename E, typename D, typename BOP, typename UOP, typename T>
1051 Task transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop, T init);
1052
1096 template <typename B, typename E, typename D, typename T, typename BOP, typename UOP>
1097 Task transform_exclusive_scan(B first, E last, D d_first, T init, BOP bop, UOP uop);
1098
1099 // ------------------------------------------------------------------------
1100 // find
1101 // ------------------------------------------------------------------------
1102
1148 template <typename B, typename E, typename T, typename UOP, PartitionerLike P = DefaultPartitioner>
1149 Task find_if(B first, E last, T &result, UOP predicate, P part = P());
1150
1196 template <typename B, typename E, typename T, typename UOP, PartitionerLike P = DefaultPartitioner>
1197 Task find_if_not(B first, E last, T &result, UOP predicate, P part = P());
1198
1248 template <typename B, typename E, typename T, typename C, PartitionerLike P>
1249 Task min_element(B first, E last, T& result, C comp, P part);
1250
1300 template <typename B, typename E, typename T, typename C, PartitionerLike P>
1301 Task max_element(B first, E last, T& result, C comp, P part);
1302
1303 // ------------------------------------------------------------------------
1304 // sort
1305 // ------------------------------------------------------------------------
1306
1326 template <typename B, typename E, typename C>
1327 Task sort(B first, E last, C cmp);
1328
1348 template <typename B, typename E>
1349 Task sort(B first, E last);
1350
1376
1377 template <typename B1, typename E1, typename B2, typename E2,
1378 typename O, PartitionerLike P = DefaultPartitioner>
1379 Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first, P part = P());
1380
1409
1410 template <typename B1, typename E1, typename B2, typename E2,
1411 typename O, typename C, PartitionerLike P = DefaultPartitioner>
1413 Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first, C cmp, P part = P());
1414
1440 template<typename B, typename E, typename V, PartitionerLike P = DefaultPartitioner>
1441 Task fill(B first, E last, V value, P part = P());
1442
1468 template<typename B, std::integral C, typename V, PartitionerLike P = DefaultPartitioner>
1469 Task fill_n(B first, C count, V value, P part = P());
1470
1498 template <typename B, typename E, typename G, PartitionerLike P= DefaultPartitioner>
1499 Task generate(B first, E last, G gen, P part = P());
1500
1528 template <typename B, std::integral C, typename G, PartitionerLike P = DefaultPartitioner>
1529 Task generate_n(B first, C count, G gen, P part = P());
1530
1531 protected:
1532
1536 Graph& _graph;
1537
1538 private:
1539
1540 template <typename L>
1541 void _linearize(L&);
1542};
1543
1544// Constructor
1546 _graph {graph} {
1547}
1548
1549// Function: emplace
1550template <StaticTaskLike C>
1552 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1553 std::in_place_type_t<Node::Static>{}, std::forward<C>(c)
1554 ));
1555}
1556
1557// Function: emplace
1558template <RuntimeTaskLike C>
1560 if constexpr (std::is_invocable_v<C, tf::Runtime&>) {
1561 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1562 std::in_place_type_t<Node::Runtime>{}, std::forward<C>(c)
1563 ));
1564 }
1565 else if constexpr (std::is_invocable_v<C, tf::NonpreemptiveRuntime&>) {
1566 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1567 std::in_place_type_t<Node::NonpreemptiveRuntime>{}, std::forward<C>(c)
1568 ));
1569 }
1570 else {
1571 static_assert(dependent_false_v<C>, "invalid runtime task callable");
1572 }
1573}
1574
1575// Function: emplace
1576template <SubflowTaskLike C>
1578 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1579 std::in_place_type_t<Node::Subflow>{}, std::forward<C>(c)
1580 ));
1581}
1582
1583// Function: emplace
1584template <ConditionTaskLike C>
1586 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1587 std::in_place_type_t<Node::Condition>{}, std::forward<C>(c)
1588 ));
1589}
1590
1591// Function: emplace
1592template <MultiConditionTaskLike C>
1594 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1595 std::in_place_type_t<Node::MultiCondition>{}, std::forward<C>(c)
1596 ));
1597}
1598
1599// Function: composed_of
1600template <GraphLike T>
1602 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1603 std::in_place_type_t<Node::Module>{}, retrieve_graph(target)
1604 ));
1605}
1606
1607// Function: adopt
1609 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1610 std::in_place_type_t<Node::AdoptedModule>{}, std::move(graph)
1611 ));
1612}
1613
1614// Function: placeholder
1616 auto node = _graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1617 std::in_place_type_t<Node::Placeholder>{}
1618 );
1619 return Task(node);
1620}
1621
1622// Function: emplace
1623template <typename... C> requires (sizeof...(C) > 1)
1624auto FlowBuilder::emplace(C&&... cs) {
1625 return std::make_tuple(emplace(std::forward<C>(cs))...);
1626}
1627
1628// Function: erase
1629inline void FlowBuilder::erase(Task task) {
1630
1631 if (!task._node) {
1632 return;
1633 }
1634
1635 // remove task from its successors' predecessor list
1636 for(size_t i=0; i<task._node->_num_successors; ++i) {
1637 task._node->_edges[i]->_remove_predecessors(task._node);
1638 }
1639
1640 // remove task from its precedessors' successor list
1641 for(size_t i=task._node->_num_successors; i<task._node->_edges.size(); ++i) {
1642 task._node->_edges[i]->_remove_successors(task._node);
1643 }
1644
1645 _graph._erase(task._node);
1646}
1647
1648
1649// Procedure: _linearize
1650template <typename L>
1651void FlowBuilder::_linearize(L& keys) {
1652
1653 auto itr = keys.begin();
1654 auto end = keys.end();
1655
1656 if(itr == end) {
1657 return;
1658 }
1659
1660 auto nxt = itr;
1661
1662 for(++nxt; nxt != end; ++nxt, ++itr) {
1663 itr->_node->_precede(nxt->_node);
1664 }
1665}
1666
1667// Procedure: linearize
1668inline void FlowBuilder::linearize(std::vector<Task>& keys) {
1669 _linearize(keys);
1670}
1671
1672// Procedure: linearize
1673inline void FlowBuilder::linearize(std::initializer_list<Task> keys) {
1674 _linearize(keys);
1675}
1676
1677// ----------------------------------------------------------------------------
1678
1715class Subflow : public FlowBuilder {
1716
1717 friend class Executor;
1718 friend class FlowBuilder;
1719
1720 public:
1721
1737 void join();
1738
1754 bool joinable() const noexcept;
1755
1759 Executor& executor() noexcept;
1760
1764 Graph& graph() { return _graph; }
1765
1775 void retain(bool flag) noexcept;
1776
1784 bool retain() const;
1785
1786 private:
1787
1788 Subflow(Executor&, Worker&, Node*, Graph&);
1789
1790 Subflow() = delete;
1791 Subflow(const Subflow&) = delete;
1792 Subflow(Subflow&&) = delete;
1793
1794 Executor& _executor;
1795 Worker& _worker;
1796 Node* _node;
1797};
1798
1799// Constructor
1800inline Subflow::Subflow(Executor& executor, Worker& worker, Node* node, Graph& graph) :
1801 FlowBuilder {graph},
1802 _executor {executor},
1803 _worker {worker},
1804 _node {node} {
1805
1806 // need to reset since there could have iterative control flow
1807 _node->_nstate &= ~(NSTATE::JOINED_SUBFLOW | NSTATE::RETAIN_SUBFLOW);
1808
1809 // clear the graph
1810 graph.clear();
1811}
1812
1813// Function: joinable
1814inline bool Subflow::joinable() const noexcept {
1815 return !(_node->_nstate & NSTATE::JOINED_SUBFLOW);
1816}
1817
1818// Function: executor
1819inline Executor& Subflow::executor() noexcept {
1820 return _executor;
1821}
1822
1823// Function: retain
1824inline void Subflow::retain(bool flag) noexcept {
1825 // default value is not to retain
1826 if(flag == true) {
1827 _node->_nstate |= NSTATE::RETAIN_SUBFLOW;
1828 }
1829 else {
1830 _node->_nstate &= ~NSTATE::RETAIN_SUBFLOW;
1831 }
1832
1833 //_node->_nstate = (_node->_nstate & ~NSTATE::RETAIN_SUBFLOW) |
1834 // (-static_cast<int>(flag) & NSTATE::RETAIN_SUBFLOW);
1835}
1836
1837// Function: retain
1838inline bool Subflow::retain() const {
1839 return _node->_nstate & NSTATE::RETAIN_SUBFLOW;
1840}
1841
1842} // end of namespace tf. ---------------------------------------------------
class to create an empty task parameter for compile-time optimization
Definition graph.hpp:191
class to create an executor
Definition executor.hpp:62
Task transform(B first1, E last1, O d_first, C c, P part=P())
constructs a parallel-transform task
Task inclusive_scan(B first, E last, D d_first, BOP bop, T init)
creates an STL-styled parallel inclusive-scan task with an initial value
Task transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P part=P())
constructs a parallel-transform task
Task inclusive_scan(B first, E last, D d_first, BOP bop)
creates an STL-styled parallel inclusive-scan task
Task for_each_by_index(R range, C callable, P part=P())
constructs a parallel-for task over a one-dimensional index range
Task sort(B first, E last, C cmp)
constructs a dynamic task to perform STL-styled parallel sort
Task adopt(Graph &&graph)
creates a module task from a graph by taking over its ownership
Definition flow_builder.hpp:1608
Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first, C cmp, P part=P())
merges two sorted ranges into a single sorted output using the supplied comparator
Task generate_n(B first, C count, G gen, P part=P())
generates N values into a range in parallel using a callable
Task for_each_index(B first, E last, S step, C callable, P part=P())
constructs an index-based parallel-for task
Task reduce_by_index(R range, T &init, L lop, G gop, P part=P())
constructs an index range-based parallel-reduction task
Task find_if(B first, E last, T &result, UOP predicate, P part=P())
constructs a task to perform STL-styled find-if algorithm
Task transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop, T init)
creates an STL-styled parallel transform-inclusive scan task
Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first, P part=P())
merges two sorted ranges into a single sorted output using the std::less comparator
Task emplace(C &&callable)
creates a static task
Definition flow_builder.hpp:1551
Task exclusive_scan(B first, E last, D d_first, T init, BOP bop)
creates an STL-styled parallel exclusive-scan task
Task transform_reduce(B first, E last, T &init, BOP bop, UOP uop, P part=P())
constructs an STL-styled parallel transform-reduce task
void erase(Task task)
removes a task from a taskflow
Definition flow_builder.hpp:1629
Task fill(B first, E last, V value, P part=P())
fills a range with a given value in parallel
FlowBuilder(Graph &graph)
constructs a flow builder with a graph
Definition flow_builder.hpp:1545
Task fill_n(B first, C count, V value, P part=P())
fills N elements with a given value in parallel
Task max_element(B first, E last, T &result, C comp, P part)
constructs a task to perform STL-styled max-element algorithm
Task min_element(B first, E last, T &result, C comp, P part)
constructs a task to perform STL-styled min-element algorithm
Task generate(B first, E last, G gen, P part=P())
generates values into a range in parallel using a callable
Task sort(B first, E last)
constructs a dynamic task to perform STL-styled parallel sort using the std::less<T> comparator,...
Task transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop)
creates an STL-styled parallel transform-inclusive scan task
Task transform_exclusive_scan(B first, E last, D d_first, T init, BOP bop, UOP uop)
creates an STL-styled parallel transform-exclusive scan task
void linearize(std::vector< Task > &tasks)
adds adjacent dependency links to a linear list of tasks
Definition flow_builder.hpp:1668
Task find_if_not(B first, E last, T &result, UOP predicate, P part=P())
constructs a task to perform STL-styled find-if-not algorithm
Task for_each(B first, E last, C callable, P part=P())
constructs an STL-styled parallel-for task
Task composed_of(T &object)
creates a module task for the target object
Definition flow_builder.hpp:1601
Task placeholder()
creates a placeholder task
Definition flow_builder.hpp:1615
Task transform_reduce(B1 first1, E1 last1, B2 first2, T &init, BOP_R bop_r, BOP_T bop_t, P part=P())
constructs an STL-styled parallel transform-reduce task
Task reduce(B first, E last, T &init, O bop, P part=P())
constructs an STL-styled parallel-reduction task
class to create a graph object
Definition graph.hpp:47
void clear()
clears the graph
Definition graph.hpp:960
class to construct a subflow graph from the execution of a dynamic task
Definition flow_builder.hpp:1715
Executor & executor() noexcept
acquires the associated executor
Definition flow_builder.hpp:1819
Graph & graph()
acquires the associated graph
Definition flow_builder.hpp:1764
void join()
enables the subflow to join its parent task
bool joinable() const noexcept
queries if the subflow is joinable
Definition flow_builder.hpp:1814
bool retain() const
queries if the subflow will be retained after it is joined
Definition flow_builder.hpp:1838
class to create a task handle over a taskflow node
Definition task.hpp:263
class to create a worker in an executor
Definition worker.hpp:55
determines if a type is a partitioner
Definition partitioner.hpp:919
taskflow namespace
Definition small_vector.hpp:20
Graph & retrieve_graph(T &target)
retrieves a reference to the underlying tf::Graph from an object
Definition graph.hpp:1098
GuidedPartitioner<> DefaultPartitioner
default partitioner set to tf::GuidedPartitioner
Definition partitioner.hpp:911