2012-07-26 78 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