{"id":2743,"date":"2008-03-12T00:52:00","date_gmt":"2008-03-12T00:52:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2008\/03\/12\/ordering-the-output-of-parallel-computations\/"},"modified":"2008-03-12T00:52:00","modified_gmt":"2008-03-12T00:52:00","slug":"ordering-the-output-of-parallel-computations","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/ordering-the-output-of-parallel-computations\/","title":{"rendered":"Ordering the output of parallel computations"},"content":{"rendered":"<p>Frequently when attempting to do multiple operations in parallel, ordering becomes an issue.&nbsp; Consider an application where I&#8217;m rendering and writing out to a video file frames of a movie:<\/p>\n<blockquote>\n<p class=\"MsoNormal\"><span>for<\/span><span> (<span>int<\/span> i = 0; i &lt; numberOfFrames; i++)<br><\/span><span>{<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>var<\/span> frame = GenerateFrame(i);<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>WriteToMovie(frame);<br><\/span><span>}<\/span><\/p>\n<\/blockquote>\n<p>For a bit of pizzazz, I&#8217;ll show the same thing with LINQ:<\/p>\n<blockquote>\n<p class=\"MsoNormal\"><span>var<\/span><span> frames = <span>from<\/span> i <span>in<\/span> <span>Enumerable<\/span>.Range(0, numberOfFrames)<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>select<\/span> GenerateFrame(i);<br><\/span><span>foreach<\/span><span> (<span>var<\/span> frame <span>in<\/span> frames) WriteToMovie(frame);<\/span><\/p>\n<\/blockquote>\n<p>Now, my GenerateFrame method is expensive and computationally intensive, so I&#8217;d like to generate frames in parallel. For this example, we&#8217;ll assume that my GenerateFrame method is thread-safe and can be called concurrently (rendering one frame doesn&#8217;t modify any state used to render another frame), but access to my WriteToMovie method must be serialized:<\/p>\n<blockquote>\n<p class=\"MsoNormal\"><span>using<\/span><span> (<span>ManualResetEvent<\/span> mre = <span>new<\/span> <span>ManualResetEvent<\/span>(<span>false<\/span>))<br><\/span><span>{<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>int<\/span> count = numberOfFrames;<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>object<\/span> obj = <span>new<\/span> <span>object<\/span>();<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>for<\/span> (<span>int<\/span> i = 0; i &lt; numberOfFrames; i++)<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>ThreadPool<\/span>.QueueUserWorkItem(state =&gt;<span><br><\/span><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>var<\/span> frame = GenerateFrame((<font color=\"#0000ff\">int<\/font>)state);<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><span>lock<\/span><span>(obj) <\/span><span>WriteToMovie(frame);<br><\/span><span><\/p>\n<p>&nbsp;<br><\/p>\n<p><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>if<\/span> (<span>Interlocked<\/span>.Decrement(<span>ref<\/span> count) == 0) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mre.Set();<\/p>\n<p><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}, state);<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>}<br><\/span><span><span>&nbsp;<\/span><span>&nbsp;&nbsp; <\/span>mre.WaitOne();<br><\/span><span>}<\/span><\/p>\n<\/blockquote>\n<p>Unfortunately, that&#8217;s quite a bit of code overhead, and it also suffers from a fundamental ordering problem: the frames may end up being written to the output movie file in an arbitrary order, based on when the frame generation completes.&nbsp; A version with this ordering issue fixed might look like the following:<\/p>\n<blockquote>\n<p class=\"MsoNormal\"><span>var<\/span><span> frames = <span>new<\/span> <span>Bitmap<\/span>[numberOfFrames];<br><\/span><span>var<\/span><span> events = (<span>from<\/span> i <span>in<\/span> <span>Enumerable<\/span>.Range(0, numberOfFrames)<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>select<\/span> <span>new<\/span> <span>ManualResetEvent<\/span>(<span>false<\/span>)).ToArray();<\/p>\n<p><\/span><span>for<\/span><span> (<span>int<\/span> i = 0; i &lt; numberOfFrames; i++)<br><\/span><span>{<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>ThreadPool<\/span>.QueueUserWorkItem(state =&gt;<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>int<\/span> frameNum = (<span>int<\/span>)state;<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>frames[frameNum] = GenerateFrame(frameNum);<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>events[frameNum].Set();<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>}, i);<br><\/span><span>}<br><\/span><span><\/p>\n<p>&nbsp;<br><\/p>\n<p><\/span><span>for<\/span><span> (<span>int<\/span> i = 0; i &lt; numberOfFrames; i++)<br><\/span><span>{<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>events[i].WaitOne();<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>WriteToMovie(frames[i]);<br><\/span><span>}<\/span><\/p>\n<\/blockquote>\n<p>This has the general effect I want, but there are still some unfortunately overheads here (for example, I&#8217;m creating, setting, and waiting on a ManualResetEvent per frame). It&#8217;s also verbose.&nbsp; Instead, I can use Future&lt;T&gt; from <a href=\"https:\/\/msdn2.microsoft.com\/en-us\/concurrency\/default.aspx\">Parallel Extensions<\/a> to solve the same problem:<\/p>\n<blockquote>\n<p class=\"MsoNormal\"><span>var<\/span><span> frames = (<span>from<\/span> i <span>in<\/span> <span>Enumerable<\/span>.Range(0, numberOfFrames)<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>select<\/span> <span>Future<\/span>.Create(() =&gt; GenerateFrame(i))).<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ToArray();<br><\/span><span>foreach<\/span><span> (<span>var<\/span> frame <span>in<\/span> frames) WriteToMovie(frame.Value);<\/span><\/p>\n<\/blockquote>\n<p>I love how close this is to the original LINQ version (and how much less code it is the than my previous ThreadPool sample).&nbsp; Rather than selecting GenerateFrame(i), I&#8217;m selecting Future.Create(() =&gt; GenerateFrame(i)), and rather than calling WriteToMovie(frame), I&#8217;m calling WriteToMovie(frame.Value).&nbsp; With those changes, the frames are now being computed in parallel (note I&#8217;m also using ToArray to calls the entire query to be enumerated), and they&#8217;re being written out to the movie file in the correct order without having to do any explicit locking or coordination.&nbsp; Sweet!<\/p>\n<p>If I wanted to, I could do the same thing without LINQ, tracking the collection of futures explicitly:<\/p>\n<blockquote>\n<p class=\"MsoNormal\"><span>var<\/span><span> frames = <span>new<\/span> <span>Queue<\/span>&lt;<span>Future<\/span>&lt;<span>Bitmap<\/span>&gt;&gt;();<br><\/span><span>for<\/span><span> (<span>int<\/span> i = 0; i &lt; numberOfFrames; i++)<br><\/span><span>{<br>&nbsp;&nbsp;&nbsp; <span>var<\/span><span> <\/span>num = i;<br><\/span><span><span>&nbsp;&nbsp;&nbsp; <\/span>frames.Enqueue(<span>Future<\/span>.Create(() =&gt; GenerateFrame(num)));<br><\/span><span>}<br><\/span><span>while<\/span><span> (frames.Count &gt; 0) WriteToMovie(frames.Dequeue().Value);<\/span><\/p>\n<\/blockquote>\n<p>I could also use PLINQ:<\/p>\n<blockquote>\n<p class=\"MsoNormal\"><span>var<\/span><span> frames = <span>from<\/span> i <span>in<\/span> <span>Enumerable<\/span>.Range(0, numberOfFrames).<br>&nbsp;&nbsp;&nbsp; AsParallel(<span>ParallelQueryOptions<\/span>.PreserveOrdering)<br><\/span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>select<\/span> GenerateFrame(i);<br><\/span><span>foreach<\/span><span> (<span>var<\/span> frame <span>in<\/span> frames) WriteToMovie(frame);<\/span><\/p>\n<\/blockquote>\n<p>Three different approaches to solving the same problem with Parallel Extensions, and all of them requiring less code (and less complicated code) than my ThreadPool example.&nbsp; Just makes you smile, doesn&#8217;t it? \ud83d\ude42<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Frequently when attempting to do multiple operations in parallel, ordering becomes an issue.&nbsp; Consider an application where I&#8217;m rendering and writing out to a video file frames of a movie: for (int i = 0; i &lt; numberOfFrames; i++){&nbsp;&nbsp;&nbsp; var frame = GenerateFrame(i);&nbsp;&nbsp;&nbsp; WriteToMovie(frame);} For a bit of pizzazz, I&#8217;ll show the same thing with [&hellip;]<\/p>\n","protected":false},"author":360,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7911,7909,7913,7910,7912,7914],"class_list":["post-2743","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-code-samples","tag-parallel-extensions","tag-parallelism-blockers","tag-plinq","tag-task-parallel-library","tag-threadpool"],"acf":[],"blog_post_summary":"<p>Frequently when attempting to do multiple operations in parallel, ordering becomes an issue.&nbsp; Consider an application where I&#8217;m rendering and writing out to a video file frames of a movie: for (int i = 0; i &lt; numberOfFrames; i++){&nbsp;&nbsp;&nbsp; var frame = GenerateFrame(i);&nbsp;&nbsp;&nbsp; WriteToMovie(frame);} For a bit of pizzazz, I&#8217;ll show the same thing with [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/2743","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/360"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=2743"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/2743\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=2743"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=2743"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=2743"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}