namespace tf { /** @page ParallelTransforms Parallel Transforms %Taskflow provides template functions for constructing tasks to perform parallel transforms over ranges of items. @tableofcontents @section ParallelTransformsInclude Include the Header You need to include the header file, taskflow/algorithm/transform.hpp, for creating a parallel-transform task. @code{.cpp} #include @endcode @section ParallelTransformsOverARange 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: @code{.cpp} while (first1 != last1) { *d_first++ = c(*first1++); } @endcode tf::Taskflow::transform simultaneously applies the callable @c c to the object obtained by dereferencing every iterator in the range [first1, last1) and stores the result in another range beginning at @c d_first. It is user's responsibility for ensuring the range is valid within the execution of the parallel-transform task. @code{.cpp} std::vector src = {1, 2, 3, 4, 5}; std::vector 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; }); @endcode @section ParallelTransformsCaptureIteratorsByReference 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. @code{.cpp} std::vector src, tgt; std::vector::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); @endcode When @c init finishes, the parallel-transform task @c transform will see @c first pointing to the beginning of @c src and @c last pointing to the end of @c src. Then, it simultaneously transforms these 1000 items by adding one to each element and stores the result in another range starting at @c d_first. @section ParallelBinaryTransformsOverARange 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 @c first1 and @c first2 using the binary operator @c c and store the result in another range pointed by @c d_first. This method is equivalent to the parallel execution of the following loop: @code{.cpp} while (first1 != last1) { *d_first++ = c(*first1++, *first2++); } @endcode 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: @code{.cpp} std::vector src1 = {1, 2, 3, 4, 5}; std::vector src2 = {5, 4, 3, 2, 1}; std::vector tgt(src1.size()); taskflow.transform( src1.begin(), src1.end(), src2.begin(), tgt.begin(), [](int i, int j){ return i + j; } ); @endcode @section ParallelTransformsCfigureAPartitioner 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: @code{.cpp} tf::StaticPartitioner static_partitioner; tf::GuidedPartitioner guided_partitioner; std::vector src1 = {1, 2, 3, 4, 5}; std::vector src2 = {5, 4, 3, 2, 1}; std::vector tgt1(src1.size()); std::vector 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 ); @endcode @note By default, parallel-transform tasks use tf::DefaultPartitioner if no partitioner is specified. */ }