<!-- iframe showing the search results (closed by default) -->
<divid="MSearchResultsWindow">
<iframesrc="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<divclass="header">
<divclass="headertitle">
<divclass="title">C5: Spawn Task Dependency Graphs at Runtime </div></div>
</div><!--header-->
<divclass="contents">
<divclass="textblock"><p>It is very common for a parallel program to spawn task dependency graphs at runtime. In Cpp-Taskflow, we call this <em>dynamic tasking</em>.</p>
<h1><aclass="anchor" id="Subflow"></a>
Subflow</h1>
<p>Dynamic tasks are those created during the execution of a dispatched graph. These tasks are spawned from a parent task and are grouped together to a <em>subflow</em> dependency graph. Cpp-Taskflow has an unified interface for static and dynamic tasking. To create a subflow for dynamic tasking, emplace a callable that takes one argument of type <aclass="el" href="classtf_1_1SubflowBuilder.html" title="The building blocks of dynamic tasking. ">tf::SubflowBuilder</a>. A <aclass="el" href="classtf_1_1SubflowBuilder.html" title="The building blocks of dynamic tasking. ">tf::SubflowBuilder</a> object will be created during the runtime and passed to the task. All graph building methods you find in taskflow are applicable for a subflow builder.</p>
<divclass="fragment"><divclass="line"> 1: <aclass="code" href="classtf_1_1BasicTaskflow.html">tf::Taskflow</a> taskflow;</div><divclass="line"> 2:</div><divclass="line"> 3: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> A = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"A"</span>); <spanclass="comment">// static task A</span></div><divclass="line"> 4: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> C = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"C"</span>); <spanclass="comment">// static task C</span></div><divclass="line"> 5: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> D = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"D"</span>); <spanclass="comment">// static task D</span></div><divclass="line"> 6:</div><divclass="line"> 7: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] (<aclass="code" href="classtf_1_1SubflowBuilder.html">tf::SubflowBuilder</a>& subflow) { </div><divclass="line"> 8: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B1 = subflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"B1"</span>); <spanclass="comment">// dynamic task B1</span></div><divclass="line"> 9: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B2 = subflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"B2"</span>); <spanclass="comment">// dynamic task B2</span></div><divclass="line">10: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B3 = subflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"B3"</span>); <spanclass="comment">// dynamic task B3</span></div><divclass="line">11: B1.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(B3); <spanclass="comment">// B1 runs bofore B3</span></div><divclass="line">12: B2.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(B3); <spanclass="comment">// B2 runs before B3</span></div><divclass="line">13: }).name(<spanclass="stringliteral">"B"</span>);</div><divclass="line">14:</div><divclass="line">15: A.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(B); <spanclass="comment">// B runs after A</span></div><divclass="line">16: A.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(C); <spanclass="comment">// C runs after A</span></div><divclass="line">17: B.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(D); <spanclass="comment">// D runs after B</span></div><divclass="line">18: C.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(D); <spanclass="comment">// D runs after C</span></div><divclass="line">19:</div><divclass="line">20: taskflow.<aclass="code" href="classtf_1_1BasicTaskflow.html#a848e425f67b49a8a7ac21f6b791999c5">dispatch</a>().get(); <spanclass="comment">// execute the graph without cleanning up topologies</span></div><divclass="line">21: <aclass="codeRef" doxygen="/Users/twhuang/PhD/Code/cpp-taskflow/docs/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << taskflow.<aclass="code" href="classtf_1_1BasicTaskflow.html#af17f2410531dfd08b0e55ef80a8f828d">dump_topologies</a>();</div></div><!-- fragment --><divclass="image">
<li>Line 3-5 creates three tasks, A, C, and D </li>
<li>Line 7-13 creates a task B that spawns a task dependency graph of three tasks B1, B2, and B3 </li>
<li>Line 15-18 add dependencies among A, B, C, and D </li>
<li>Line 20 dispatches the graph and waits until it finishes without cleaning up the topology </li>
<li>Line 21 dumps the topology that represents the entire task dependency graph</li>
</ul>
<p>Line 7-13 is the main coding block to enable dynamic tasking. Cpp-Taskflow uses a <ahref="https://en.cppreference.com/w/cpp/utility/variant">std::variant</a> date type to unify the interface of static tasking and dynamic tasking. The runtime will create a <aclass="el" href="classtf_1_1SubflowBuilder.html" title="The building blocks of dynamic tasking. ">tf::SubflowBuilder</a> passing it to task B, and spawn a dependency graph as described by the associated callable. This new subflow graph will be added to the topology to which its parent task B belongs to. Due to the property of dynamic tasking, we cannot dump its structure before execution. We will need to dispatch the graph first and call the method <aclass="el" href="classtf_1_1BasicTaskflow.html#af17f2410531dfd08b0e55ef80a8f828d" title="dumps the present topologies to a std::ostream in DOT format ">tf::Taskflow::dump_topologies</a>.</p>
<h1><aclass="anchor" id="DetachASubflow"></a>
Detach a Subflow</h1>
<p>By default, a spawned subflow joins its parent task. That is, all nodes of zero outgoing edges in the subflow will precede the parent task. This forces a subflow to follow the dependency constraints after its parent task. Having said that, you can detach a subflow from its parent task, allowing its execution to flow independently.</p>
<divclass="fragment"><divclass="line"> 1: <aclass="code" href="classtf_1_1BasicTaskflow.html">tf::Taskflow</a> taskflow;</div><divclass="line"> 2:</div><divclass="line"> 3: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> A = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"A"</span>); <spanclass="comment">// static task A</span></div><divclass="line"> 4: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> C = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"C"</span>); <spanclass="comment">// static task C</span></div><divclass="line"> 5: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> D = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"D"</span>); <spanclass="comment">// static task D</span></div><divclass="line"> 6:</div><divclass="line"> 7: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] (<aclass="code" href="classtf_1_1SubflowBuilder.html">tf::SubflowBuilder</a>& subflow) { </div><divclass="line"> 8: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B1 = subflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"B1"</span>); <spanclass="comment">// dynamic task B1</span></div><divclass="line"> 9: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B2 = subflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"B2"</span>); <spanclass="comment">// dynamic task B2</span></div><divclass="line">10: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B3 = subflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () {}).name(<spanclass="stringliteral">"B3"</span>); <spanclass="comment">// dynamic task B3</span></div><divclass="line">11: B1.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(B3); <spanclass="comment">// B1 runs bofore B3</span></div><divclass="line">12: B2.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(B3); <spanclass="comment">// B2 runs before B3</span></div><divclass="line">13: subflow.<aclass="code" href="classtf_1_1SubflowBuilder.html#a0660bc0ca5ec375700703cbc94f66942">detach</a>(); <spanclass="comment">// detach this subflow</span></div><divclass="line">14: }).name(<spanclass="stringliteral">"B"</span>);</div><divclass="line">15:</div><divclass="line">16: A.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(B); <spanclass="comment">// B runs after A</span></div><divclass="line">17: A.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(C); <spanclass="comment">// C runs after A</span></div><divclass="line">18: B.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(D); <spanclass="comment">// D runs after B</span></div><divclass="line">19: C.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(D); <spanclass="comment">// D runs after C</span></div><divclass="line">20:</div><divclass="line">21: taskflow.<aclass="code" href="classtf_1_1BasicTaskflow.html#a848e425f67b49a8a7ac21f6b791999c5">dispatch</a>().get(); <spanclass="comment">// execute the graph without cleaning up topologies</span></div><divclass="line">22: <aclass="codeRef" doxygen="/Users/twhuang/PhD/Code/cpp-taskflow/docs/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << taskflow.<aclass="code" href="classtf_1_1BasicTaskflow.html#af17f2410531dfd08b0e55ef80a8f828d">dump_topologies</a>();</div></div><!-- fragment --><p>The figure below demonstrates a detached subflow based on the previous example. A detached subflow will eventually join the end of the topology of its parent task.</p>
<li>Line 3-20 creates a task to spawn a subflow of two tasks A1 and A2 </li>
<li>Line 9-18 spawns another subflow of two tasks A2_1 and A2_2 out of its parent task A2 </li>
<li>Line 23-24 dispatches the graph asynchronously and dump its structure when it finishes</li>
</ul>
<p>Similarly, you can detach a nested subflow from its parent subflow. A detached subflow will run independently and eventually join the topology of its parent subflow. </p>
</div></div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<divid="nav-path" class="navpath"><!-- id is needed for treeview function! -->