2010-03-17 20 views
2

這是進行績效分析的有效方法嗎?我想精確到納秒,並確定強制類型轉換的性能:C#性能分析 - 如何計算CPU週期?

class PerformanceTest 
{ 
    static double last = 0.0; 
    static List<object> numericGenericData = new List<object>(); 
    static List<double> numericTypedData = new List<double>(); 

    static void Main(string[] args) 
    { 
     double totalWithCasting = 0.0; 
     double totalWithoutCasting = 0.0; 
     for (double d = 0.0; d < 1000000.0; ++d) 
     { 
      numericGenericData.Add(d); 
      numericTypedData.Add(d); 
     } 
     Stopwatch stopwatch = new Stopwatch(); 
     for (int i = 0; i < 10; ++i) 
     { 

      stopwatch.Start(); 
      testWithTypecasting(); 
      stopwatch.Stop(); 
      totalWithCasting += stopwatch.ElapsedTicks; 

      stopwatch.Start(); 
      testWithoutTypeCasting(); 
      stopwatch.Stop(); 
      totalWithoutCasting += stopwatch.ElapsedTicks; 
     } 

     Console.WriteLine("Avg with typecasting = {0}", (totalWithCasting/10)); 
     Console.WriteLine("Avg without typecasting = {0}", (totalWithoutCasting/10)); 
     Console.ReadKey(); 
    } 

    static void testWithTypecasting() 
    { 
     foreach (object o in numericGenericData) 
     { 
      last = ((double)o*(double)o)/200; 
     } 
    } 

    static void testWithoutTypeCasting() 
    { 
     foreach (double d in numericTypedData) 
     { 
      last = (d * d)/200; 
     } 
    } 
} 

輸出是:

Avg with typecasting = 468872.3 
Avg without typecasting = 501157.9 

我有點可疑......它看起來像有在幾乎沒有任何影響性能。鑄造真的很便宜嗎?

更新:

class PerformanceTest 
{ 
    static double last = 0.0; 
    static object[] numericGenericData = new object[100000]; 
    static double[] numericTypedData = new double[100000]; 

    static Stopwatch stopwatch = new Stopwatch(); 
    static double totalWithCasting = 0.0; 
    static double totalWithoutCasting = 0.0; 
    static void Main(string[] args) 
    { 
     for (int i = 0; i < 100000; ++i) 
     { 
      numericGenericData[i] = (double)i; 
      numericTypedData[i] = (double)i; 
     } 

     for (int i = 0; i < 10; ++i) 
     { 
      stopwatch.Start(); 
      testWithTypecasting(); 
      stopwatch.Stop(); 
      totalWithCasting += stopwatch.ElapsedTicks; 
      stopwatch.Reset(); 

      stopwatch.Start(); 
      testWithoutTypeCasting(); 
      stopwatch.Stop(); 
      totalWithoutCasting += stopwatch.ElapsedTicks; 
      stopwatch.Reset(); 
     } 

     Console.WriteLine("Avg with typecasting = {0}", (totalWithCasting/(10.0))); 
     Console.WriteLine("Avg without typecasting = {0}", (totalWithoutCasting/(10.0))); 
     Console.ReadKey(); 
    } 

    static void testWithTypecasting() 
    { 
     foreach (object o in numericGenericData) 
     { 
      last = ((double)o * (double)o)/200; 
     } 
    } 

    static void testWithoutTypeCasting() 
    { 
     foreach (double d in numericTypedData) 
     { 
      last = (d * d)/200; 
     } 
    } 
} 

輸出是:

Avg with typecasting = 4791 
Avg without typecasting = 3303.9 
+2

有點旁註。也許它不需要陳述,但是當你正在做這種測試時,確保你在發佈模式下編譯。你不會想象在調試過程中幕後發生的事情。就像我說的,也許它不需要陳述,但它可能值得一提。 – Dested 2010-03-17 23:31:37

+0

@Dested我在釋放和調試模式下試過它,輸出沒有差別。 – Kiril 2010-03-17 23:38:21

+0

類型轉換對我來說看起來差不多是3倍。我錯過了什麼嗎? – 2010-03-17 23:46:47

回答

8

請注意,這並不是說您正在測量,而是取消裝箱。這些值始終是雙倍的,沒有類型轉換正在進行。

您忘記了在測試之間重置秒錶,因此您要重複添加以前所有測試的累計時間。如果您將報價轉換爲實際時間,您會發現它比運行測試所花費的時間多得多。

如果你把每stopwatch.Start();前添加stopwatch.Reset();,你會得到一個更合理的結果,如:

Avg with typecasting = 41027,1 
Avg without typecasting = 20594,3 

