+<span class="n">tf</span><span class="o">::</span><span class="n">Executor</span><span class="w"> </span><span class="nf">executor2</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span><span class="w"> </span><span class="c1">// create an executor of 4 worker threads</span></pre><aside class="m-note m-warning"><h4>Attention</h4><p>Creating a <a href="classtf_1_1Executor.html" class="m-doc">tf::<wbr />Executor</a> has non-negligible overhead. Unless your application requires multiple executors, we recommend creating a single <a href="classtf_1_1Executor.html" class="m-doc">tf::<wbr />Executor</a> and reusing it to run multiple taskflows.</p></aside></section><section id="UnderstandWorkStealingInExecutor"><h2><a href="#UnderstandWorkStealingInExecutor">Understand Work-stealing in Executor</a></h2><p>Taskflow designs a highly efficient <em>work-stealing</em> algorithm to schedule and run tasks in an executor. Work-stealing is a dynamic scheduling algorithm widely used in parallel computing to distribute and balance workload among multiple threads or cores. Specifically, within an executor, each worker maintains its own local queue of tasks. When a worker finishes its own tasks, instead of becoming idle or going sleep, it (thief) tries to <em>steal</em> a task from the queue another worker (victim). The figure below illustrates the idea of work-stealing:</p><img class="m-image" src="work-stealing.png" alt="Image" /><p>The key advantage of work-stealing lies in its <em>decentralized</em> nature and efficiency. Most of the time, worker threads work on their local queues without contention. Stealing only occurs when a worker becomes idle, minimizing overhead associated with synchronization and task distribution. This decentralized strategy effectively balances the workload, ensuring that idle workers are put to work and that the overall computation progresses efficiently.</p><p>That being said, the internal scheduling mechanisms in <a href="classtf_1_1Executor.html" class="m-doc">tf::<wbr />Executor</a> are not trivial, and it's not easy to explain every detail in just a few sentences. If you're interested in learning more about the technical details, please refer to our paper published in 2022 <em>IEEE Transactions on Parallel and Distributed Systems (TPDS)</em>:</p><ul><li>Tsung-Wei Huang, Dian-Lun Lin, Chun-Xun Lin, and Yibo Lin, "<a href="https://tsung-wei-huang.github.io/papers/tpds21-taskflow.pdf">Taskflow: A Lightweight Parallel and Heterogeneous Task Graph Computing System</a>," <em>IEEE Transactions on Parallel and Distributed Systems (TPDS)</em>, vol. 33, no. 6, pp. 1303-1320, June 2022</li></ul></section><section id="ExecuteATaskflow"><h2><a href="#ExecuteATaskflow">Execute a Taskflow</a></h2><p><a href="classtf_1_1Executor.html" class="m-doc">tf::<wbr />Executor</a> provides a set of <code>run_*</code> methods, <a href="classtf_1_1Executor.html#a8d08f0cb79e7b3780087975d13368a96" class="m-doc">tf::<wbr />Executor::<wbr />run</a>, <a href="classtf_1_1Executor.html#af15db5f7dde8e7ff1f86ef8fe825e9e2" class="m-doc">tf::<wbr />Executor::<wbr />run_n</a>, and <a href="classtf_1_1Executor.html#ae4f9e214ea5ee873e8d90a70bc1c77e8" class="m-doc">tf::<wbr />Executor::<wbr />run_until</a> to run a taskflow for one time, multiple times, or until a given predicate evaluates to true. All methods accept an optional callback to invoke after the execution completes, and return a <a href="classtf_1_1Future.html" class="m-doc">tf::<wbr />Future</a> for users to access the execution status. The code below shows several ways to run a taskflow.</p><pre class="m-code"><span class="w"> </span><span class="mi">1</span><span class="o">:</span><span class="w"> </span><span class="c1">// Declare an executor and a taskflow</span>
0 commit comments