{"id":5208,"date":"2017-03-03T21:51:01","date_gmt":"2017-03-03T21:51:01","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/task-blocks\/"},"modified":"2023-06-26T12:20:24","modified_gmt":"2023-06-26T12:20:24","slug":"task-blocks","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/task-blocks\/","title":{"rendered":"Task Blocks"},"content":{"rendered":"<p>Task blocks use the well-known fork-join paradigm for the parallel execution of tasks.<\/p>\n<p><!--more--><\/p>\n<p>Who invented it in C++? Microsoft, with its <a href=\"https:\/\/en.wikipedia.org\/wiki\/Parallel_Patterns_Library\">Parallel Patterns Library<\/a> (PPL), and Intel, with its <a href=\"https:\/\/en.wikipedia.org\/wiki\/Threading_Building_Blocks\">Threading Building Blocks<\/a> (TBB), were involved in proposal <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2015\/n4411.pdf\">N4441<\/a>. Additionally, Intel used its experience with its <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cilk\">Cilk Plus <\/a>library.<\/p>\n<p>The name fork-join is relatively easy to explain.<\/p>\n<h2>Fork and join<\/h2>\n<p>The most straightforward approach to the fork-join paradigm is graphic.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5190\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/ForkJoin.png\" alt=\"ForkJoin\" width=\"700\" height=\"178\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/ForkJoin.png 1271w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/ForkJoin-300x76.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/ForkJoin-1024x261.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/ForkJoin-768x196.png 768w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>How does it work?<\/p>\n<p>The creator invokes <span style=\"font-family: courier new,courier;\">define_task_block<\/span> or <span style=\"font-family: courier new,courier;\">define_task_block_restore_thread<\/span>.&nbsp; This call creates a task block that can create tasks or wait for completion. The synchronization is at the end of the task block. Creating a new task is the fork phase; the synchronization of the task blocks the join phase of the process. Admittedly, that was easy. Let&#8217;s have a look at a piece of code.&nbsp;<\/p>\n<div style=\"background: #ffffff none repeat scroll 0% 0%; overflow: auto; width: auto; border-width: 0.1em 0.1em 0.1em 0.8em;\">\n<table style=\"width: 525px; height: 186px;\">\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0px; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0px; line-height: 125%;\"><span style=\"color: #0000ff;\">template<\/span> &lt;<span style=\"color: #0000ff;\">typename<\/span> Func&gt; \r\n<span style=\"color: #2b91af;\">int<\/span> traverse(node&amp; n, Func &amp;&amp; f){ \r\n    <span style=\"color: #2b91af;\">int<\/span> left = 0, right = 0; \r\n    define_task_block(                 \r\n        [&amp;](task_block&amp; tb){ \r\n            <span style=\"color: #0000ff;\">if<\/span> (n.left) tb.run([&amp;]{ left = traverse(*n.left, f); }); \r\n            <span style=\"color: #0000ff;\">if<\/span> (n.right) tb.run([&amp;]{ right = traverse(*n.right, f); });\r\n         }\r\n    );                                                         \r\n    <span style=\"color: #0000ff;\">return<\/span> f(n) + left + right; \r\n} <\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: courier new,courier;\">traverse<\/span> is a function template that invokes on each node of its tree the function <span style=\"font-family: Courier New,Courier,monospace;\">func<\/span>. The keyword <span style=\"font-family: courier new,courier;\">define_task_block<\/span> defines the task block. The task block <span style=\"font-family: Courier New,Courier,monospace;\">tb <\/span>can start a new task in this block. Exactly that happens at the left and right branches of the tree in lines 6 and 7. Line 9 is the end of the task block and, hence, the synchronization point.<\/p>\n<p>That was my first overview. Now I will write about the missing details of the definition of a task block; the task blocks itself, its interface, and the scheduler.<\/p>\n<\/p>\n<h2>Task Blocks<\/h2>\n<p>You can define a task block by using one of both functions <span style=\"font-family: courier new,courier;\">define_task_block<\/span> or <span style=\"font-family: courier new,courier;\">define_task_block_restore_thread<\/span>.<\/p>\n<h3>define_task_block versus define_task_block_restore_thread<\/h3>\n<p>The subtle difference is that <span style=\"font-family: courier new,courier;\">define_task_block_restore_thread<\/span> guarantees that the creator thread of the task block is the same thread that will run after the task block.<\/p>\n<div style=\"background: #ffffff none repeat scroll 0% 0%; overflow: auto; width: auto; border-width: 0.1em 0.1em 0.1em 0.8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0px; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0px; line-height: 125%;\">...\r\ndefine_task_block([&amp;](<span style=\"color: #0000ff;\">auto<\/span>&amp; tb){  \r\n  tb.run([&amp;]{[] func(); });\r\n  define_task_block_restore_thread([&amp;](<span style=\"color: #0000ff;\">auto<\/span>&amp; tb){\r\n    tb.run([&amp;]([]{ func2(); });\r\n    define_task_block([&amp;](<span style=\"color: #0000ff;\">auto<\/span>&amp; tb){\r\n       tb.run([&amp;]{ func3(); }\r\n    });\r\n    ...\r\n    ...\r\n  });\r\n  ...\r\n  ...\r\n});\r\n...\r\n...\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Task Bocks guarantee that the creator thread of the outermost task block (lines 2 &#8211; 14) is the same thread that will run the statements after finishing the task block. That means that the thread that executes line 2 is the same thread that executes lines 15 and 16. This guarantee will not hold for nested task blocks. Therefore, the creator thread of the task block in lines 6 &#8211; 8 will not automatically perform lines 9 and 10. Use the function define_task_block_restore_thread (line 4) if you need that guarantee. Now it holds that the creator thread performing line 4 is the same thread performing lines 12 and 13.<\/p>\n<h3>task_block<\/h3>\n<p><em>To make you not crazy, I will distinguish between the task block and task_block in this section. I mean with a task block created by one of the two functions <span style=\"font-family: courier new,courier;\">define_task_block<\/span> or <span style=\"font-family: courier new,courier;\">define_task_block_restore_thread<\/span>. In contrast,&nbsp;<span style=\"font-family: courier new,courier;\">task_block tb <\/span>is the object that can start via <span style=\"font-family: courier new,courier;\">tb.run<\/span> new tasks.<\/em><\/p>\n<p><em>A<\/em><span style=\"font-family: courier;\"> task_block <\/span><span style=\"font-family: courier;\"><span style=\"font-family: Helvetica,Arial,sans-serif;\">has a very limited interface. <\/span><\/span>You can not explicitly define it. You have to use one of the two functions <span style=\"font-family: courier new,courier;\">define_task_block<\/span> or <span style=\"font-family: courier new,courier;\">define_task_block_restore_thread<\/span>.<span style=\"font-family: courier new,courier;\"> <\/span>The <span style=\"font-family: courier new,courier;\">task_block tb<\/span> is in the scope of its task block active and can start new tasks (<span style=\"font-family: courier new,courier;\">tb.run)<\/span> or wait (<span style=\"font-family: courier new,courier;\">tb.wait)<\/span> until a task is done.<\/p>\n<div style=\"background: #ffffff none repeat scroll 0% 0%; overflow: auto; width: auto; border-width: 0.1em 0.1em 0.1em 0.8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0px; line-height: 125%;\">1\r\n2\r\n3\r\n4\r\n5<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0px; line-height: 125%;\">define_task_block([&amp;](<span style=\"color: #0000ff;\">auto<\/span>&amp; tb){  \r\n  tb.run([&amp;]{ process(x1, x2) });\r\n  <span style=\"color: #0000ff;\">if<\/span> (x2 == x3) tb.wait();\r\n  process(x3, x4);\r\n});\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>What is the code snippet doing? A new task is started in line 2. This task needs the data <span style=\"font-family: courier new,courier;\">x1<\/span> and <span style=\"font-family: courier new,courier;\">x2<\/span>. In line 4, the data <span style=\"font-family: courier new,courier;\">x3<\/span> and <span style=\"font-family: courier new,courier;\">x4<\/span> are used. If <span style=\"font-family: courier new,courier;\">x2 == x3<\/span> is true, the variable has to be protected from shared access. This is why the task block <span style=\"font-family: courier new,courier;\">tb<\/span> now waits until the task in line 2 is done.<\/p>\n<h2>The scheduler<\/h2>\n<p>The scheduler takes care of which thread is running. This is no more in the responsibility of the programmer. Therefore, threads are exactly reduced to their minimum usage: an implementation detail. Two strategies for the tasks are started by the task block <span style=\"font-family: Courier New,Courier,monospace;\">tb.run <\/span>call. The parent stands for the creator thread, and the child for the new task.<\/p>\n<p><strong>Child stealing<\/strong>: The scheduler steals the task and executes it.<\/p>\n<p><strong>Parent stealing<\/strong>: The task block <span style=\"font-family: courier new,courier;\">tb<\/span> itself executed the task. The scheduler now steals the parent.<\/p>\n<p>Proposal <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2015\/n4411.pdf\">N4441<\/a> is open for both strategies.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>At first, I want to have a closer look at concepts. My post &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/concepts-lite\">Concepts<\/a>&#8221; gave you a first idea of the concepts in C++20. In my <a href=\"https:\/\/www.modernescpp.com\/index.php\/concepts-placeholders\">next post<\/a>, I will write about placeholder syntax and the definition of concepts.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"moz-signature\">&nbsp;<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Task blocks use the well-known fork-join paradigm for the parallel execution of tasks.<\/p>\n","protected":false},"author":21,"featured_media":5190,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[367],"tags":[482],"class_list":["post-5208","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-multithreading-c-17-and-c-20","tag-outdated"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5208","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/comments?post=5208"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5208\/revisions"}],"predecessor-version":[{"id":6880,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5208\/revisions\/6880"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5190"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}