Skip to content

Commit 5afc30e

Browse files
committed
Add an example about a way to pass data betweentasks
1 parent d1741c3 commit 5afc30e

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

example/data_flow.cpp

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
3+
cpp-taskflow works on directed acyclic graphs.
4+
And here we want to pass information between the flow elements.
5+
6+
To do so, we see the cpp-taskflow arcs as objects memory where the functions on
7+
the nodes read from or write to.
8+
9+
The function on every node will *read from* the objects of memory of its
10+
incoming edges and *write to* the objects of its outcoming edges.
11+
12+
The cpp-taskflow semantics ensures the synchronization.
13+
14+
15+
Nodes without incoming edges will require the input from somewhere else;
16+
instead nodes without outcoming edges have to execute some side effects to be
17+
useful.
18+
19+
20+
In this example we fill up (in parallel) two vectors of the results of a fair
21+
percentile die and we pick up the maximum values from each cell, and output the
22+
result.
23+
24+
.----------------.
25+
| fill in vector |----|
26+
'----------------' |->.-------------. .-----------------.
27+
| pick up max |---->| print in stdout |
28+
.----------------. |->'-------------' '-----------------'
29+
| fill in vector |----|
30+
'----------------'
31+
32+
The output will be twenty random integer between 1 and 100, that are clearly
33+
not uniform distributed as they favor larger numbers.
34+
35+
The code assumes the taskflow is executed once, when using the Framework
36+
feature the programmer needs care to keep the invariants.
37+
38+
It is then suggested to use const references (eg., vector<int> const&) for the
39+
objects related to the incoming arcs and references for outcoming ones.
40+
41+
*/
42+
43+
44+
#include <taskflow/taskflow.hpp>
45+
46+
//All those includes are just to init the mersenne twister
47+
#include <array>
48+
#include <algorithm>
49+
#include <functional>
50+
#include <random>
51+
//until here
52+
53+
#include <vector>
54+
#include <iostream>
55+
56+
std::mt19937 init_mersenne_twister() {
57+
std::array<std::uint32_t, std::mt19937::state_size> seed_bits{};
58+
std::random_device real_random{};
59+
std::generate(seed_bits.begin(), seed_bits.end(), std::ref(real_random));
60+
std::seed_seq wrapped_seed_bits(seed_bits.begin(), seed_bits.end());
61+
62+
return std::mt19937(wrapped_seed_bits);
63+
}
64+
65+
66+
class Fill_in_vector {
67+
public:
68+
Fill_in_vector(std::vector<int>& v, int length)
69+
: v_{v}, length_{length} {}
70+
71+
void operator()() {
72+
auto rng = init_mersenne_twister();
73+
std::uniform_int_distribution<int> percentile_die(1, 100);
74+
75+
//the taskflow is used only once, so we can mess up with length_ value
76+
while (length_ > 0) {
77+
--length_;
78+
v_.push_back( percentile_die(rng) );
79+
}
80+
}
81+
private:
82+
std::vector<int>& v_;
83+
int length_;
84+
};
85+
86+
87+
class Pick_up_max {
88+
public:
89+
Pick_up_max(std::vector<int>& in1, std::vector<int>& in2, std::vector<int>& out)
90+
: in1_{in1}, in2_{in2}, out_{out} {}
91+
void operator()() {
92+
for (std::vector<int>::size_type i{}, e = in1_.size(); i < e; ++i) {
93+
in1_[i] = std::max(in1_[i], in2_[i]);
94+
}
95+
// the taskflow is executed once, so we avoid one copy
96+
out_.swap(in1_);
97+
}
98+
private:
99+
std::vector<int>& in1_;
100+
std::vector<int>& in2_;
101+
std::vector<int>& out_;
102+
};
103+
104+
105+
class Print {
106+
public:
107+
Print(std::vector<int> const& v)
108+
: v_{v} {}
109+
110+
void operator()() {
111+
bool first{ true };
112+
for (auto i : v_) {
113+
if (not first) {
114+
std::cout << ", ";
115+
}
116+
std::cout << i;
117+
first = false;
118+
}
119+
std::cout << "\n";
120+
}
121+
private:
122+
std::vector<int> const& v_;
123+
};
124+
125+
126+
int main() {
127+
// Set up the memory for the arcs
128+
std::vector<int> in1{}, in2{}, out{};
129+
130+
// Prepare the functors for taskflow
131+
tf::Taskflow tf;
132+
auto [
133+
fill_in_vector1,
134+
fill_in_vector2,
135+
pick_up_max,
136+
print
137+
] = tf.emplace(
138+
Fill_in_vector(in1, 20),
139+
Fill_in_vector(in2, 20),
140+
Pick_up_max(in1, in2, out),
141+
Print(out)
142+
);
143+
144+
// Set up dependencies
145+
fill_in_vector1.precede(pick_up_max);
146+
fill_in_vector2.precede(pick_up_max);
147+
pick_up_max.precede(print);
148+
149+
// Execution
150+
tf.wait_for_all();
151+
152+
return 0;
153+
}
154+

0 commit comments

Comments
 (0)