<ahref="#Stencil3DConditionTask">Iteration Control with a Condition Task</a>
</li>
<liclass="level1">
<ahref="#Stencil3DDesignPoints">Design Points</a>
</li>
</ul>
</div>
<divclass="textblock"><p>We parallelize a three-dimensional Jacobi stencil over a regular Cartesian grid using <aclass="el" href="classtf_1_1IndexRanges.html" title="class to create an N-dimensional index range of integral indices">tf::IndexRanges</a> and <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>. This example demonstrates how multidimensional index ranges map naturally onto volumetric parallel workloads in scientific computing.</p>
<h1><aclass="anchor" id="Stencil3DProblem"></a>
Problem Formulation</h1>
<p>A stencil computation updates each interior cell of a 3D grid by combining the values of its six face-adjacent neighbors. The 7-point Jacobi stencil used here computes one iteration of the Laplacian smoother:</p>
<p>The computation reads from an input volume <code>u</code> and writes to a separate output volume <code>v</code> so that reads and writes do not interfere. Only interior cells are updated; the one-cell boundary layer is treated as a fixed Dirichlet condition and is never written. The sequential baseline is a straightforward triple loop:</p>
</div><!-- fragment --><p>Each interior cell is independent of every other interior cell within the same sweep, so all updates can be executed in parallel.</p>
<p>We represent the interior iteration space as a <code>tf::IndexRanges<int, 3></code> covering indices <code>[1, Nx-1) x [1, Ny-1) x [1, Nz-1)</code> and dispatch it with <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>. The executor partitions the flat index space into axis-aligned sub-boxes and schedules them across available workers. Within each sub-box the kernel executes the same triple loop as the sequential version:</p>
<divclass="ttc" id="aclasstf_1_1Executor_html_a519777f5783981d534e9e53b99712069"><divclass="ttname"><ahref="classtf_1_1Executor.html#a519777f5783981d534e9e53b99712069">tf::Executor::run</a></div><divclass="ttdeci">tf::Future< void > run(Taskflow &taskflow)</div><divclass="ttdoc">runs a taskflow once</div></div>
<divclass="ttc" id="aclasstf_1_1FlowBuilder_html_a2582a216d54dacca2b7022ea7e89452a"><divclass="ttname"><ahref="classtf_1_1FlowBuilder.html#a2582a216d54dacca2b7022ea7e89452a">tf::FlowBuilder::for_each_by_index</a></div><divclass="ttdeci">Task for_each_by_index(R range, C callable, P part=P())</div><divclass="ttdoc">constructs a parallel-for task over a one- or multi-dimensional index range</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>
<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 --><h1><aclass="anchor" id="Stencil3DMultipleSweeps"></a>
Multiple Sweeps</h1>
<p>A single stencil sweep is rarely sufficient in practice. Iterating for a fixed number of steps requires swapping the input and output buffers after each sweep and re-running the same taskflow. Because <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> captures <code>u</code> and <code>v</code> by reference, swapping the pointers before each <code>run</code> is sufficient:</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_1Taskflow_html"><divclass="ttname"><ahref="classtf_1_1Taskflow.html">tf::Taskflow</a></div><divclass="ttdoc">class to create a taskflow object</div><divclass="ttdef"><b>Definition</b> taskflow.hpp:64</div></div>
</div><!-- fragment --><h1><aclass="anchor" id="Stencil3DConditionTask"></a>
Iteration Control with a Condition Task</h1>
<p>The loop-based approach runs the taskflow from the host and re-enters the executor once per sweep. An alternative is to encode the iteration control entirely inside the task graph using a condition task, so the executor drives the full multi-sweep computation in a single <code>run</code> call.</p>
<p>The graph has two nodes: a parallel stencil task and a condition task that swaps the buffers and decides whether to loop back or stop:</p>
<divclass="ttc" id="aclasstf_1_1FlowBuilder_html_a4d52a7fe2814b264846a2085e931652c"><divclass="ttname"><ahref="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">tf::FlowBuilder::emplace</a></div><divclass="ttdeci">Task emplace(C &&callable)</div><divclass="ttdoc">creates a static task</div><divclass="ttdef"><b>Definition</b> flow_builder.hpp:1781</div></div>
<divclass="ttc" id="aclasstf_1_1Task_html_a331b1b726555072e7c7d10941257f664"><divclass="ttname"><ahref="classtf_1_1Task.html#a331b1b726555072e7c7d10941257f664">tf::Task::succeed</a></div><divclass="ttdeci">Task & succeed(Ts &&... tasks)</div><divclass="ttdoc">adds precedence links from other tasks to this</div><divclass="ttdef"><b>Definition</b> task.hpp:1313</div></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>The condition task returns <code>0</code> to jump back to <code>stencil</code> for the next sweep, or <code>1</code> to fall through and let the graph terminate. The back-edge from <code>check</code> to <code>stencil</code> forms the loop; Taskflow's scheduler re-activates <code>stencil</code> each time the condition returns <code>0</code> and the executor drives the entire multi-sweep computation in a single <code>run</code> call without re-entering from the host.</p>
<divclass="dotgraph">
<iframescrolling="no" frameborder="0" src="dot_stencil3D_taskflow.svg" width="404" height="64"><p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p></iframe></div>
<dlclass="section note"><dt>Note</dt><dd>The condition task runs on a single worker thread, so <code>u</code>, <code>v</code>, and <code>iter</code> can be plain (non-atomic) variables. The stencil task is fully complete before the condition task runs, so the swap and the counter increment are always sequenced correctly.</dd></dl>
<p>There are a few important design points worth noting for this example, which also apply generally to parallel stencil algorithms:</p>
<ul>
<li>Double buffering: Reading from <code>u</code> and writing to <code>v</code> prevents read-write races between workers. A single-buffer update would require a reduction or synchronization point between every pair of updated cells, which eliminates the parallelism.</li>
<li>Interior-only range: The <code>tf::IndexRanges<int, 3></code> is defined over <code>[1, N-1)</code> in each dimension, not <code>[0, N)</code>. This means the boundary halo is never written inside the parallel kernel, so there is no need for conditional boundary checks inside the inner loop. The boundary conditions are enforced implicitly by never touching those cells.</li>
<li>Partitioner choice: A regular stencil has uniform work per cell, so <aclass="el" href="classtf_1_1StaticPartitioner.html" title="class to construct a static partitioner for scheduling parallel algorithms">tf::StaticPartitioner</a> delivers the best performance. It assigns each worker a pre-determined contiguous sub-box of the iteration space with no synchronization overhead during execution. For workloads where the per-cell cost varies (adaptive meshes, masked stencils), <aclass="el" href="classtf_1_1GuidedPartitioner.html" title="class to create a guided partitioner for scheduling parallel algorithms">tf::GuidedPartitioner</a> or <aclass="el" href="classtf_1_1DynamicPartitioner.html" title="class to create a dynamic partitioner for scheduling parallel algorithms">tf::DynamicPartitioner</a> may be more appropriate.</li>
</ul>
<dlclass="section note"><dt>Note</dt><dd>When scheduling a multi-dimensional range like <code>tf::IndexRanges<int, 3></code>, Taskflow partitions the flat index space into axis-aligned sub-boxes rather than arbitrary flat ranges. Each sub-box delivered to the kernel is a geometrically contiguous region of the grid, which preserves spatial locality and benefits from cache reuse across the innermost dimension. </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! -->
<ul>
<liclass="navelem"><aclass="el" href="Examples.html">Learning from Examples</a></li>
<liclass="footer">
Maintained by <ahref="https://tsung-wei-huang.github.io/">Dr. Tsung-Wei Huang</a>
—
Generated by <ahref="https://www.doxygen.org/index.html"><imgclass="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.13.1