2009-10-10 11 views
22

Wenn ich das folgende Programm ausführe und den Leistungszähler ansehe, ergeben die Ergebnisse für mich keinen Sinn. Der Durchschnittswert ist Null und die Min/Max-Werte sind ~ 0,4, wenn ich ~ 0,1 oder ~ 100 erwarte.Wie AverageTimer32 und AverageBase-Leistungsindikatoren mit System.Diagnostics.Stopwatch?

Was ist mein Problem?

-Code

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 Performance Counter Screenshot http://friendfeed-media.com/50028bb6a0016931a3af5122774b56f93741bb5c

Antwort

33

Die System.Diagnostics API enthält eine recht subtile Quelle großer Verwirrung: System.Diagnostics 'Ticks' sind nicht das gleiche wie DateTime oder TimeSpan 'Ticks'!

Wenn Sie StopWatch.ElapsedTicks anstelle von StopWatch.Elapsed.Ticks verwenden, sollte es funktionieren.

Die documentation enthält weitere Informationen dazu.

9

Mark Seemann erklärte die verwirrende Quelle des Problems, aber ich möchte ein wenig zusätzliche Informationen bereitstellen.

Wenn Sie Ihre AverageTimer32 Leistungsindikator gesetzt von einem TimeSpan und kein Stopwatch Sie die folgende Konvertierung durchführen kann.

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

Warum Sie nicht markiert müssen Gießen Sie ((Int32) .. .) PerformanceCounterTicks wird so lange ausgewertet, dass alle Werte tatsächlich lange Zahlen sind. –

+0

@DavideIcardi: Danke, Sie haben Recht, dass die Signatur der 'IncrementBy'-Methode ein' Int64' akzeptiert, so dass kein Cast benötigt wird. Ich habe den Cast aus dem Code entfernt. –

+0

Schön! genau das was ich gesucht habe! – vtortola

0

Dies ist ein alter Thread, aber ich dachte, ich läuten würde ich wurde von jemandem von Microsoft gesagt, dass ich TimeSpan, StopWatch oder DateTime nicht verwenden sollte, wenn ich mit Leistungsindikatoren arbeite. Stattdessen empfahl er die folgende native Methode zu meinem Projekt hinzufügen:

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

Wenn ein Zähler erhöht wird, empfahl er so wie dies zu tun:

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

Hat er auch einen Grund dafür angegeben? 'StopWatch' ist nur ein Wrapper für' QueryPerformanceCounter' (mit einem Fallback, falls dieser nicht verfügbar ist). Wenn 'StopWatch.IsHighResolution' wahr ist, entspricht' StopWatch.GetTimeStamp() 'dem' QueryPerformanceCounter'. – CodesInChaos

+0

Er zitierte ein Microsoft Patterns and Practices Buch über Performance. Seine Argumentation war, dass Sie so wenig Overhead wie möglich wollten, wenn Sie dieselbe Aktion mehrmals ausführen. Leistungsindikatoren können viele Male pro Sekunde inkrementiert werden. Wenn Sie eine Stoppuhr verwenden, instanziieren Sie jedes Mal ein StopWatch-Objekt, wenn Sie die Leistung einer Methode messen und den Zähler erhöhen möchten. Diese Stoppuhr-Objekte müssen dann als Müll gesammelt werden. Indem Sie QueryPerformanceCounter direkt aufrufen, schneiden Sie den Mittelmann aus und speichern Sie die Konstruktion und die Sammlung des Stoppuhrobjekts. – RobV8R

+3

'StopWatch.GetTimeStamp()' ist eine statische Methode und ein dünner Wrapper über 'QueryPerformanceCounter'.Die einzigen zusätzlichen Kosten sind eine Verzweigung in einem statischen Feld, die sich im Laufe der Zeit nicht ändert, sodass die Verzweigungsvorhersage ziemlich gut funktionieren sollte. – CodesInChaos

Verwandte Themen