Why are delegates generated dynamically from expressions slower than hard-coded lambdas?

I expected that delegates created from expression trees would achieve roughly the same performance as hard-coded, static equivalent anonymous methods. However, it seems that dynamically created delegates are noticeably slower ...

Here is a simple test program illustrating the case. It simply accesses 3 properties of an object 1,000,000 times:

static void Main() { var foo = new Foo { A = 42, B = "Hello world", C = new DateTime(1970, 1, 1) }; Func<Foo, int> getA; Func<Foo, string> getB; Func<Foo, DateTime> getC; // Using hard-coded lambdas getA = f => fA; getB = f => fB; getC = f => fC; Console.WriteLine("Hard-coded: {0}", Test(foo, getA, getB, getC)); // Using dynamically generated delegates ParameterExpression prm = Expression.Parameter(typeof(Foo), "foo"); getA = Expression.Lambda<Func<Foo, int>>(Expression.Property(prm, "A"), prm).Compile(); getB = Expression.Lambda<Func<Foo, string>>(Expression.Property(prm, "B"), prm).Compile(); getC = Expression.Lambda<Func<Foo, DateTime>>(Expression.Property(prm, "C"), prm).Compile(); Console.WriteLine("Generated: {0}", Test(foo, getA, getB, getC)); } const int N = 1000000; static TimeSpan Test(Foo foo, Func<Foo, int> getA, Func<Foo, string> getB, Func<Foo, DateTime> getC) { var sw = Stopwatch.StartNew(); for (int i = 0; i < N; i++) { getA(foo); getB(foo); getC(foo); } sw.Stop(); return sw.Elapsed; } public class Foo { public int A { get; set; } public string B { get; set; } public DateTime C { get; set; } } 

I consistently get results showing that hard-coded lambdas are about 6 times faster:

 Hard-coded: 00:00:00.0115959 Generated: 00:00:00.0735896 Hard-coded: 00:00:00.0113993 Generated: 00:00:00.0648543 Hard-coded: 00:00:00.0115280 Generated: 00:00:00.0611804 

Can anyone explain these results? Is this related to compiler optimization? or JIT optimization?

Thank you for understanding.


EDIT: I ran my tests with LINQPad, which compiles with optimizations enabled. When I run the same tests in VS with optimizations disabled, I get about the same results in both cases. Thus, it seems that the compiler simply injected access to properties into hard-coded lambdas ...

Bonus question: is there a way to optimize the code generated from expression trees?

+4
source share
2 answers

Just a guess, but I think it's an optimizer that sees that Lambda expressions are simple getters and turn them into direct access or similar. Generated expressions cannot be optimized, so they result in slower code.

This should happen during compilation so that you try to turn off the optimization and check the results again.

+3
source

Something else is happening there. The reference critically depends on the selected .NET Platform Target. I reproduce the results for .NET 4.0, but .NET 3.5 is consistently faster than the generated version, regardless of the type of assembly.

Guess what could change in 4.0 to make it much slower, too complicated, it's not easy to parse the code. And support for the expression tree has changed significantly. I recommend that you send feedback to connect.microsoft.com, expect a design response. Hope annotated.

+3
source

All Articles