2010-03-15 67 views
4

您好我調查創建的CacheDependency對象的性能損失,所以我寫了一個很簡單的測試程序如下:性能委託和方法組

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Web.Caching; 

namespace Test 
{ 
    internal class Program 
    { 
     private static readonly string[] keys = new[] {"Abc"}; 
     private static readonly int MaxIteration = 10000000; 

     private static void Main(string[] args) 
     { 
      Debug.Print("first set"); 
      test7(); 
      test6(); 
      test5(); 
      test4(); 
      test3(); 
      test2(); 
      Debug.Print("second set"); 
      test2(); 
      test3(); 
      test4(); 
      test5(); 
      test6(); 
      test7(); 
     } 

     private static void test2() 
     { 
      DateTime start = DateTime.Now; 
      var list = new List<CacheDependency>(); 
      for (int i = 0; i < MaxIteration; i++) 
      { 
       list.Add(new CacheDependency(null, keys)); 
      } 

      Debug.Print("test2 Time: " + (DateTime.Now - start)); 
     } 

     private static void test3() 
     { 
      DateTime start = DateTime.Now; 
      var list = new List<Func<CacheDependency>>(); 
      for (int i = 0; i < MaxIteration; i++) 
      { 
       list.Add(() => new CacheDependency(null, keys)); 
      } 

      Debug.Print("test3 Time: " + (DateTime.Now - start)); 
     } 

     private static void test4() 
     { 
      var p = new Program(); 
      DateTime start = DateTime.Now; 
      var list = new List<Func<CacheDependency>>(); 
      for (int i = 0; i < MaxIteration; i++) 
      { 
       list.Add(p.GetDep); 
      } 

      Debug.Print("test4 Time: " + (DateTime.Now - start)); 
     } 

     private static void test5() 
     { 
      var p = new Program(); 
      DateTime start = DateTime.Now; 
      var list = new List<Func<CacheDependency>>(); 
      for (int i = 0; i < MaxIteration; i++) 
      { 
       list.Add(() => { return p.GetDep(); }); 
      } 

      Debug.Print("test5 Time: " + (DateTime.Now - start)); 
     } 

     private static void test6() 
     { 
      DateTime start = DateTime.Now; 
      var list = new List<Func<CacheDependency>>(); 
      for (int i = 0; i < MaxIteration; i++) 
      { 
       list.Add(GetDepSatic); 
      } 

      Debug.Print("test6 Time: " + (DateTime.Now - start)); 
     } 

     private static void test7() 
     { 
      DateTime start = DateTime.Now; 
      var list = new List<Func<CacheDependency>>(); 
      for (int i = 0; i < MaxIteration; i++) 
      { 
       list.Add(() => { return GetDepSatic(); }); 
      } 

      Debug.Print("test7 Time: " + (DateTime.Now - start)); 
     } 

     private CacheDependency GetDep() 
     { 
      return new CacheDependency(null, keys); 
     } 

     private static CacheDependency GetDepSatic() 
     { 
      return new CacheDependency(null, keys); 
     } 
    } 
} 

但我不明白爲什麼這些結果看起來像這樣的:

first set 
test7 Time: 00:00:00.4840277 
test6 Time: 00:00:02.2041261 
test5 Time: 00:00:00.1910109 
test4 Time: 00:00:03.1401796 
test3 Time: 00:00:00.1820105 
test2 Time: 00:00:08.5394884 
second set 
test2 Time: 00:00:07.7324423 
test3 Time: 00:00:00.1830105 
test4 Time: 00:00:02.3561347 
test5 Time: 00:00:00.1750100 
test6 Time: 00:00:03.2941884 
test7 Time: 00:00:00.1850106 

特別是:

  1. 爲什麼TEST4和TEST6慢得多 比他們的代表版本?我也 注意到Resharper特別是 有一個評論代表 版本建議改變test5和 test7「祕密方法組」。 與test4和test6 相同,但它們實際上比較慢?
  2. 我在調用 test4和test6時,應該不是靜態的 調用始終更快,我似乎沒有一致的 性能差異?

回答

1

這是相當有趣。我想知道如果你的百萬條目列表不會導致垃圾收集和歪曲你的結果。嘗試改變這些函數被調用的順序,看看結果給你什麼。

另一件事是,JIT可能已經優化了您的代碼,而不是每次都創建lambda表達式,並且只是反覆插入相同的值。可能值得在它上面運行,看看實際產生了什麼。

2

在方法組(4,6)的測試中,C#編譯器不緩存委託(Func)對象。它每次創造新的。在7和5中,它將Action對象緩存到生成的調用方法的方法。因此,從方法組創建Funcs非常緩慢(堆分配),但調用速度很快,因爲動作直接指向您的方法。由於Func被緩存,所以lambda表達式的動作創建速度很快,但它指向生成的方法,因此只有一個不必要的方法調用。

要注意的是並非所有的lambda表達式可以被緩存(關閉打破這一邏輯)

0

爲什麼TEST4和TEST6比他們的代表的版本很慢?我還注意到,Resharper特別對代表版本提出了建議,建議將test5和test7改爲「Covert to method group」。與test4和test6相同,但它們實際上比較慢?

你會加入

 Debug.Print(ReferenceEquals(list[0], list[1]) ? "same" : "different"); 

每個方法的末尾得到一個大線索。

與委託的版本,Func獲取編譯有點像它實際上是:

var func = Func<CacheDependency> <>_hiddenfieldwithinvalidC#name; 
if (func == null) 
{ 
    <>_hiddenfieldwithinvalidC#name = func =() => p.GetDep(); 
} 

雖然用的方法組它得到大致相同的編譯爲:

func = new Func<CacheDependency>(p.GetDep()); 

這memoisation是當編譯器可以確定它是安全的時候,用lambdas創建的委託做了很多工作,但是不能將方法組轉換爲委託,並且你看到的性能差異顯示了原因。

我似乎並沒有一致的性能差異呼籲TEST4和TEST6的時候,不應該靜態調用快是永遠​​?

不一定。雖然靜態調用少了一個參數的優勢,繞過(因爲沒有隱含this參數),這種差異:

  1. 沒有太大的開始。
  2. 如果不使用this,可能會被打散。
  3. 可能被優化了,因爲在調用之前寄存器中的this指針是this指針在調用之後的寄存器,所以沒有必要實際做任何事情來實現它。
  4. 呃,別的。我沒有聲稱這份清單是詳盡的。

真的什麼性能的好處有從靜態的比較,如果你做的是在實例方法自然靜態你可以用周圍的是不是真的需要的對象和浪費時間的過度傳遞結束。也就是說,如果你正在做靜態方法中自然的實例,那麼你最終可以在不需要的參數中存儲/檢索和/或分配和/或傳遞對象,並且同樣不好。