{"id":5452,"date":"2018-06-08T05:36:51","date_gmt":"2018-06-08T05:36:51","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/a-short-detour-executors\/"},"modified":"2023-06-26T11:49:56","modified_gmt":"2023-06-26T11:49:56","slug":"a-short-detour-executors","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/a-short-detour-executors\/","title":{"rendered":"A Short Detour: Executors"},"content":{"rendered":"<p>A few weeks ago, one of the authors of the proposal to the futures in C++ Felix Petriconi wrote me an E-Mail. He said my article about <a href=\"https:\/\/www.modernescpp.com\/index.php\/std-future-extensions\">std::future Extensions<\/a> is quite dated. Honestly, he is right. The future of the futures changed mainly because of the executors.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5450\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/timeline20_23.png\" alt=\"timeline20 23\" width=\"600\" height=\"301\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/timeline20_23.png 862w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/timeline20_23-300x151.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/timeline20_23-768x386.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Before is write about the future of the futures, I have to introduce the concepts of executors.&nbsp; Executors have quite a history in C++. The discussion began at least eight years ago. The details, Detlef Vollmanns gives in his presentation &#8220;<a href=\"http:\/\/www.vollmann.ch\/en\/presentations\/executors2018.pdf\">Finally Executors for C++<\/a>&#8221; a great overview.<\/p>\n<p>This post is mainly based on the proposals for the design of executors <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2018\/p0761r2.pdf\">P0761<\/a> and their formal description <a href=\"http:\/\/open-std.org\/JTC1\/SC22\/WG21\/docs\/papers\/2018\/p0443r7.html\">P0443<\/a>. This post also refers to the relatively new &#8220;Modest Executor Proposal&#8221; <a href=\"http:\/\/open-std.org\/JTC1\/SC22\/WG21\/docs\/papers\/2018\/p1055r0.pdf\">P1055<\/a>.<\/p>\n<p>First of all. What are Executors?<\/p>\n<h2>Executors<\/h2>\n<p>Executors are the basic building block for execution in C++ and fulfill a similar role for execution, such as allocators for the containers in C++. In June 2018, many proposals were written for executors, and many design decisions are still open. They are expected to be part of C++23 but can be used much earlier as an extension of the C++ standard.<\/p>\n<p>An executor consists of rules about <strong>where<\/strong>, <strong>when<\/strong>, and <strong>how<\/strong> to run a callable. A callable can be a function, a function object, or a lambda function.<\/p>\n<ul>\n<li><strong>Where<\/strong>: The callable may run on an internal or external processor, and the result is read back from the internal or external processor.<\/li>\n<li><strong>When<\/strong>: The callable may run immediately or just be scheduled.<\/li>\n<li><strong>How<\/strong>: The callable may run on a CPU or GPU or even be executed in a vectorized way.<\/li>\n<\/ul>\n<p>Because the executors are the building blocks for execution, the concurrency and parallelism features of C++ heavily depend on them. This holds for the new concurrency features in <a href=\"https:\/\/www.modernescpp.com\/index.php\/category\/multithreading-c-17-and-c-20\">C++20\/23<\/a>, such as extended futures, latches and barriers, coroutines, transactional memory, and task blocks. This holds for the extensions for networking but also the&nbsp;<a href=\"https:\/\/www.modernescpp.com\/index.php\/parallel-algorithm-of-the-standard-template-library\">parallel algorithms of the STL<\/a>.<\/p>\n<\/p>\n<h2>First Examples<\/h2>\n<h3>Using an Executor<\/h3>\n<p>Here are a few code snippets showing the usage of the executor <span style=\"font-family: 'courier new', courier;\">my_excutor<\/span>:<\/p>\n<ul>\n<li>The promise <span style=\"font-family: courier new, courier;\">std::async<\/span><\/li>\n<\/ul>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ get an executor through some means<\/span>\r\nmy_executor_type my_executor <span style=\"color: #555555;\">=<\/span> ...\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ launch an async using my executor<\/span>\r\n<span style=\"color: #006699; font-weight: bold;\">auto<\/span> future <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>async(my_executor, [] {\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Hello world, from a new execution agent!\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n});\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<ul>\n<li>The STL algorithm <span style=\"font-family: courier new, courier;\">std::for_each<\/span><\/li>\n<\/ul>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ get an executor through some means<\/span>\r\nmy_executor_type my_executor <span style=\"color: #555555;\">=<\/span> ...\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ execute a parallel for_each \"on\" my executor<\/span>\r\nstd<span style=\"color: #555555;\">::<\/span>for_each(std<span style=\"color: #555555;\">::<\/span>execution<span style=\"color: #555555;\">::<\/span>par.on(my_executor),\r\n              data.begin(), data.end(), func);\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<h3>Obtaining an Executor<\/h3>\n<p>There are various ways to obtain an executor.<\/p>\n<ul>\n<li>From the execution context<span style=\"font-family: courier new, courier;\"> static_thread_pool<\/span><\/li>\n<\/ul>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ create a thread pool with 4 threads<\/span>\r\nstatic_thread_pool <span style=\"color: #cc00ff;\">pool<\/span>(<span style=\"color: #ff6600;\">4<\/span>);\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ get an executor from the thread pool<\/span>\r\n<span style=\"color: #006699; font-weight: bold;\">auto<\/span> exec <span style=\"color: #555555;\">=<\/span> pool.executor();\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ use the executor on some long-running task<\/span>\r\n<span style=\"color: #006699; font-weight: bold;\">auto<\/span> task1 <span style=\"color: #555555;\">=<\/span> long_running_task(exec);\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<ul>\n<li>From the system executor<\/li>\n<\/ul>\n<p style=\"padding-left: 60px;\">This is the default executor that usually uses a thread for the execution. It is used if no another one is specified.<\/p>\n<ul>\n<li>From an executor adapter<\/li>\n<\/ul>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ get an executor from a thread pool<\/span>\r\n<span style=\"color: #006699; font-weight: bold;\">auto<\/span> exec <span style=\"color: #555555;\">=<\/span> pool.executor();\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ wrap the thread pool's executor in a logging_executor<\/span>\r\nlogging_executor<span style=\"color: #555555;\">&lt;<\/span>decltype(exec)<span style=\"color: #555555;\">&gt;<\/span> logging_exec(exec);\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ use the logging executor in a parallel sort<\/span>\r\nstd<span style=\"color: #555555;\">::<\/span>sort(std<span style=\"color: #555555;\">::<\/span>execution<span style=\"color: #555555;\">::<\/span>par.on(logging_exec), my_data.begin(), my_data.end());\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: courier new, courier;\">logging_executor<\/span> is in the code snippet a wrapper for the pool executor.<\/p>\n<h2>Goals of an Executor Concept<\/h2>\n<p>What are the goals of an executor concept according to proposal&nbsp;<a href=\"http:\/\/open-std.org\/JTC1\/SC22\/WG21\/docs\/papers\/2018\/p1055r0.pdf\">P1055<\/a>?<\/p>\n<ol>\n<li><strong>Batchable<\/strong>: control the trade-off between the cost of the transition of the callable and its size of it.<\/li>\n<li><strong>Heterogenous<\/strong>: allow the callable to run on heterogeneous contexts and get the result back.<\/li>\n<li><strong>Orderable<\/strong>: specify the order in which the callables are invoked. The goal includes ordering guarantees such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Stack_(abstract_data_type)\">LIFO <\/a>(<strong>L<\/strong>ast <strong>I<\/strong>n, <strong>F<\/strong>irst <strong>O<\/strong>ut),&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/FIFO_(computing_and_electronics\">FIFO<\/a>&nbsp; (<strong>F<\/strong>irst&nbsp;<strong>I<\/strong>n,&nbsp;<strong>F<\/strong>irst&nbsp;<strong>O<\/strong>ut) execution, priority or time constraints, or sequential execution.<\/li>\n<li><strong>Controllable<\/strong>: the callable must be targetable to a specific compute resource, deferred, or canceled.<\/li>\n<li><strong>Continuable<\/strong>: to control asynchronous callable signals are needed. These signals must indicate whether the result is available, whether an error occurred, when the callable is done, or if the callee wants to cancel the callable. The explicit starting of the callable or stopping the staring should also be possible.<\/li>\n<li><strong>Layerable<\/strong>: hierarchies allow capabilities to be added without increasing the complexity of the simpler use cases.<\/li>\n<li><strong>Usable<\/strong>: ease of use for the implementer and the user should be the main goal.<\/li>\n<li><strong>Composable<\/strong>: allows users to extend the executors for features not part of the standard.<\/li>\n<li><strong>Minimal<\/strong>: nothing should exist on the executor concepts that could be added externally in a library on top of the concept.<\/li>\n<\/ol>\n<h2>Execution Functions<\/h2>\n<p>An executor provides one or more execution functions for creating execution agents from a callable. An executor has to support at least one of the six following functions.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5451\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/ExecutionFunction.png\" alt=\"ExecutionFunction\" width=\"500\" height=\"203\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/ExecutionFunction.png 827w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/ExecutionFunction-300x122.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/06\/ExecutionFunction-768x312.png 768w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/p>\n<p>Each execution function has two properties: cardinality and direction.<\/p>\n<ul>\n<li><strong>Cardinality<\/strong>:\n<ul>\n<li><span style=\"font-family: courier new, courier;\">single<\/span>: creates one execution agent<\/li>\n<li><span style=\"font-family: courier new, courier;\">bulk<\/span>: creates a group of execution agents<\/li>\n<\/ul>\n<\/li>\n<li><strong>Direction<\/strong>:\n<ul>\n<li><span style=\"font-family: courier new, courier;\">oneway<\/span>: creates an execution agent and does not return a result<\/li>\n<li><span style=\"font-family: courier new, courier;\">twoway<\/span>: creates an execution agent and does return a future that can be used to wait for execution to complete<\/li>\n<li><span style=\"font-family: courier new, courier;\">then<\/span>: creates an execution agent and does return a future that can be used to wait for execution to complete. The execution agent begins execution after a given future becomes ready.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Let me explain the execution functions more informally.<\/p>\n<p>First, I refer to the single cardinality case.<\/p>\n<ul>\n<li>A <span style=\"font-family: courier new, courier;\">oneway<\/span> execution function is a fire-and-forget job. It&#8217;s quite similar to a <a href=\"https:\/\/www.modernescpp.com\/index.php\/the-special-futures\">fire-and-forget future<\/a>, but it does not automatically block in the future&#8217;s destructor.<\/li>\n<li>A <span style=\"font-family: courier new, courier;\">twoway<\/span> execution function returns you a future that you can use to pick up the result. This behaves similarly to a <a href=\"https:\/\/www.modernescpp.com\/index.php\/promise-and-future\"><span style=\"font-family: courier new, courier;\">std::promise<\/span><\/a> that gives you back the handle to the associated&nbsp;<span style=\"font-family: courier new, courier;\">std::future<\/span>.<\/li>\n<li>A <span style=\"font-family: courier new, courier;\">then<\/span> execution is a kind of continuation. It gives you a future, but the execution agent runs only if the provided future is&nbsp;<span style=\"font-family: courier new, courier;\">ready<\/span>.<\/li>\n<\/ul>\n<p>Second, the bulk cardinality case is more complicated. These functions create a group of execution agents, and each of these execution agents calls the given callable. They return the result of a factory and not the result of a single callable <span style=\"font-family: courier new, courier;\">f<\/span> invoked by the execution agents. The user is responsible for disambiguating the right result via this factory.<\/p>\n<h3><span style=\"font-family: courier new, courier;\">execution::require<\/span><\/h3>\n<p>How can you be sure that your executor supports the specific execution function?<\/p>\n<p>In the special case, you know it.<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">concrete_context<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> my_oneway_single_executor<span style=\"color: #555555;\">&amp;<\/span> ex)\r\n{\r\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> task <span style=\"color: #555555;\">=<\/span> ...;\r\n    ex.execute(task);\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>In general, you can use the function&nbsp;<span style=\"font-family: courier new, courier;\">execution::require<\/span> to ask for it.<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #006699; font-weight: bold;\">template &lt;typename Executor&gt;<\/span>\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">generic_context<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> Executor<span style=\"color: #555555;\">&amp;<\/span> ex)\r\n{\r\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> task <span style=\"color: #555555;\">=<\/span> ...;\r\n\r\n    <span style=\"color: #0099ff; font-style: italic;\">\/\/ ensure .twoway_execute() is available with execution::require()<\/span>\r\n    execution<span style=\"color: #555555;\">::<\/span>require(ex, execution<span style=\"color: #555555;\">::<\/span>single, execution<span style=\"color: #555555;\">::<\/span>twoway).twoway_execute(task);\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>In this case, the executor <span style=\"font-family: courier new, courier;\">ex<\/span> has to be a single cardinality and two-way direction executor.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>In the <a href=\"https:\/\/www.modernescpp.com\/index.php\/the-end-of-the-detour-unified-futures\">next post<\/a>, I will continue my detour from the C++ core guidelines. The future of the futures changed mainly because of the executors; therefore, I will write about the futures.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A few weeks ago, one of the authors of the proposal to the futures in C++ Felix Petriconi wrote me an E-Mail. He said my article about std::future Extensions is quite dated. Honestly, he is right. The future of the futures changed mainly because of the executors.<\/p>\n","protected":false},"author":21,"featured_media":5450,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[367],"tags":[484,482],"class_list":["post-5452","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-multithreading-c-17-and-c-20","tag-executors","tag-outdated"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5452","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=5452"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5452\/revisions"}],"predecessor-version":[{"id":6824,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5452\/revisions\/6824"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5450"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5452"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5452"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5452"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}