Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Could it be that the inner lambdas are not being compiled?!? Here's a proof of concept:</p> <pre><code>static void UsingCompiledExpressionWithMethodCall() { var where = typeof(Enumerable).GetMember("Where").First() as System.Reflection.MethodInfo; where = where.MakeGenericMethod(typeof(int)); var l = Expression.Parameter(typeof(IEnumerable&lt;int&gt;), "l"); var arg0 = Expression.Parameter(typeof(int), "i"); var lambda0 = Expression.Lambda&lt;Func&lt;int, bool&gt;&gt;( Expression.Equal(Expression.Modulo(arg0, Expression.Constant(2)), Expression.Constant(0)), arg0).Compile(); var c1 = Expression.Call(where, l, Expression.Constant(lambda0)); var arg1 = Expression.Parameter(typeof(int), "i"); var lambda1 = Expression.Lambda&lt;Func&lt;int, bool&gt;&gt;(Expression.GreaterThan(arg1, Expression.Constant(5)), arg1).Compile(); var c2 = Expression.Call(where, c1, Expression.Constant(lambda1)); var f = Expression.Lambda&lt;Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt;&gt;(c2, l); 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 with MethodCall: {0}", tn - t0); } </code></pre> <p>And now the timings are:</p> <pre><code>Using lambda: 625020 Using lambda compiled: 14687970 Using lambda combined: 468765 Using lambda compiled with MethodCall: 468765 </code></pre> <p>Woot! Not only it is fast, it is faster than the native lambda. (<em>Scratch head</em>).</p> <hr> <p>Of course the above code is simply too painful to write. Let's do some simple magic:</p> <pre><code>static void UsingCompiledConstantExpressions() { var f1 = (Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&gt;&gt;)(l =&gt; l.Where(i =&gt; i % 2 == 0)); var f2 = (Func&lt;IEnumerable&lt;int&gt;, IEnumerable&lt;int&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(Expression.Constant(f2), Expression.Invoke(Expression.Constant(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 constant: {0}", tn - t0); } </code></pre> <p>And some timings, VS2010, Optimizations ON, Debugging OFF:</p> <pre><code>Using lambda: 781260 Using lambda compiled: 14687970 Using lambda combined: 468756 Using lambda compiled with MethodCall: 468756 Using lambda compiled constant: 468756 </code></pre> <p>Now you could argue that I'm not generating the whole expression dynamically; just the chaining invocations. But in the above example I generate the whole expression. And the timings match. This is just a shortcut to write less code.</p> <hr> <p>From my understanding, what is going on is that the .Compile() method does not propagate the compilations to inner lambdas, and thus the constant invocation of <code>CreateDelegate</code>. But to truly understand this, I would love to have a .NET guru comment a little about the internal stuff going on.</p> <p>And <em>why</em>, oh <em>why</em> is this now faster than a native lambda!?</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