2009-10-10 4 views
22

Quand j'exécuter le programme suivant et regarder le compteur de performance les résultats ne font pas de sens pour moi. La valeur moyenne est zéro et les valeurs min/max sont ~ 0.4 quand j'attendrais ~ 0.1 ou ~ 100.Comment utiliser les compteurs de performance AverageTimer32 et AverageBase avec System.Diagnostics.Stopwatch?

Quel est mon problème?

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

compteur de performance Screenshot Performance Counter Screenshot http://friendfeed-media.com/50028bb6a0016931a3af5122774b56f93741bb5c

Répondre

33

L'API System.Diagnostics contient une source assez subtile d'une grande confusion: les tiques de «System.Diagnostics ne sont pas les mêmes que DateTime ou TimeSpan "ticks"!

Si vous utilisez StopWatch.ElapsedTicks au lieu de StopWatch.Elapsed.Ticks, il devrait fonctionner.

Le documentation contient plus d'informations à ce sujet.

9

Mark Seemann a expliqué la source de confusion du problème, mais je voudrais donner un peu d'informations supplémentaires.

Si vous souhaitez définir votre compteur de performance AverageTimer32 d'un TimeSpan et non un Stopwatch vous pouvez effectuer la conversion suivante:.

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

Pourquoi avez-vous besoin coulée sans contrôle ((Int32) .. .)? performanceCounterTicks est évalué aussi longtemps, toutes les valeurs sont en fait des nombres longs. –

+0

@DavideIcardi: Merci, vous avez raison que la signature de la méthode '' IncrementBy' accepte un Int64' donc il n'y a pas besoin d'effectuer un casting. J'ai supprimé la distribution du code. –

+0

Bien! juste ce que je cherchais! – vtortola

0

Ceci est un vieux fil, mais je pensais que je serais carillon dans I a été dit par quelqu'un de Microsoft que je ne devrais pas utiliser TimeSpan, StopWatch ou DateTime lorsque vous travaillez avec des compteurs de performance. Au lieu de cela, il a recommandé d'ajouter la méthode native suivante à mon projet:

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

Quand incrémenter un compteur, il a recommandé le faire comme ceci:

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

A-t-il également donné une raison à cela? 'StopWatch' est juste un wrapper sur' QueryPerformanceCounter' (avec un retour s'il n'est pas disponible). Si 'StopWatch.IsHighResolution' est vrai,' StopWatch.GetTimeStamp() 'est équivalent à' QueryPerformanceCounter'. – CodesInChaos

+0

Il a cité un livre Microsoft Patterns and Practices sur Performance. Son raisonnement était que vous vouliez le moins de frais possible en exécutant la même action plusieurs fois. Les compteurs de performance peuvent être incrémentés plusieurs fois par seconde. En utilisant un StopWatch, vous instanciez un objet StopWatch chaque fois que vous voulez mesurer la performance d'une méthode et incrémenter le compteur. Ces objets Stopwatch doivent ensuite être récupérés. En appelant 'QueryPerformanceCounter' directement, vous supprimez l'intermédiaire et enregistrez la construction et la collecte de l'objet Stopwatch. – RobV8R

+3

'StopWatch.GetTimeStamp()' est une méthode statique et une enveloppe fine sur 'QueryPerformanceCounter'.Le seul coût supplémentaire est une branche sur un champ statique qui ne change pas au fil du temps, donc la prédiction de branchement devrait plutôt bien fonctionner. – CodesInChaos

Questions connexes