{"id":55967,"date":"2009-10-30T17:40:00","date_gmt":"2009-10-30T17:40:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2009\/10\/30\/plinq-queries-that-run-sequentially\/"},"modified":"2009-10-30T17:40:00","modified_gmt":"2009-10-30T17:40:00","slug":"plinq-queries-that-run-sequentially","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/plinq-queries-that-run-sequentially\/","title":{"rendered":"PLINQ Queries That Run Sequentially"},"content":{"rendered":"<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">The goal of PLINQ is to execute computationally intensive LINQ to Objects queries efficiently by splitting up the work across multiple cores on multi-core machines. However, not all queries are equally appropriate for parallelism.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Usually, the best way to use PLINQ is to write short, simple queries with an expensive delegate. This is one example of such query:<\/font><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp; <\/span>var q = <\/font><\/span><span><font size=\"3\">src.AsParallel()<\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Where(x =&gt; ExpensiveFilter(x));<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp; <\/span>foreach(var x in q) { &#8230; }<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">And here is another:<\/font><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp; <\/span>int sum =&nbsp;<\/font><\/span><span><font size=\"3\">src.AsParallel()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Select(x =&gt; ExpensiveFunc(x))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Sum();<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">One design goal behind PLINQ is maximum parity with LINQ to Objects. So, you can combine LINQ operators in all kinds of ways, and PLINQ will correctly execute the query. However, performance characteristics get trickier as queries get more complex.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">In some cases, PLINQ may decide to run the query in its entirety or in part sequentially. For example, this query will execute sequentially up to and including the TakeWhile operator:<\/font><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp; <\/span>src.AsParallel()<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.Select(x =&gt; Foo(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.TakeWhile(x =&gt; Filter(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.Select(x =&gt; Bar(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.ToArray();<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">The TakeWhile operator introduces a tricky sequential dependency &ndash; whether an element is or isn&rsquo;t included in the output depends on the result of Filter() on all previous elements in the sequence. There are various ways to execute this query partly in parallel that take different trade-offs. Depending on the selectivity of Filter and the costs of Foo, Bar and Filter, there are different algorithms which may be appropriate.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Whether a particular query executes in part sequentially depends on the combination of operators in the query. The precise rules are fairly complex, but they can be summarized in a simple way. If a query contains one of these operators, PLINQ may decide to run it sequentially:<\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">First, Last<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">Take, Skip<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">TakeWhile, SkipWhile<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">Concat<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">Special overloads of Select, Where and SelectMany that pass position indices into the user delegate<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">ElementAt<\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">Zip<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">For example, if the Take operator follows a Where operator, PLINQ will execute the Where and the Take sequentially by default. Filtering followed by a Take is a tricky operation to parallelize &ndash; different algorithms are appropriate depending on the selectivity of the filter, the size of the argument passed to Take, and other details. However, if the Take is applied straight to an array (or an array followed by a Select), PLINQ can handle the Take operator efficiently simply by restricting execution to a section of the array.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Also, in some queries, SelectMany causes the part of the query that comes before the SelectMany to run sequentially. I haven&rsquo;t seen a practical example where this would be an issue, though. SelectMany produces multiple output elements for each input, and the part of the query before the SelectMany is normally not the computationally expensive part.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">You can prevent PLINQ from falling back to sequential execution by turning on the ForceParallelism mode. In the ForceParallelism mode, PLINQ will always use parallel algorithms to execute the query, even if potentially expensive algorithms will be used. This is desirable if the query contains an expensive delegate:<\/font><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp; <\/span>src.AsParallel()<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.WithExecutionMode(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParallelExecutionMode.ForceParallelism)<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.Select(x =&gt; Foo(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&nbsp;&nbsp;<\/span>.TakeWhile(x =&gt; Filter(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.Select(x =&gt; Bar(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.ToArray();<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Alternatively, you can try breaking up the query so that only the simple but computationally expensive part of the query is done in PLINQ, and the rest of the processing is done in LINQ to Objects:<\/font><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp; <\/span>src.Select(x =&gt; Foo(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.TakeWhile(x =&gt; Filter(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.AsParallel() \/\/ &lt;- only parallelize here<\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.Select(x =&gt; Bar(x))<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>.ToArray();<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">This implementation will scale well if the calls to Bar() represent the bulk of the work in the query. This solution is often preferable, as the part of the query that executes in parallel is clearly marked, and thus the code is easier to understand.<span><\/p>\n<p><\/span><\/font><\/font><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The goal of PLINQ is to execute computationally intensive LINQ to Objects queries efficiently by splitting up the work across multiple cores on multi-core machines. However, not all queries are equally appropriate for parallelism. Usually, the best way to use PLINQ is to write short, simple queries with an expensive delegate. This is one example [&hellip;]<\/p>\n","protected":false},"author":481,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[],"class_list":["post-55967","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam"],"acf":[],"blog_post_summary":"<p>The goal of PLINQ is to execute computationally intensive LINQ to Objects queries efficiently by splitting up the work across multiple cores on multi-core machines. However, not all queries are equally appropriate for parallelism. Usually, the best way to use PLINQ is to write short, simple queries with an expensive delegate. This is one example [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55967","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\/481"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=55967"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55967\/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=55967"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=55967"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=55967"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}