拆箱的數值是如此昂貴,它只有檢查對象中的數據類型正確,然後獲得價值。儘管這種類型已經知道了,但還是有很多工作要做。請記住,您還在測量結果的循環,計算和分配,這對兩個測試都是一樣的。

裝箱的價值比拆箱更昂貴,因爲它在堆上分配一個對象。

+0

涵蓋了所有的細節。 – Kiril 2010-03-18 00:30:37

3

1)是的,鑄造通常是(非常)便宜。

2)您不會在託管語言中獲得納秒級精度。或者在大多數操作系統下采用非託管語言。

考慮

  • 其他進程
  • 垃圾收集
  • 不同的JITters
  • 不同的CPU

而且,你的測量包括foreach循環,看起來像50%以上我。也許90%。

+0

你說的沒錯,當我提出在我看到一個顯著的性能差異循環秒錶: 平均類型轉換= 129570.35537 平均無鑄字= 378055.40987 鑄字幾乎是4倍慢! – Kiril 2010-03-17 23:36:49

+0

除此之外,用循環內的Sw來測量秒錶文物。你的結果是否可重複> – 2010-03-17 23:40:49

+0

@亨克是的,我多次運行這個應用程序(發佈和調試模式),它會產生相同的結果。 – Kiril 2010-03-17 23:42:10

1

當你打電話給Stopwatch.Start時,讓定時器繼續從停止的地方繼續運行。在重新開始之前,您需要調用Stopwatch.Reset()將定時器設置回零。就個人而言,我只是使用stopwatch = Stopwatch.StartNew(),每當我想啓動計時器以避免這種混淆。

此外,您可能想在啓動「時序循環」之前調用兩種測試方法,以便讓他們有機會「熱身」這段代碼並確保JIT有機會運行甚至是比賽場地。

當我在我的機器上這樣做時,我發現testWithTypecasting的運行時間約爲testWithoutTypeCasting的一半。

然而,這是說,演員本身不可能是性能處罰的最重要的部分。 testWithTypecasting方法在盒裝雙精度列表上運行,這意味着除了增加總內存消耗量之外,還需要檢索每個值的額外間接級別(請參考內存中其他位置的值)。這增加了花費在內存訪問上的時間,並且可能比「在演員」中花費的CPU時間更大。

0

查看System.Diagnostics名稱空間中的性能計數器,當您創建一個新計數器時,首先創建一個類別,然後指定一個或多個計數器放入其中。

// Create a collection of type CounterCreationDataCollection. 
System.Diagnostics.CounterCreationDataCollection CounterDatas = 
    new System.Diagnostics.CounterCreationDataCollection(); 
// Create the counters and set their properties. 
System.Diagnostics.CounterCreationData cdCounter1 = 
    new System.Diagnostics.CounterCreationData(); 
System.Diagnostics.CounterCreationData cdCounter2 = 
    new System.Diagnostics.CounterCreationData(); 
cdCounter1.CounterName = "Counter1"; 
cdCounter1.CounterHelp = "help string1"; 
cdCounter1.CounterType = System.Diagnostics.PerformanceCounterType.NumberOfItems64; 
cdCounter2.CounterName = "Counter2"; 
cdCounter2.CounterHelp = "help string 2"; 
cdCounter2.CounterType = System.Diagnostics.PerformanceCounterType.NumberOfItems64; 
// Add both counters to the collection. 
CounterDatas.Add(cdCounter1); 
CounterDatas.Add(cdCounter2); 
// Create the category and pass the collection to it. 
System.Diagnostics.PerformanceCounterCategory.Create(
    "Multi Counter Category", "Category help", CounterDatas); 

MSDN docs

0

只是一個想法,但有時相同的機器代碼可以採取不同的循環次數取決於它在內存對齊來執行的,所以你可能要添加一個控制或控制。

0

不要「自己做」C#,而是用C語言編寫x86-32版本,而後來通常可以使用rdtsc指令,這比OS刻度更準確。有關rdtsc的更多信息可以通過搜索stackoverflow找到。在C下,它通常可以作爲內部函數或內置函數使用,並且在計算機啓動後返回時鐘週期數(長8字節/長__ int64 - 無符號整數)。因此,如果CPU的時鐘速度爲3 Ghz,則底層計數器每秒增加30億次。除少數早期的AMD處理器外,所有多核CPU都將使其計數器保持同步。

如果C#沒有它,你可能會考慮寫一個非常短的C函數來從C#訪問它。如果通過函數或內聯訪問指令,會產生很大的開銷。兩個背對背調用函數的區別將成爲基本的測量開銷。如果你正在考慮計算你的應用程序,你將不得不確定幾個更復雜的開銷值。

您可能會考慮關閉CPU的節能模式(並重新啓動PC),因爲它會降低在低活動期間向CPU提供的時鐘頻率。這是因爲它導致不同內核的時間戳計數器變得不同步。