| Taskflow: A General-purpose Task-parallel Programming System

Loading...
Searching...
No Matches
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::Taskflow::transform simultaneously applies the callable `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::ref to marshal parameter update between dependent tasks. This is especially useful when the range is unknown at the time of creating a parallel-transform task, but needs initialization from another task.

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);
tf::Task
class to create a task handle over a taskflow node
Definition task.hpp:263
tf::Task::precede
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition task.hpp:947

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
);
tf::GuidedPartitioner
class to create a guided partitioner for scheduling parallel algorithms
Definition partitioner.hpp:254
tf::StaticPartitioner
class to construct a static partitioner for scheduling parallel algorithms
Definition partitioner.hpp:551

Attention
  By default, parallel-transform tasks use tf::DefaultPartitioner if no partitioner is specified.