2009-10-10 11 views
22

Quando eseguo il seguente programma e guardo il contatore delle prestazioni, i risultati non hanno senso per me. Il valore medio è zero ei valori min/max sono ~ 0.4 quando mi aspetterei ~ 0.1 o ~ 100.Come utilizzare i contatori delle prestazioni AverageTimer32 e AverageBase con System.Diagnostics.Stopwatch?

Qual è il mio problema?

Codice

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(); 
    } 
} 

contatori di prestazioni Schermata Performance Counter Screenshot http://friendfeed-media.com/50028bb6a0016931a3af5122774b56f93741bb5c

risposta

33

L'API System.Diagnostics contiene una bella sottile fonte di grande confusione: System.Diagnostics 'zecche non sono gli stessi DateTime o TimeSpan 'ticks'!

Se si utilizza StopWatch.ElapsedTicks anziché StopWatch.Elapsed.Ticks, dovrebbe funzionare.

Il documentation contiene ulteriori informazioni a riguardo.

9

Mark Seemann ha spiegato la fonte confusa del problema, ma vorrei fornire un po 'di informazioni aggiuntive.

Se si desidera impostare il vostro contatore AverageTimer32 prestazioni da un TimeSpan e non un Stopwatch è possibile eseguire la seguente conversione:.

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

Perché avete bisogno di fusione incontrollata ((Int32) .. .)? performanceCounterTicks viene valutato come lungo, tutti i valori sono in realtà numeri lunghi. –

+0

@DavideIcardi: Grazie, hai ragione che la firma del metodo 'IncrementBy' accetta un 'Int64' quindi non è necessario eseguire un cast. Ho rimosso il cast dal codice. –

+0

Bello! proprio quello che stavo cercando! – vtortola

0

Questo è un vecchio thread, ma ho pensato di carillon I è stato detto da qualcuno di Microsoft che non dovrei usare TimeSpan, StopWatch o DateTime quando lavoro con Contatori di prestazioni. Invece, ha raccomandato di aggiungere il seguente metodo nativo al mio progetto:

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

Quando incrementare un contatore, ha raccomandato di farlo in questo modo:

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

Ha anche dato una ragione per questo? 'StopWatch' è solo un wrapper su' QueryPerformanceCounter' (con un fallback se non è disponibile). Se 'StopWatch.IsHighResolution' è vero,' StopWatch.GetTimeStamp() 'è equivalente a' QueryPerformanceCounter'. – CodesInChaos

+0

Ha citato un libro Microsoft Patterns and Practices sulle prestazioni. Il suo ragionamento era che volevi il più piccolo possibile quando esegui la stessa azione molte volte. I contatori di prestazioni possono essere incrementati molte volte al secondo. Utilizzando uno StopWatch, si crea un'istanza di un oggetto StopWatch ogni volta che si desidera misurare le prestazioni di un metodo e incrementare il contatore. Questi oggetti Cronometro devono quindi essere raccolti. Chiamando direttamente 'QueryPerformanceCounter', si ritaglia l'intermediario e si salvano la costruzione e la raccolta dell'oggetto Stopwatch. – RobV8R

+3

'StopWatch.GetTimeStamp()' è un metodo statico e un thin wrapper su 'QueryPerformanceCounter'.L'unico costo aggiuntivo è un ramo su un campo statico che non cambia nel tempo, quindi la previsione del ramo dovrebbe funzionare abbastanza bene. – CodesInChaos

Problemi correlati