2009-10-10 8 views
22

Kiedy wykonuję poniższy program i patrzę na licznik wydajności, wyniki nie mają dla mnie żadnego sensu. Średnia wartość wynosi zero, a wartości min/max wynoszą ~ 0,4, gdy można się spodziewać ~ 0,1 lub ~ 100.Jak korzystać z liczników wydajności AverageTimer32 i AverageBase za pomocą System.Diagnostics.Stopwatch?

Jaki jest mój problem?

Kod

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

Wydajność Licznik Screenshot Performance Counter Screenshot http://friendfeed-media.com/50028bb6a0016931a3af5122774b56f93741bb5c

Odpowiedz

33

System.Diagnostics API zawiera bardzo subtelne źródło wielkiego zamieszania: System.Diagnostics 'kleszcze' nie są takie same jak DateTime lub TimeSpan "ticks"!

Jeśli używasz StopWatch.ElapsedTicks zamiast StopWatch.Elapsed.Ticks, powinien działać.

The documentation zawiera więcej informacji na ten temat.

9

Mark Seemann wyjaśnił mylące źródło problemu, ale chciałbym podać trochę dodatkowych informacji.

Jeśli chcesz ustawić swój licznik AverageTimer32 wydajności z TimeSpan a nie Stopwatch można wykonać następującą konwersję.

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

dlaczego trzeba odlewania niezaznaczone ((Int32) .. .)? performanceCounterTicks jest oceniany jako długi, wszystkie wartości są w rzeczywistości długimi liczbami. –

+0

@DavideIcardi: Dzięki, masz rację, że podpis metody 'IncrementBy' akceptuje' Int64', więc nie ma potrzeby wykonywania rzutowania. Usunąłem obsadę z kodu. –

+0

Nice! dokładnie to, czego szukałem! – vtortola

0

Jest to stary wątek, ale pomyślałem, że ja kurant został poinformowany przez kogoś z Microsoft, że nie powinienem używać TimeSpan, StopWatch lub DateTime podczas pracy z Licznikami wydajności. Zamiast Polecił dodając następujący sposób natywny do mojego projektu:

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

Kiedy inkrementacja licznika, polecił robić to tak:

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

Czy podał też powód? 'StopWatch' jest po prostu opakowaniem w' QueryPerformanceCounter' (z rezerwą, jeśli nie jest dostępna). Jeśli "StopWatch.IsHighResolution" jest prawdziwe, 'StopWatch.GetTimeStamp()' jest równoważne 'QueryPerformanceCounter'. – CodesInChaos

+0

Przytoczył książkę Microsoft Patterns and Practices o wydajności. Jego rozumowanie polegało na tym, że podczas wykonywania tej samej akcji wiele razy starałeś się robić tak mało wysiłku, jak to tylko możliwe. Liczniki wydajności mogą być zwiększane wielokrotnie na sekundę. Za pomocą StopWatch tworzysz obiekt StopWatch za każdym razem, gdy chcesz zmierzyć wydajność metody i zwiększyć licznik. Te obiekty Stopwatch muszą następnie zostać zebrane. Przez bezpośrednie wywoływanie 'QueryPerformanceCounter' wycinasz środkowego człowieka i zapisujesz konstrukcję i kolekcję obiektu Stoper. – RobV8R

+3

'StopWatch.GetTimeStamp()' jest metodą statyczną i cienką opcją nad 'QueryPerformanceCounter'.Jedynym dodatkowym kosztem jest odgałęzienie na polu statycznym, które nie zmienia się w czasie, więc przewidywanie rozgałęzień powinno działać całkiem dobrze. – CodesInChaos

Powiązane problemy