为什么从表达式动态生成的委托比硬编码的lambdas慢?

时间:2010-08-02 22:48:24

标签: .net performance delegates lambda expression-trees

我希望从表达式树生成的委托可以实现与硬编码,静态,等效匿名方法大致相同的性能。但是,似乎动态生成的委托明显更慢......

这是一个简单的测试程序来说明这种情况。它只访问对象的3个属性1000000次:

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 => f.A;
    getB = f => f.B;
    getC = f => f.C;
    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; }
}

我一直得到的结果表明,硬编码的lambdas快了大约6倍:

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

有人可以解释这些结果吗?那是由于编译器优化吗?还是JIT优化?

感谢您的见解


编辑:我正在使用LINQPad运行我的测试,该编译启用了优化。当我在VS中运行相同的测试且禁用了优化时,在两种情况下我都得到大致相同的结果。因此,似乎编译器只是在硬编码的lambdas中内联属性访问...

奖金问题:有没有办法优化从表达式树生成的代码?

2 个答案:

答案 0 :(得分:3)

只是一个猜测,但我认为优化器看到Lambda表达式是简单的getter并将它们转换为直接访问或类似。生成的表达式无法优化,因此会导致代码变慢。

它应该在编译时发生,因此您应该在禁用优化的情况下尝试并再次检查结果。

答案 1 :(得分:3)

还有其他事情要发生。基准测试严重依赖于您选择的.NET平台目标。我执行重新生成.NET 4.0的结果,但无论构建类型如何,.NET 3.5在生成的版本上都会更快。

猜测4.0中可能发生了哪些变化以使其变得如此之慢太难了,这不是易于分析的代码。并且表达式树支持已经显着改变了。我建议您将反馈发布到connect.microsoft.com,确实需要“按设计”响应。希望注释。