2012-07-26 104 views
5

我有一个简单的场景,我试图在股票对象列表上测试表达式编译树的性能。以下是代码编译表达式树的性能

表达式编译树的性能比静态lambda调用慢5倍。我不确定这是否是表达式编译树所期望的标准性能。希望有任何见解。

LambdaExpression(); 
List<Stock> stocks = new List<Stock>(); 
for (int ctr = 0; ctr <= 5000000; ctr++) 
{ 
    Stock stk1 = new Stock() { Price = ctr, Symbol = "A", CloseDate = DateTime.Now, FaceValue = ctr } ; 
    stocks.Add(stk1); 
} 
CompileTimeLamda(a); 
DynamicLambda(a); 


public static void LambdaExpression() 
{ 
    ParameterExpression CS1 = Expression.Parameter(typeof(Stock), "d"); 

    var line1 = Expression.Equal(Expression.Property(CS1, typeof(Stock).GetProperty("Symbol")), Expression.Constant("MSFT", typeof(string))); 
    var line2 = Expression.GreaterThan(Expression.Property(Expression.Property(CS1, typeof(Stock).GetProperty("CloseDate")),typeof(DateTime).GetProperty("Millisecond")), 
           Expression.Constant(0, typeof(int))); 
    var line3 = Expression.GreaterThan(Expression.Property(CS1, typeof(Stock).GetProperty("Price")), Expression.Constant((double)0, typeof(double))); 
    var line4 = Expression.And(line1,line2); 
    var line5 = Expression.OrElse(line4, line3); 

    func = Expression.Lambda<Func<Stock, bool>>(line5, new ParameterExpression[] { CS1 }).Compile(); 
} 


public static void DynamicLambda(List<Stock> stks) 
{ 
    Stopwatch watch = new Stopwatch(); 
    watch.Start(); 
    foreach (var d in stks) 
    { 
     func(d); 
    } 
    watch.Stop(); 
    Console.WriteLine("Dynamic Lambda :" + watch.ElapsedMilliseconds); 
} 

public static void CompileTimeLamda(List<Stock> stks) 
{ 
    Stopwatch watch = new Stopwatch(); 
    watch.Start(); 
    foreach (var d in stks) 
    { 
     if (d.Symbol == "MSFT" && d.CloseDate.Millisecond > 0 || 
            (d.Price) > 0) ; 
    } 
    watch.Stop(); 
    Console.WriteLine("Compile Time Lamda " +watch.ElapsedMilliseconds); 
} 
+3

检查编译后的IL;优化器可能会杀死所有的代码。 – SLaks 2012-07-26 21:04:36

+0

你实际上并没有比较2个lambda表达式。第二个是编译的代码,即没有委托。代表很可能是让它变慢的原因。 – MikeKulls 2012-07-26 21:38:29

+0

你似乎在比较苹果和橘子。 “编译时lambda”完全不使用lambda。另外,编译器可能会优化掉循环,因为你没有真正做任何事情(空语句“;”) – 2012-07-26 21:39:37

回答

2

的差异与具有编译器的更多信息,并花更多的精力放在优化的代码,如果你在编译时编译它,而不是在运行时...此外,使用Lambda做,你有一个更“灵活”的程序(你可以在运行时选择lambda)。这需要花费额外的函数调用 ,并且会失去很多潜在的优化。

做出更“公平”比较,你可以使用类似比较静态拉姆达VS动态拉姆达:

Func<Stock, bool> compileTime = (Stock d) => (d.Symbol == "MSFT" && d.CloseDate.Millisecond > 0) || d.Price > 0; 

,而不是硬编码代码..

在那里,您还可以找到一个区别,但稍微小一点...差异是出于同样的原因(更多的优化)...你可以通过手工优化你的lambda来减少差异(尽管这并不总是可能的,因为编译器可以创建无法用lambda手动创建的有效CLI代码)。

但是例如,如果从改变你的动态拉姆达:

var line5 = Expression.OrElse(line4, line3); 

到:

var line5 = Expression.OrElse(line3, line4); 

你会看到拉姆达1X和你原来的编译代码2倍之间是如何执行。

5

我做了一些测试,比较了lambda表达式,编译表达式树,直接函数调用和内联代码。结果非常有趣。我几乎认为在我的测试中有一个错误,因为表达式树更快,但我想这不是不可能的。拉姆达表达是最慢的!有趣的是,表达式树比函数调用更快,并且只比内联代码稍慢。不是我所期望的。

编辑:其实我会考虑的Lambda和编译功能,在速度相同的结果如下

void TestIt() 
    { 
     var ints = new int[10000000]; 
     Random rand = new Random(); 
     for (int i = 0; i < ints.Length; i++) 
      ints[i] = rand.Next(100); 

     Func<int, int> func1 = i => i + 2; 
     Func<int, int> func2 = CompileIt(); 

     var stopwatch = new Stopwatch(); 

     for (int x = 0; x < 3; x++) 
     { 
      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = func1(ints[i]); 
      stopwatch.Stop(); 
      Console.Write("Lamba      "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 

      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = func2(ints[i]); 
      stopwatch.Stop(); 
      Console.Write("Lambda from expression tree "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 

      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = AddTwo(ints[i]); 
      stopwatch.Stop(); 
      Console.Write("Compiled function   "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 

      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = ints[i] + 2; 
      stopwatch.Stop(); 
      Console.Write("Compiled code    "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 
     } 
    } 

    private int AddTwo(int value) 
    { 
     return value + 2; 
    } 

    private void ShowSum(int[] ints) 
    { 
     Console.WriteLine(" Sum = " + ints.Sum(i => i).ToString()); 
    } 

    private Func<int, int> CompileIt() 
    { 
     var param1 = Expression.Parameter(typeof(int)); 
     Expression body = Expression.Add(param1, Expression.Constant(2)); 
     return Expression.Lambda<Func<int, int>>(body, new [] { param1 }).Compile(); 
    } 

结果3个运行是:

Lamba      164 Sum = 515074919 
Lambda from expression tree 86 Sum = 535074919 
Compiled function   155 Sum = 555074919 
Compiled code    54 Sum = 575074919 

Lamba      153 Sum = 595074919 
Lambda from expression tree 88 Sum = 615074919 
Compiled function   156 Sum = 635074919 
Compiled code    53 Sum = 655074919 

Lamba      156 Sum = 675074919 
Lambda from expression tree 88 Sum = 695074919 
Compiled function   157 Sum = 715074919 
Compiled code    54 Sum = 735074919