</div><!-- fragment --><h1><aclass="anchor" id="ParallelReductionCreate"></a>
Create a Parallel-Reduction Task</h1>
<p>The task created by <aclass="el" href="classtf_1_1FlowBuilder.html#afb24798ebf46e253a40b01bffb1da6a7" title="constructs an STL-styled parallel-reduction task">tf::Taskflow::reduce(B first, E last, T& result, O bop, P part)</a> performs parallel reduction over the range <code>[first, last)</code> using the binary operator <code>bop</code> and stores the reduced result in <code>result</code>. It represents the parallel execution of the following loop:</p>
<divclass="line"> result = bop(result, *itr);</div>
<divclass="line">}</div>
</div><!-- fragment --><p>At runtime, the reduction task partitions the range among workers, each computing a partial result, and then combines those partial results into <code>result</code> using <code>bop</code>. The initial value of <code>result</code> participates in the reduction — it is combined with the partial results as if it were an additional element. <code>result</code> is captured by reference inside the task; it is the user's responsibility to ensure it remains alive during execution.</p>
<divclass="fragment"><divclass="line"><spanclass="keywordtype">int</span> sum = 100;</div>
<divclass="ttc" id="aclasstf_1_1Task_html"><divclass="ttname"><ahref="classtf_1_1Task.html">tf::Task</a></div><divclass="ttdoc">class to create a task handle over a taskflow node</div><divclass="ttdef"><b>Definition</b> task.hpp:569</div></div>
</div><!-- fragment --><p>The order in which <code>bop</code> is applied to pairs of elements is <em>unspecified</em>. Elements of the range may be grouped and rearranged in arbitrary order, as illustrated below for a sum-reduction over eight elements:</p>
<divclass="dotgraph">
<iframescrolling="no" frameborder="0" src="dot_parallel_reduction.svg" width="798" height="456"><p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p></iframe></div>
<p>The result and argument types of <code>bop</code> must be consistent with the element type.</p>
<p>You can pass iterators by reference using <ahref="https://en.cppreference.com/w/cpp/utility/functional/ref">std::ref</a> to marshal parameter updates between dependent tasks. This is useful when the range is not known at task-graph construction time but is initialized by an upstream task.</p>
<divclass="fragment"><divclass="line"><spanclass="keywordtype">int</span> sum = 100;</div>
<divclass="ttc" id="aclasstf_1_1Task_html_a8c78c453295a553c1c016e4062da8588"><divclass="ttname"><ahref="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">tf::Task::precede</a></div><divclass="ttdeci">Task & precede(Ts &&... tasks)</div><divclass="ttdoc">adds precedence links from this to other tasks</div><divclass="ttdef"><b>Definition</b> task.hpp:1305</div></div>
</div><!-- fragment --><p>When <code>init</code> finishes, <code>first</code> and <code>last</code> point to the initialized data range of <code>vec</code>, and the reduction task performs parallel reduction over the 10 elements.</p>
<p>It is common to transform each element into a new type and then reduce over the transformed values. The task created by <aclass="el" href="classtf_1_1FlowBuilder.html#a5283a732a77ea75446f8ed5d3377f02c" title="constructs an STL-styled parallel transform-reduce task">tf::Taskflow::transform_reduce(B first, E last, T& result, BOP bop, UOP uop, P part)</a> applies the unary operator <code>uop</code> to each element and then performs parallel reduction over <code>result</code> and the transformed values using <code>bop</code>. It represents the parallel execution of the following loop:</p>
<divclass="line"> [](<spanclass="keywordtype">int</span> a, <spanclass="keywordtype">int</span> b) { return a + b; }, <spanclass="comment">// binary reduction operator</span></div>
<divclass="line"> [](<spanclass="keywordtype">char</span> c) -> <spanclass="keywordtype">int</span> { return c - <spanclass="stringliteral">'0'</span>; } <spanclass="comment">// unary transformation operator</span></div>
</div><!-- fragment --><p>The order in which <code>bop</code> is applied to the transformed elements is <em>unspecified</em>. It is possible that <code>bop</code> will receive r-value arguments from both sides (e.g., <code>bop(uop(*itr1), uop(*itr2))</code>) due to transformed temporaries. When data passing is expensive, define the result type <code>T</code> to be move-constructible.</p>
<p>As with <aclass="el" href="classtf_1_1FlowBuilder.html#afb24798ebf46e253a40b01bffb1da6a7" title="constructs an STL-styled parallel-reduction task">tf::Taskflow::reduce</a>, iterators can be passed by reference using <ahref="https://en.cppreference.com/w/cpp/utility/functional/ref">std::ref</a> so that an upstream task can set up the range before the parallel-transform-reduction runs.</p>
<divclass="line"> [](<spanclass="keywordtype">int</span> a, <spanclass="keywordtype">int</span> b) { <spanclass="keywordflow">return</span> a + b; },</div>
<divclass="line"> [](<spanclass="keywordtype">char</span> c) -> <spanclass="keywordtype">int</span> { <spanclass="keywordflow">return</span> c - <spanclass="charliteral">'0'</span>; }</div>
<divclass="line">);</div>
<divclass="line"></div>
<divclass="line"><spanclass="comment">// wrong! first and last are captured by copy at construction time</span></div>
<divclass="line"><spanclass="comment">// tf::Task task = taskflow.transform_reduce(first, last, sum, bop, uop);</span></div>
</div><!-- fragment --><h1><aclass="anchor" id="ParallelReduceByIndexCreate"></a>
Create a Parallel-Reduce-by-Index Task</h1>
<p>Unlike <aclass="el" href="classtf_1_1FlowBuilder.html#afb24798ebf46e253a40b01bffb1da6a7" title="constructs an STL-styled parallel-reduction task">tf::Taskflow::reduce</a>, which gives each worker a single element at a time, <aclass="el" href="classtf_1_1FlowBuilder.html#a3ea810696c4b29824d1aaef15342c825" title="constructs an index range-based parallel-reduction task over a one- or multi-dimensional index range">tf::Taskflow::reduce_by_index</a> gives each worker a contiguous <em>subrange</em> of the index space. This allows the local reduction to be written as an explicit loop over the subrange, enabling optimisations such as SIMD vectorisation, custom accumulator types, or data initialisation interleaved with reduction. The method, <aclass="el" href="classtf_1_1FlowBuilder.html#a3ea810696c4b29824d1aaef15342c825" title="constructs an index range-based parallel-reduction task over a one- or multi-dimensional index range">tf::Taskflow::reduce_by_index</a>, represents the parallel execution of the following two-phase loop:</p>
<divclass="fragment"><divclass="line"><spanclass="comment">// phase 1: each worker computes a partial result over its subrange</span></div>
<divclass="line">T partial = lop(subrange, std::nullopt); <spanclass="comment">// first subrange: no prior total</span></div>
</div><!-- fragment --><p>The local operator <code>lop</code> is invoked once per subrange assigned to a worker. Its second argument is a <code>std::optional<T></code> carrying the running total accumulated by that worker so far:</p>
<ul>
<li><code>std::nullopt</code> on the first subrange processed by a worker — the worker should initialise its accumulator from scratch.</li>
<li>A value on subsequent subranges — the worker should continue accumulating from the provided running total.</li>
</ul>
<p>The global operator <code>gop</code> combines the per-worker partial results and the initial value of <code>result</code> into the final answer.</p>
<dlclass="section user"><dt>Reduce over a One-dimensional (1D) Index Range</dt><dd></dd></dl>
<p>The example below performs a sum-reduction over a large array, initialising each element inside the local reducer:</p>
<divclass="ttc" id="anamespacetf_html_a6c928ec9248757ba8276e316ef26846b"><divclass="ttname"><ahref="namespacetf.html#a6c928ec9248757ba8276e316ef26846b">tf::IndexRange</a></div><divclass="ttdeci">IndexRanges< T, 1 > IndexRange</div><divclass="ttdoc">alias for the common 1D case of tf::IndexRanges</div><divclass="ttdef"><b>Definition</b> iterator.hpp:612</div></div>
</div><!-- fragment --><p>The global reducer combines all partial results with the initial value of <code>res</code> (here <code>1.0</code>), so the final answer is <code>1.0</code> + <code>100000.0</code> = <code>100001.0</code>.</p>
<dlclass="section user"><dt>Reduce over a Multi-dimensional Index Range</dt><dd></dd></dl>
<p><aclass="el" href="classtf_1_1FlowBuilder.html#a3ea810696c4b29824d1aaef15342c825" title="constructs an index range-based parallel-reduction task over a one- or multi-dimensional index range">tf::Taskflow::reduce_by_index</a> also accepts an N-dimensional <code><aclass="el" href="classtf_1_1IndexRanges.html">tf::IndexRanges<T, N></a></code>. The two-phase loop shown above is unchanged — only the type of <code>subrange</code> passed to <code>lop</code> changes: instead of a 1D subrange, <code>lop</code> receives one orthogonal <em>sub-box</em> of the partitioned Cartesian product per invocation, exactly like the callable passed to <aclass="el" href="classtf_1_1FlowBuilder.html#a2582a216d54dacca2b7022ea7e89452a" title="constructs a parallel-for task over a one- or multi-dimensional index range">tf::Taskflow::for_each_by_index</a>. Each dimension of a sub-box is a <code>std::tuple<T, T, T></code> of (begin, end, step_size) accessible through <code>dim(d)</code>, so <code>lop</code> typically destructures it via structured bindings and accumulates over nested loops:</p>
<divclass="ttc" id="aclasstf_1_1IndexRanges_html"><divclass="ttname"><ahref="classtf_1_1IndexRanges.html">tf::IndexRanges</a></div><divclass="ttdoc">class to create an N-dimensional index range of integral indices</div><divclass="ttdef"><b>Definition</b> iterator.hpp:188</div></div>
<divclass="ttc" id="aclasstf_1_1IndexRanges_html_a4e0162b872edd6176e9d6a308b295427"><divclass="ttname"><ahref="classtf_1_1IndexRanges.html#a4e0162b872edd6176e9d6a308b295427">tf::IndexRanges::dim</a></div><divclass="ttdeci">const std::tuple< T, T, T > & dim(size_t d) const</div><divclass="ttdoc">returns the (begin, end, step) tuple for dimension d (read-only)</div><divclass="ttdef"><b>Definition</b> iterator.hpp:297</div></div>
</div><!-- fragment --><p>As with the 1D case, <code>lop</code> is invoked once per sub-box assigned to a worker, its running total resets to <code>std::nullopt</code> at the start of each worker, and <code>gop</code> combines every worker's partial result together with the initial value of <code>res</code>. The same pattern extends to three or more dimensions by adding one more <code><aclass="el" href="namespacetf.html#a6c928ec9248757ba8276e316ef26846b" title="alias for the common 1D case of tf::IndexRanges">tf::IndexRange</a></code> to the constructor of <code><aclass="el" href="classtf_1_1IndexRanges.html" title="class to create an N-dimensional index range of integral indices">tf::IndexRanges</a></code> and one more nested loop (and one more structured binding) inside <code>lop</code>.</p>
<p>If a dimension has zero size, it and all dimensions inner to it contribute no iterations to any sub-box, following the exact same rules described in <aclass="el" href="ParallelIterations.html">Parallel Iterations</a> for <aclass="el" href="classtf_1_1FlowBuilder.html#a2582a216d54dacca2b7022ea7e89452a" title="constructs a parallel-for task over a one- or multi-dimensional index range">tf::Taskflow::for_each_by_index</a>. In particular, if the range is empty overall (<code>range.size() == 0</code>), <aclass="el" href="classtf_1_1FlowBuilder.html#a3ea810696c4b29824d1aaef15342c825" title="constructs an index range-based parallel-reduction task over a one- or multi-dimensional index range">tf::Taskflow::reduce_by_index</a> returns without invoking <code>lop</code> or <code>gop</code> at all, leaving <code>res</code> unchanged.</p>
<dlclass="section user"><dt>Capture IndexRange by Reference</dt><dd></dd></dl>
<p>You can pass the index range by reference using <ahref="https://en.cppreference.com/w/cpp/utility/functional/ref">std::ref</a> so that an upstream task can set the bounds before the parallel-reduce-by-index runs.</p>
<divclass="ttc" id="aclasstf_1_1IndexRanges_html_a253e15e199f974ee26b2e33a5e2b3cf1"><divclass="ttname"><ahref="classtf_1_1IndexRanges.html#a253e15e199f974ee26b2e33a5e2b3cf1">tf::IndexRanges::end</a></div><divclass="ttdeci">T end() const</div><divclass="ttdoc">queries the ending index of the range (only available when N == 1)</div><divclass="ttdef"><b>Definition</b> iterator.hpp:358</div></div>
<divclass="ttc" id="aclasstf_1_1IndexRanges_html_ae37261f0d2f326449c469233561a3da6"><divclass="ttname"><ahref="classtf_1_1IndexRanges.html#ae37261f0d2f326449c469233561a3da6">tf::IndexRanges::begin</a></div><divclass="ttdeci">T begin() const</div><divclass="ttdoc">queries the starting index of the range (only available when N == 1)</div><divclass="ttdef"><b>Definition</b> iterator.hpp:346</div></div>
<divclass="ttc" id="aclasstf_1_1IndexRanges_html_afcb30e2b9567ad685e702201d2265880"><divclass="ttname"><ahref="classtf_1_1IndexRanges.html#afcb30e2b9567ad685e702201d2265880">tf::IndexRanges::step_size</a></div><divclass="ttdeci">T step_size() const</div><divclass="ttdoc">queries the step size of the range (only available when N == 1)</div><divclass="ttdef"><b>Definition</b> iterator.hpp:370</div></div>
</div><!-- fragment --><h1><aclass="anchor" id="ParallelReductionConfigureAPartitioner"></a>
Configure a Partitioner</h1>
<p>A partitioner controls how the iteration space is divided among workers. Taskflow provides four partitioners, each suited to different workload characteristics:</p>
<ul>
<li><aclass="el" href="classtf_1_1StaticPartitioner.html" title="class to construct a static partitioner for scheduling parallel algorithms">tf::StaticPartitioner</a> divides the range into equal-sized chunks ahead of execution and assigns them to workers in order. It has the lowest scheduling overhead and delivers the best performance when every element costs roughly the same amount of work to reduce.</li>
<li><aclass="el" href="classtf_1_1DynamicPartitioner.html" title="class to create a dynamic partitioner for scheduling parallel algorithms">tf::DynamicPartitioner</a> distributes fixed-sized chunks to workers on demand as they become available. It adapts well to workloads where reduction cost varies per element, at the expense of slightly higher coordination overhead.</li>
<li><aclass="el" href="classtf_1_1GuidedPartitioner.html" title="class to create a guided partitioner for scheduling parallel algorithms">tf::GuidedPartitioner</a> distributes chunks whose size decreases adaptively as work is consumed — large chunks early to reduce overhead, smaller chunks late to balance the tail. This is the default partitioner and delivers stable, near-optimal performance across a wide range of workloads.</li>
<li><aclass="el" href="classtf_1_1RandomPartitioner.html" title="class to construct a random partitioner for scheduling parallel algorithms">tf::RandomPartitioner</a> distributes chunks of randomly sampled sizes, which can help avoid systematic load imbalances caused by data-dependent cost patterns.</li>
</ul>
<p>The following example creates two parallel-reduction tasks using different partitioners:</p>
<divclass="ttc" id="aclasstf_1_1GuidedPartitioner_html"><divclass="ttname"><ahref="classtf_1_1GuidedPartitioner.html">tf::GuidedPartitioner</a></div><divclass="ttdoc">class to create a guided partitioner for scheduling parallel algorithms</div><divclass="ttdef"><b>Definition</b> partitioner.hpp:636</div></div>
<divclass="ttc" id="aclasstf_1_1StaticPartitioner_html"><divclass="ttname"><ahref="classtf_1_1StaticPartitioner.html">tf::StaticPartitioner</a></div><divclass="ttdoc">class to construct a static partitioner for scheduling parallel algorithms</div><divclass="ttdef"><b>Definition</b> partitioner.hpp:476</div></div>
</div><!-- fragment --><p>As a rule of thumb, prefer <aclass="el" href="classtf_1_1StaticPartitioner.html" title="class to construct a static partitioner for scheduling parallel algorithms">tf::StaticPartitioner</a> when every element costs the same to reduce (e.g., summation over a plain array) and <aclass="el" href="classtf_1_1GuidedPartitioner.html" title="class to create a guided partitioner for scheduling parallel algorithms">tf::GuidedPartitioner</a> for irregular workloads (e.g., reductions whose cost depends on the element value). <aclass="el" href="classtf_1_1DynamicPartitioner.html" title="class to create a dynamic partitioner for scheduling parallel algorithms">tf::DynamicPartitioner</a> is a good choice when chunks must be kept small and strictly equal in size.</p>
<dlclass="section note"><dt>Note</dt><dd>By default, parallel-reduction tasks use <aclass="el" href="namespacetf.html#ace2c5adcd5039483eebb6dbdbb6f33e3" title="default partitioner set to tf::GuidedPartitioner">tf::DefaultPartitioner</a> (currently <aclass="el" href="classtf_1_1GuidedPartitioner.html" title="class to create a guided partitioner for scheduling parallel algorithms">tf::GuidedPartitioner</a>) if no partitioner is specified. </dd></dl>
</div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<!-- HTML footer for doxygen 1.13.1-->
<!-- start footer part -->
<divid="nav-path" class="navpath"><!-- id is needed for treeview function! -->