Note that there are some explanatory texts on larger screens.

plurals
  1. POCompiled C# Lambda Expressions Performance
    text
    copied!<p>Consider the following simple manipulation over a collection:</p> <pre><code>static List&lt;int&gt; x = new List&lt;int&gt;() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var result = x.Where(i =&gt; i % 2 == 0).Where(i =&gt; i &gt; 5); </code></pre> <p>Now let's use Expressions. The following code is roughly equivalent:</p> <pre><code>static void UsingLambda() { Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt; lambda = l =&gt; l.Where(i =&gt; i % 2 == 0).Where(i =&gt; i &gt; 5); var t0 = DateTime.Now.Ticks; for (int j = 1; j &lt; MAX; j++) var sss = lambda(x).ToList(); var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda: {0}", tn - t0); } </code></pre> <p>But I want to build the expression on-the-fly, so here's a new test:</p> <pre><code>static void UsingCompiledExpression() { var f1 = (Expression&lt;Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt;&gt;)(l =&gt; l.Where(i =&gt; i % 2 == 0)); var f2 = (Expression&lt;Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt;&gt;)(l =&gt; l.Where(i =&gt; i &gt; 5)); var argX = Expression.Parameter(typeof(IEnumerable&lt;int&gt;), "x"); var f3 = Expression.Invoke(f2, Expression.Invoke(f1, argX)); var f = Expression.Lambda&lt;Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt;&gt;(f3, argX); var c3 = f.Compile(); var t0 = DateTime.Now.Ticks; for (int j = 1; j &lt; MAX; j++) var sss = c3(x).ToList(); var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda compiled: {0}", tn - t0); } </code></pre> <p>Of course it isn't exactly like the above, so to be fair, I modify the first one slightly:</p> <pre><code>static void UsingLambdaCombined() { Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt; f1 = l =&gt; l.Where(i =&gt; i % 2 == 0); Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt; f2 = l =&gt; l.Where(i =&gt; i &gt; 5); Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt; lambdaCombined = l =&gt; f2(f1(l)); var t0 = DateTime.Now.Ticks; for (int j = 1; j &lt; MAX; j++) var sss = lambdaCombined(x).ToList(); var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda combined: {0}", tn - t0); } </code></pre> <p>Now comes the results for MAX = 100000, VS2008, debugging ON:</p> <pre><code>Using lambda compiled: 23437500 Using lambda: 1250000 Using lambda combined: 1406250 </code></pre> <p>And with debugging OFF:</p> <pre><code>Using lambda compiled: 21718750 Using lambda: 937500 Using lambda combined: 1093750 </code></pre> <p><em>Surprise</em>. The compiled expression is roughly 17x slower than the other alternatives. Now here comes the questions:</p> <ol> <li>Am I comparing non-equivalent expressions?</li> <li>Is there a mechanism to make .NET "optimize" the compiled expression?</li> <li>How do I express the same chain call <code>l.Where(i =&gt; i % 2 == 0).Where(i =&gt; i &gt; 5);</code> programatically?</li> </ol> <hr> <p>Some more statistics. Visual Studio 2010, debugging ON, optimizations OFF:</p> <pre><code>Using lambda: 1093974 Using lambda compiled: 15315636 Using lambda combined: 781410 </code></pre> <p>Debugging ON, optimizations ON:</p> <pre><code>Using lambda: 781305 Using lambda compiled: 15469839 Using lambda combined: 468783 </code></pre> <p>Debugging OFF, optimizations ON:</p> <pre><code>Using lambda: 625020 Using lambda compiled: 14687970 Using lambda combined: 468765 </code></pre> <p><em>New Surprise.</em> Switching from VS2008 (C#3) to VS2010 (C#4), makes the <code>UsingLambdaCombined</code> faster than the native lambda.</p> <hr> <p>Ok, I've found a way to improve the lambda compiled performance by more than an order of magnitude. Here's a tip; after running the profiler, 92% of the time is spent on:</p> <pre><code>System.Reflection.Emit.DynamicMethod.CreateDelegate(class System.Type, object) </code></pre> <p>Hmmmm... Why is it creating a new delegate in every iteration? I'm not sure, but the solution follows in a separate post.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload