表达树的表现

cs0*_*815 23 .net c# performance expression-trees

我目前的理解是这样的'硬编码'代码:

public int Add(int x, int y) {return x + y;}
Run Code Online (Sandbox Code Playgroud)

将始终比表达式树代码执行更好,如下所示:

Expression<Func<int, int, int>> add = (x, y) => x + y; 
var result = add.Compile()(2, 3);

var x = Expression.Parameter(typeof(int)); 
var y = Expression.Parameter(typeof(int)); 
return (Expression.Lambda(Expression.Add(x, y), x, y).
    Compile() as Func<int, int, int>)(2, 3);
Run Code Online (Sandbox Code Playgroud)

因为编译器有更多信息,如果在编译时编译它,可以花更多精力优化代码.这一般是正确的吗?

Bas*_*Bas 20

汇编

调用与Expression.Compile您的应用程序包含的任何其他.NET代码完全相同的过程在以下意义上:

  • 生成IL代码
  • IL代码被JIT加载到机器代码

(跳过解析步骤,因为已经创建了表达式树,不必从输入代码生成)

您可以查看表达式编译器的源代码,以验证是否确实生成了IL代码.

优化

请注意,CLR完成的几乎所有优化都是在JIT步骤中完成的,而不是编译C#源代码.在将lambda委托中的IL代码编译为机器代码时,也会进行此优化.

你的榜样

在您的示例中,您正在比较苹果和橙子.第一个示例是方法定义,第二个示例是创建方法,编译和执行它的运行时代码.创建/编译方法所花费的时间比实际执行它要长得多.但是,您可以在创建后保留已编译方法的实例.完成后,生成的方法的性能应与原始C#方法的性能相同.

考虑这种情况:

private static int AddMethod(int a, int b)
{
    return a + b;
}

Func<int, int, int> add1 = (a, b) => a + b;
Func<int, int, int> add2 = AddMethod;

var x = Expression.Parameter(typeof (int));
var y = Expression.Parameter(typeof (int));
var additionExpr = Expression.Add(x, y);
Func<int, int, int> add3 = 
              Expression.Lambda<Func<int, int, int>>(
                  additionExpr, x, y).Compile();
//the above steps cost a lot of time, relatively.

//performance of these three should be identical
add1(1, 2);
add2(1, 2);
add3(1, 2);
Run Code Online (Sandbox Code Playgroud)

因此,可能得出的结论是:IL代码是IL代码,无论它是如何生成的,Linq表达式生成IL代码.


usr*_*usr 5

您的Add函数可能编译为某些函数开销(如果没有内联)和单个add指令.没有比这更快.

即使构造这个表达式树也会慢一个数量级.与直接C#实现相比,为每次调用编译新函数将非常昂贵.

尝试只编译一次该函数并将其存储在某处.