2009-10-10 55 views
22

當我執行以下程序並查看性能計數器時,結果對我來說沒有意義。當我期望〜0.1或〜100時,平均值爲零,最小/最大值爲〜0.4。如何在System.Diagnostics.Stopwatch中使用AverageTimer32和AverageBase性能計數器?

我的問題是什麼?

代碼

class Program 
{ 
    const string CategoryName = "____Test Category"; 
    const string CounterName = "Average Operation Time"; 
    const string BaseCounterName = "Average Operation Time Base"; 

    static void Main(string[] args) 
    { 
     if (PerformanceCounterCategory.Exists(CategoryName)) 
      PerformanceCounterCategory.Delete(CategoryName); 

     var counterDataCollection = new CounterCreationDataCollection(); 

     var avgOpTimeCounter = new CounterCreationData() 
     { 
      CounterName = CounterName, 
      CounterHelp = "Average Operation Time Help", 
      CounterType = PerformanceCounterType.AverageTimer32 
     }; 
     counterDataCollection.Add(avgOpTimeCounter); 

     var avgOpTimeBaseCounter = new CounterCreationData() 
     { 
      CounterName = BaseCounterName, 
      CounterHelp = "Average Operation Time Base Help", 
      CounterType = PerformanceCounterType.AverageBase 
     }; 
     counterDataCollection.Add(avgOpTimeBaseCounter); 

     PerformanceCounterCategory.Create(CategoryName, "Test Perf Counters", PerformanceCounterCategoryType.SingleInstance, counterDataCollection); 

     var counter = new PerformanceCounter(CategoryName, CounterName, false); 
     var baseCounter = new PerformanceCounter(CategoryName, BaseCounterName, false); 

     for (int i = 0; i < 500; i++) 
     { 
      var sw = Stopwatch.StartNew(); 
      Thread.Sleep(100); 
      sw.Stop(); 

      Console.WriteLine(string.Format("t({0}) ms({1})", sw.Elapsed.Ticks, sw.Elapsed.TotalMilliseconds)); 
      counter.IncrementBy(sw.Elapsed.Ticks); 
      baseCounter.Increment(); 
     } 

     Console.Read(); 
    } 
} 

性能計數器截圖 Performance Counter Screenshot http://friendfeed-media.com/50028bb6a0016931a3af5122774b56f93741bb5c

回答

33

的System.Diagnostics程序API包含了極大的混亂的一個非常微妙來源:System.Diagnostics程序 '滴答' 是不一樣的DateTime或TimeSpan'勾號'!

如果您使用StopWatch.ElapsedTicks而不是StopWatch.Elapsed.Ticks,它應該工作。

documentation包含更多信息。

9

Mark Seemann解釋了這個問題的原因,但我想提供一些額外的信息。

如果你想從TimeSpan,而不是一個Stopwatch可以執行下面的轉換設置您AverageTimer32性能計數器:

var performanceCounterTicks = timeSpan.Ticks*Stopwatch.Frequency/TimeSpan.TicksPerSecond; 
averageTimerCounter.IncrementBy(performanceCounterTicks); 
averageTimerCounterBase.Increment(); 
+0

爲什麼你需要鑄造選中((Int32)已.. )? performanceCounterTicks被評估爲long,所有的值實際上都是很長的數字。 – 2013-01-02 15:25:19

+0

@DavideIcardi:謝謝,你說得對'IncrementBy'方法的簽名接受'Int64',所以不需要執行強制轉換。我已經從代碼中刪除了演員。 – 2013-01-02 16:18:34

+0

不錯!正確的我正在尋找! – vtortola 2014-04-24 21:31:05

0

這是一個古老的線程,但我想我會附和我微軟的一位人士告訴我,在使用性能計數器時,我不應該使用TimeSpan,StopWatchDateTime。相反,他建議增加以下本地方法到我的項目:

internal static class NativeMethods 
{ 
    [DllImport("Kernel32.dll")] 
    public static extern void QueryPerformanceCounter(ref long ticks); 
} 

當遞增計數器,他建議這樣做是這樣的:

public void Foo() 
{ 
    var beginTicks = 0L; 

    var endTicks = 0L; 

    NativeMethods.QueryPerformanceCounter(ref beginTicks); 

    // Do stuff 

    NativeMethods.QueryPerformanceCounter(ref endTicks); 

    this.Counter.IncrementBy(endTicks - beginTicks); 
    this.BaseCounter.Increment(); 
} 
+1

他也給出了一個理由嗎? 'StopWatch'只是'QueryPerformanceCounter'上的一個包裝器(如果它不可用,還有一個回退)。如果'StopWatch.IsHighResolution'爲true,'StopWatch.GetTimeStamp()'等同於'QueryPerformanceCounter'。 – CodesInChaos 2014-06-11 17:07:13

+0

他引用了一本關於性能的Microsoft模式和實踐書。他的推理是當你多次執行同樣的動作時,你希望儘可能少的開銷。性能計數器可能會每秒增加很多次。通過使用StopWatch,每次您要測量方法的性能並增加計數器時,都要實例化一個StopWatch對象。這些秒錶對象必須被垃圾收集。通過直接調用QueryPerformanceCounter,你可以剪掉中間人並保存Stopwatch對象的構造和集合。 – RobV8R 2014-06-12 15:35:54

+3

'StopWatch.GetTimeStamp()'是一個靜態方法,它是QueryPerformanceCounter上的一個簡單封裝。唯一的額外費用是靜態字段上的一個分支,它不隨時間而改變,所以分支預測應該工作得很好。 – CodesInChaos 2014-06-21 14:08:34