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
13// ------------------------------------------------------------------------------------------------
14// Concept
15// ------------------------------------------------------------------------------------------------
16
31template <typename C, typename B>
33requires(C c, B b) {
34 c(*b);
35};
36
52template <typename C, typename B1, typename B2>
54requires(C c, B1 b1, B2 b2) {
55 c(*b1, *b2);
56};
57
73template <typename C, typename B, typename O>
75requires(C c, B b, O o) {
76 *o = c(*b);
77};
78
95template <typename C, typename B1, typename B2, typename O>
97requires(C c, B1 b1, B2 b2, O o) {
98 *o = c(*b1, *b2);
99};
100
101// ------------------------------------------------------------------------------------------------
102// FlowBuilder
103// ------------------------------------------------------------------------------------------------
104
115
116 friend class Executor;
117
118 public:
119
123 FlowBuilder(Graph& graph);
124
143 template <StaticTaskLike C>
144 Task emplace(C&& callable);
145
164 template <RuntimeTaskLike C>
165 Task emplace(C&& callable);
166
189 template <SubflowTaskLike C>
190 Task emplace(C&& callable);
191
222 template <ConditionTaskLike C>
223 Task emplace(C&& callable);
224
256 template <MultiConditionTaskLike C>
257 Task emplace(C&& callable);
258
283 template <typename... C> requires (sizeof...(C) > 1)
284 auto emplace(C&&... callables);
285
306 void erase(Task task);
307
371 template <GraphLike T>
372 Task composed_of(T& object);
373
395 Task adopt(Graph&& graph);
396
425 template <GraphLike T>
426 Task emplace(T& object);
427
454 Task emplace(Graph&& graph);
455
481
499 void linearize(std::vector<Task>& tasks);
500
516 void linearize(std::initializer_list<Task> tasks);
517
518
519 // ------------------------------------------------------------------------
520 // parallel iterations
521 // ------------------------------------------------------------------------
522
555 template <InputIteratorLike B, InputIteratorLike E, typename C, PartitionerLike P = DefaultPartitioner>
557 Task for_each(B first, E last, C callable, P part = P());
558
598 template <typename B, typename E, typename S, typename C, PartitionerLike P = DefaultPartitioner>
599 Task for_each_index(B first, E last, S step, C callable, P part = P());
600
717 template <IndexRangesLike R, typename C, PartitionerLike P = DefaultPartitioner>
718 Task for_each_by_index(R range, C callable, P part = P());
719
720 // ------------------------------------------------------------------------
721 // transform
722 // ------------------------------------------------------------------------
723
758 template <InputIteratorLike B, InputIteratorLike E, typename O, typename C,
760 requires UnaryTransformLike<
761 C,
762 std::decay_t<std::unwrap_ref_decay_t<B>>,
763 std::decay_t<std::unwrap_ref_decay_t<O>>
764 >
765 Task transform(B first1, E last1, O d_first, C c, P part = P());
766
803 template <InputIteratorLike B1, InputIteratorLike E1, InputIteratorLike B2, typename O, typename C,
805 requires BinaryTransformLike<
806 C,
807 std::decay_t<std::unwrap_ref_decay_t<B1>>,
808 std::decay_t<std::unwrap_ref_decay_t<B2>>,
809 std::decay_t<std::unwrap_ref_decay_t<O>>
810 >
811 Task transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P part = P());
812
813 // ------------------------------------------------------------------------
814 // reduction
815 // ------------------------------------------------------------------------
816
850 template <InputIteratorLike B, InputIteratorLike E, typename T, typename O, PartitionerLike P = DefaultPartitioner>
851 Task reduce(B first, E last, T& init, O bop, P part = P());
852
950 template <IndexRangesLike R, typename T, typename L, typename G, PartitionerLike P = DefaultPartitioner>
951 Task reduce_by_index(R range, T& init, L lop, G gop, P part = P());
952
953 // ------------------------------------------------------------------------
954 // transform and reduction
955 // ------------------------------------------------------------------------
956
992 template <InputIteratorLike B, InputIteratorLike E, typename T, typename BOP, typename UOP,
994 Task transform_reduce(B first, E last, T& init, BOP bop, UOP uop, P part = P());
995
1032
1033 template <InputIteratorLike B1, InputIteratorLike E1, InputIteratorLike B2, typename T,
1034 typename BOP_R, typename BOP_T, PartitionerLike P = DefaultPartitioner>
1035 requires BinaryOperationLike<
1036 BOP_T,
1037 std::decay_t<std::unwrap_ref_decay_t<B1>>,
1038 std::decay_t<std::unwrap_ref_decay_t<B2>>
1039 >
1041 B1 first1, E1 last1, B2 first2, T& init, BOP_R bop_r, BOP_T bop_t, P part = P()
1042 );
1043
1044 // ------------------------------------------------------------------------
1045 // scan
1046 // ------------------------------------------------------------------------
1047
1086 template <InputIteratorLike B, InputIteratorLike E, typename D, typename BOP>
1087 Task inclusive_scan(B first, E last, D d_first, BOP bop);
1088
1130 template <InputIteratorLike B, InputIteratorLike E, typename D, typename BOP, typename T>
1131 Task inclusive_scan(B first, E last, D d_first, BOP bop, T init);
1132
1173 template <InputIteratorLike B, InputIteratorLike E, typename D, typename T, typename BOP>
1174 Task exclusive_scan(B first, E last, D d_first, T init, BOP bop);
1175
1176 // ------------------------------------------------------------------------
1177 // transform scan
1178 // ------------------------------------------------------------------------
1179
1221 template <InputIteratorLike B, InputIteratorLike E, typename D, typename BOP, typename UOP>
1222 Task transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop);
1223
1268 template <InputIteratorLike B, InputIteratorLike E, typename D, typename BOP, typename UOP, typename T>
1269 Task transform_inclusive_scan(B first, E last, D d_first, BOP bop, UOP uop, T init);
1270
1314 template <InputIteratorLike B, InputIteratorLike E, typename D, typename T, typename BOP, typename UOP>
1315 Task transform_exclusive_scan(B first, E last, D d_first, T init, BOP bop, UOP uop);
1316
1317 // ------------------------------------------------------------------------
1318 // find
1319 // ------------------------------------------------------------------------
1320
1366 template <InputIteratorLike B, InputIteratorLike E, typename T, typename UOP, PartitionerLike P = DefaultPartitioner>
1367 Task find_if(B first, E last, T &result, UOP predicate, P part = P());
1368
1414 template <InputIteratorLike B, InputIteratorLike E, typename T, typename UOP, PartitionerLike P = DefaultPartitioner>
1415 Task find_if_not(B first, E last, T &result, UOP predicate, P part = P());
1416
1466 template <InputIteratorLike B, InputIteratorLike E, typename T, typename C, PartitionerLike P>
1467 Task min_element(B first, E last, T& result, C comp, P part);
1468
1518 template <InputIteratorLike B, InputIteratorLike E, typename T, typename C, PartitionerLike P>
1519 Task max_element(B first, E last, T& result, C comp, P part);
1520
1521 // ------------------------------------------------------------------------
1522 // sort
1523 // ------------------------------------------------------------------------
1524
1544 template <InputIteratorLike B, InputIteratorLike E, typename C>
1545 Task sort(B first, E last, C cmp);
1546
1566 template <InputIteratorLike B, InputIteratorLike E>
1567 Task sort(B first, E last);
1568
1602 template <InputIteratorLike B1, InputIteratorLike E1, InputIteratorLike B2, InputIteratorLike E2, typename O>
1603 Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first);
1604
1640 template <InputIteratorLike B1, InputIteratorLike E1,
1642 typename O, typename C>
1643 Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first, C cmp);
1644
1670 template<InputIteratorLike B, InputIteratorLike E, typename V, PartitionerLike P = DefaultPartitioner>
1671 Task fill(B first, E last, V value, P part = P());
1672
1698 template<InputIteratorLike B, std::integral C, typename V, PartitionerLike P = DefaultPartitioner>
1699 Task fill_n(B first, C count, V value, P part = P());
1700
1728 template <InputIteratorLike B, InputIteratorLike E, typename G, PartitionerLike P= DefaultPartitioner>
1729 Task generate(B first, E last, G gen, P part = P());
1730
1758 template <InputIteratorLike B, std::integral C, typename G, PartitionerLike P = DefaultPartitioner>
1759 Task generate_n(B first, C count, G gen, P part = P());
1760
1761 protected:
1762
1766 Graph& _graph;
1767
1768 private:
1769
1770 template <typename L>
1771 void _linearize(L&);
1772};
1773
1774// Constructor
1776 _graph {graph} {
1777}
1778
1779// Function: emplace
1780template <StaticTaskLike C>
1782 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1783 std::in_place_type_t<Node::Static>{}, std::forward<C>(c)
1784 ));
1785}
1786
1787// Function: emplace
1788template <RuntimeTaskLike C>
1790 if constexpr (std::is_invocable_v<C, tf::Runtime&>) {
1791 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1792 std::in_place_type_t<Node::Runtime>{}, std::forward<C>(c)
1793 ));
1794 }
1795 else if constexpr (std::is_invocable_v<C, tf::NonpreemptiveRuntime&>) {
1796 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1797 std::in_place_type_t<Node::NonpreemptiveRuntime>{}, std::forward<C>(c)
1798 ));
1799 }
1800 else {
1801 static_assert(dependent_false_v<C>, "invalid runtime task callable");
1802 }
1803}
1804
1805// Function: emplace
1806template <SubflowTaskLike C>
1808 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1809 std::in_place_type_t<Node::Subflow>{}, std::forward<C>(c)
1810 ));
1811}
1812
1813// Function: emplace
1814template <ConditionTaskLike C>
1816 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1817 std::in_place_type_t<Node::Condition>{}, std::forward<C>(c)
1818 ));
1819}
1820
1821// Function: emplace
1822template <MultiConditionTaskLike C>
1824 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1825 std::in_place_type_t<Node::MultiCondition>{}, std::forward<C>(c)
1826 ));
1827}
1828
1829// Function: composed_of
1830template <GraphLike T>
1832 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1833 std::in_place_type_t<Node::Module>{}, retrieve_graph(target)
1834 ));
1835}
1836
1837// Function: adopt
1839 return Task(_graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1840 std::in_place_type_t<Node::AdoptedModule>{}, std::move(graph)
1841 ));
1842}
1843
1844// Function: emplace (convenience overload of composed_of)
1845template <GraphLike T>
1847 return composed_of(object);
1848}
1849
1850// Function: emplace (convenience overload of adopt)
1852 return adopt(std::move(graph));
1853}
1854
1855// Function: placeholder
1857 auto node = _graph._emplace_back(NSTATE::NONE, ESTATE::NONE, DefaultTaskParams{}, nullptr, nullptr, 0,
1858 std::in_place_type_t<Node::Placeholder>{}
1859 );
1860 return Task(node);
1861}
1862
1863// Function: emplace
1864template <typename... C> requires (sizeof...(C) > 1)
1865auto FlowBuilder::emplace(C&&... cs) {
1866 return std::make_tuple(emplace(std::forward<C>(cs))...);
1867}
1868
1869// Function: erase
1870inline void FlowBuilder::erase(Task task) {
1871
1872 if (!task._node) {
1873 return;
1874 }
1875
1876 // remove task from its successors' predecessor list
1877 for(size_t i=0; i<task._node->_num_successors; ++i) {
1878 task._node->_edges[i]->_remove_predecessors(task._node);
1879 }
1880
1881 // remove task from its precedessors' successor list
1882 for(size_t i=task._node->_num_successors; i<task._node->_edges.size(); ++i) {
1883 task._node->_edges[i]->_remove_successors(task._node);
1884 }
1885
1886 _graph._erase(task._node);
1887}
1888
1889
1890// Procedure: _linearize
1891template <typename L>
1892void FlowBuilder::_linearize(L& keys) {
1893
1894 auto itr = keys.begin();
1895 auto end = keys.end();
1896
1897 if(itr == end) {
1898 return;
1899 }
1900
1901 auto nxt = itr;
1902
1903 for(++nxt; nxt != end; ++nxt, ++itr) {
1904 itr->_node->_precede(nxt->_node);
1905 }
1906}
1907
1908// Procedure: linearize
1909inline void FlowBuilder::linearize(std::vector<Task>& keys) {
1910 _linearize(keys);
1911}
1912
1913// Procedure: linearize
1914inline void FlowBuilder::linearize(std::initializer_list<Task> keys) {
1915 _linearize(keys);
1916}
1917
1918// ----------------------------------------------------------------------------
1919
1956class Subflow : public FlowBuilder {
1957
1958 friend class Executor;
1959 friend class FlowBuilder;
1960
1961 public:
1962
1978 void join();
1979
1995 bool joinable() const noexcept;
1996
2000 Executor& executor() noexcept;
2001
2005 Graph& graph() { return _graph; }
2006
2016 void retain(bool flag) noexcept;
2017
2025 bool retain() const;
2026
2027 private:
2028
2029 Subflow(Executor&, Worker&, Node*, Graph&);
2030
2031 Subflow() = delete;
2032 Subflow(const Subflow&) = delete;
2033 Subflow(Subflow&&) = delete;
2034
2035 Executor& _executor;
2036 Worker& _worker;
2037 Node* _node;
2038};
2039
2040// Constructor
2041inline Subflow::Subflow(Executor& executor, Worker& worker, Node* node, Graph& graph) :
2042 FlowBuilder {graph},
2043 _executor {executor},
2044 _worker {worker},
2045 _node {node} {
2046
2047 // need to reset since there could have iterative control flow
2048 _node->_nstate &= ~(NSTATE::JOINED_SUBFLOW | NSTATE::RETAIN_SUBFLOW);
2049
2050 // clear the graph
2051 graph.clear();
2052}
2053
2054// Function: joinable
2055inline bool Subflow::joinable() const noexcept {
2056 return !(_node->_nstate & NSTATE::JOINED_SUBFLOW);
2057}
2058
2059// Function: executor
2060inline Executor& Subflow::executor() noexcept {
2061 return _executor;
2062}
2063
2064// Function: retain
2065inline void Subflow::retain(bool flag) noexcept {
2066 // default value is not to retain
2067 if(flag == true) {
2068 _node->_nstate |= NSTATE::RETAIN_SUBFLOW;
2069 }
2070 else {
2071 _node->_nstate &= ~NSTATE::RETAIN_SUBFLOW;
2072 }
2073
2074 //_node->_nstate = (_node->_nstate & ~NSTATE::RETAIN_SUBFLOW) |
2075 // (-static_cast<int>(flag) & NSTATE::RETAIN_SUBFLOW);
2076}
2077
2078// Function: retain
2079inline bool Subflow::retain() const {
2080 return _node->_nstate & NSTATE::RETAIN_SUBFLOW;
2081}
2082
2083} // end of namespace tf. ---------------------------------------------------
class to create an empty task parameter for compile-time optimization
Definition graph.hpp:217
class to create an executor
Definition executor.hpp:62
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 inclusive_scan(B first, E last, D d_first, BOP bop)
creates an STL-styled parallel inclusive-scan task
Task transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P part=P())
constructs a parallel-transform task
Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first, C cmp)
merges two sorted ranges into a single sorted output using a custom comparator
Task for_each_by_index(R range, C callable, P part=P())
constructs a parallel-for task over a one- or multi-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:1838
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 over a one- or multi-dimensional index range
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 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 emplace(C &&callable)
creates a static task
Definition flow_builder.hpp:1781
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:1870
Task for_each(B first, E last, C callable, P part=P())
constructs an STL-styled parallel-for task
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:1775
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:1909
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 transform(B first1, E last1, O d_first, C c, P part=P())
constructs a parallel-transform task
Task composed_of(T &object)
creates a module task for the target object
Definition flow_builder.hpp:1831
Task placeholder()
creates a placeholder task
Definition flow_builder.hpp:1856
Task merge(B1 first1, E1 last1, B2 first2, E2 last2, O d_first)
merges two sorted ranges into a single sorted output using the std::less comparator
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:970
class to construct a subflow graph from the execution of a dynamic task
Definition flow_builder.hpp:1956
Executor & executor() noexcept
acquires the associated executor
Definition flow_builder.hpp:2060
Graph & graph()
acquires the associated graph
Definition flow_builder.hpp:2005
void join()
enables the subflow to join its parent task
bool joinable() const noexcept
queries if the subflow is joinable
Definition flow_builder.hpp:2055
bool retain() const
queries if the subflow will be retained after it is joined
Definition flow_builder.hpp:2079
class to create a task handle over a taskflow node
Definition task.hpp:569
class to create a worker in an executor
Definition worker.hpp:55
concept to check if a binary operation is valid
Definition flow_builder.hpp:53
concept to check if a binary transformation operation is valid
Definition flow_builder.hpp:96
concept to check if a type is a stateful input iterator
Definition iterator.hpp:677
concept to check if a type is a partitioner
Definition partitioner.hpp:1138
concept to check if a unary operation is valid
Definition flow_builder.hpp:32
concept to check if a unary transformation operation is valid
Definition flow_builder.hpp:74
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:1108
GuidedPartitioner<> DefaultPartitioner
default partitioner set to tf::GuidedPartitioner
Definition partitioner.hpp:1130