Parallel Transforms
Taskflow provides template functions for constructing tasks to perform parallel transforms over ranges of items.
Include the Header
You need to include the header file, taskflow/algorithm/transform.hpp
, for creating a parallel-transform task.
#include <taskflow/algorithm/transform.hpp>
Create a Unary Parallel-Transform Task
Parallel-transform transforms a range of items, possibly with a different type for the transformed data, and stores the result in another range. The task created by tf::Taskflow::transform(B first1, E last1, O d_first, C c, P&& part) is equivalent to a parallel execution of the following loop:
while (first1 != last1) { *d_first++ = c(*first1++); }
tf::c
to the object obtained by dereferencing every iterator in the range [first1, last1)
and stores the result in another range beginning at d_first
. It is user's responsibility for ensuring the range is valid within the execution of the parallel-transform task.
std::vector<int> src = {1, 2, 3, 4, 5}; std::vector<int> tgt(src.size()); taskflow.transform(src.begin(), src.end(), tgt.begin(), [](int i){ std::cout << "transforming item " << i << " to " << i + 1 << '\n'; return i + 1; });
Capture Iterators by Reference
You can pass iterators by reference using std::
std::vector<int> src, tgt; std::vector<int>::iterator first, last, d_first; tf::Task init = taskflow.emplace([&](){ src.resize(1000); tgt.resize(1000); first = src.begin(); last = src.end(); d_first = tgt.begin(); }); tf::Task transform = taskflow.transform( std::ref(first), std::ref(last), std::ref(d_first), [&](int i) { std::cout << "transforming item " << i << " to " << i + 1 << '\n'; return i+1; } ); init.precede(transform);
When init
finishes, the parallel-transform task transform
will see first
pointing to the beginning of src
and last
pointing to the end of src
. Then, it simultaneously transforms these 1000 items by adding one to each element and stores the result in another range starting at d_first
.
Create a Binary Parallel-Transform Task
You can use the overload, tf::Taskflow::transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P&& part), to perform parallel transforms on two source ranges pointed by first1
and first2
using the binary operator c
and store the result in another range pointed by d_first
. This method is equivalent to the parallel execution of the following loop:
while (first1 != last1) { *d_first++ = c(*first1++, *first2++); }
The following example creates a parallel-transform task that adds two ranges of elements one by one and stores the result in a target range:
std::vector<int> src1 = {1, 2, 3, 4, 5}; std::vector<int> src2 = {5, 4, 3, 2, 1}; std::vector<int> tgt(src1.size()); taskflow.transform( src1.begin(), src1.end(), src2.begin(), tgt.begin(), [](int i, int j){ return i + j; } );
Configure a Partitioner
You can configure a partitioner for parallel-transform tasks to run with different scheduling methods, such as guided partitioning, dynamic partitioning, and static partitioning. The following example creates two parallel-transform tasks using two different partitioners, one with the static partitioning algorithm and another one with the guided partitioning algorithm:
tf::StaticPartitioner static_partitioner; tf::GuidedPartitioner guided_partitioner; std::vector<int> src1 = {1, 2, 3, 4, 5}; std::vector<int> src2 = {5, 4, 3, 2, 1}; std::vector<int> tgt1(src1.size()); std::vector<int> tgt2(src2.size()); // create a parallel-transform task with static execution partitioner taskflow.transform( src1.begin(), src1.end(), src2.begin(), tgt1.begin(), [](int i, int j){ return i + j; }, static_partitioner ); // create a parallel-transform task with guided execution partitioner taskflow.transform( src1.begin(), src1.end(), src2.begin(), tgt2.begin(), [](int i, int j){ return i + j; }, guided_partitioner );