ParallelTransforms

# Parallel Transforms

Include the Header
ParallelTransforms_1ParallelTransformsInclude
Create a Unary Parallel-Transform Task
ParallelTransforms_1ParallelTransformsOverARange
Capture Iterators by Reference
ParallelTransforms_1ParallelTransformsCaptureIteratorsByReference
Create a Binary Parallel-Transform Task
ParallelTransforms_1ParallelBinaryTransformsOverARange
Configure a Partitioner
ParallelTransforms_1ParallelTransformsCfigureAPartitioner

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(),[](inti){
    std::cout<<"transformingitem"<<i<<"to"<<i+1<<'\n';
    returni+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>::iteratorfirst,last,d_first;

    tf::Taskinit=taskflow.emplace([&](){
    src.resize(1000);
    tgt.resize(1000);
    first=src.begin();
    last=src.end();
    d_first=tgt.begin();
    });

    tf::Tasktransform=taskflow.transform(
    std::ref(first),std::ref(last),std::ref(d_first),
    [&](inti){
    std::cout<<"transformingitem"<<i<<"to"<<i+1<<'\n';
    returni+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(),
    [](inti,intj){
    returni+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::StaticPartitionerstatic_partitioner;
    tf::GuidedPartitionerguided_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());

    //createaparallel-transformtaskwithstaticexecutionpartitioner
    taskflow.transform(
    src1.begin(),src1.end(),src2.begin(),tgt1.begin(),
    [](inti,intj){
    returni+j;
    },
    static_partitioner
    );

    //createaparallel-transformtaskwithguidedexecutionpartitioner
    taskflow.transform(
    src1.begin(),src1.end(),src2.begin(),tgt2.begin(),
    [](inti,intj){
    returni+j;
    },
    guided_partitioner
    );

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