X Tutup
/* 2019/02/25 - contributed by Paolo Bolzoni cpp-taskflow works on directed acyclic graphs. And here we want to pass information between the flow elements. To do so, we see the cpp-taskflow arcs as objects memory where the functions on the nodes read from or write to. The function on every node will *read from* the objects of memory of its incoming edges and *write to* the objects of its outcoming edges. The cpp-taskflow semantics ensures the synchronization. Nodes without incoming edges will require the input from somewhere else; instead nodes without outcoming edges have to execute some side effects to be useful. In this example we fill up (in parallel) two vectors of the results of a fair percentile die and we pick up the maximum values from each cell, and output the result. .----------------. | fill in vector |----| '----------------' |->.-------------. .-----------------. | pick up max |---->| print in stdout | .----------------. |->'-------------' '-----------------' | fill in vector |----| '----------------' The output will be twenty random integer between 1 and 100, that are clearly not uniform distributed as they favor larger numbers. The code assumes the taskflow is executed once, when using the Framework feature the programmer needs care to keep the invariants. It is then suggested to use const references (eg., vector const&) for the objects related to the incoming arcs and references for outcoming ones. */ #include //All those includes are just to init the mersenne twister #include #include #include #include //until here #include #include std::mt19937 init_mersenne_twister() { std::array seed_bits{}; std::random_device real_random{}; std::generate(seed_bits.begin(), seed_bits.end(), std::ref(real_random)); std::seed_seq wrapped_seed_bits(seed_bits.begin(), seed_bits.end()); return std::mt19937(wrapped_seed_bits); } class Fill_in_vector { public: Fill_in_vector(std::vector& v, int length) : v_{v}, length_{length} {} void operator()() { auto rng = init_mersenne_twister(); std::uniform_int_distribution percentile_die(1, 100); //the taskflow is used only once, so we can mess up with length_ value while (length_ > 0) { --length_; v_.push_back( percentile_die(rng) ); } } private: std::vector& v_; int length_; }; class Pick_up_max { public: Pick_up_max(std::vector& in1, std::vector& in2, std::vector& out) : in1_{in1}, in2_{in2}, out_{out} {} void operator()() { for (std::vector::size_type i{}, e = in1_.size(); i < e; ++i) { in1_[i] = std::max(in1_[i], in2_[i]); } // the taskflow is executed once, so we avoid one copy out_.swap(in1_); } private: std::vector& in1_; std::vector& in2_; std::vector& out_; }; class Print { public: Print(std::vector const& v) : v_{v} {} void operator()() { bool first{ true }; for (auto i : v_) { if (!first) { std::cout << ", "; } std::cout << i; first = false; } std::cout << "\n"; } private: std::vector const& v_; }; int main() { // Set up the memory for the arcs std::vector in1{}, in2{}, out{}; // Prepare the functors for taskflow tf::Taskflow tf; auto [ fill_in_vector1, fill_in_vector2, pick_up_max, print ] = tf.emplace( Fill_in_vector(in1, 20), Fill_in_vector(in2, 20), Pick_up_max(in1, in2, out), Print(out) ); // Set up dependencies fill_in_vector1.precede(pick_up_max); fill_in_vector2.precede(pick_up_max); pick_up_max.precede(print); // Execution tf.wait_for_all(); return 0; }
X Tutup