【问题标题】:Compiled expression tree performance编译表达式树性能
【发布时间】:2017-03-12 19:01:11
【问题描述】:

如果运行following code

using System;
using System.Linq.Expressions;
using System.Diagnostics;

public class E
{
    public double V { get; set; }
}

public class Program
{
    public static void Main()
    {
        E e = new E();
        Func<double> f = () => e.V;

        Expression expr = Expression.Property(Expression.Constant(e), "V");
        Expression<Func<double>> exp = Expression.Lambda<Func<double>>(expr);
        Func<double> ef = exp.Compile();

        e.V = 123;

        int attempts = 5;
        for (int j = 0; j < attempts; j++)
        {
            int c = 100000;

            double[] r1 = new double[c];
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < c; i++)
            {
                r1[i] = f();
            }
            sw.Stop();

            double[] r2 = new double[c];
            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            for (int i = 0; i < c; i++)
            {
                r2[i] = ef();
            }
            sw2.Stop();

            double rat = (double)sw.ElapsedTicks / sw2.ElapsedTicks;

            Console.WriteLine(rat);
        }
    }
}

然后事实证明,编译后的表达式比 lambda 慢得多。是预期的结果吗?是否有可能以某种方式重写为表达式以获得等效的代码,但哪个会更快?

【问题讨论】:

  • 您是否在启用优化的发布模式下进行测试?慢多少?
  • 大约慢 3-5 倍。
  • 最有可能 JIT 内联您的 lambda 并对其执行优化,这是动态生成的方法无法做到的。
  • @Evk AFAIK,CLR 不能内联委托。

标签: c# .net performance lambda expression-trees


【解决方案1】:

您的委托 f 是使用已编译生成的类创建的,该类具有 E 类型的字段 e 和访问值,如下所示:

return <Target>.e.V;

在第二种情况(表达式)中,委托是使用常量指令创建的,该指令使用闭包作为目标,其中 e 是第一个元素。代码可以这样表示:

return ((E)<Target>.Constants[0]).V;

这就是为什么在第一种情况下性能更好。

注意:在 Visual Studio 中使用“监视窗口”,在调试代码时,可以检查“f.Target”和“ef.Target”来确认。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2017-08-19
    • 1970-01-01
    相关资源
    最近更新 更多