【问题标题】:How to use AverageTimer32 and AverageBase performance counters with System.Diagnostics.Stopwatch?如何将 AverageTimer32 和 AverageBase 性能计数器与 System.Diagnostics.Stopwatch 一起使用?
【发布时间】:2010-12-05 13:17:01
【问题描述】:

当我执行以下程序并查看性能计数器时,结果对我来说没有意义。平均值为零,最小值/最大值为 ~0.4,而我预计 ~0.1 或 ~100。

我的问题是什么?

代码

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

【问题讨论】:

    标签: c# .net performancecounter


    【解决方案1】:

    System.Diagnostics API 包含一个非常微妙的混淆来源:System.Diagnostics 'ticks' 与 DateTime 或 TimeSpan 'ticks' 不同!

    如果您使用 StopWatch.ElapsedTicks 而不是 StopWatch.Elapsed.Ticks,它应该可以工作。

    documentation 包含有关此的更多信息。

    【讨论】:

      【解决方案2】:

      Mark Seemann 解释了问题的令人困惑的根源,但我想提供一些额外的信息。

      如果您想从TimeSpan 而不是Stopwatch 设置您的AverageTimer32 性能计数器,您可以执行以下转换:

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

      【讨论】:

      • 为什么需要强制转换 unchecked((Int32)...) ? performanceCounterTicks 被评估为 long,所有值实际上都是 long 数字。
      • @DavideIcardi:谢谢,IncrementBy 方法的签名接受Int64 是对的,因此无需执行强制转换。我已经从代码中删除了演员表。
      • TimeSpan.TicksPerSecond 可能更适合使用未来和平台,而不是对值进行硬编码。
      【解决方案3】:

      这是一个旧线程,但我想我会插话。微软有人告诉我,在使用性能计数器时,我不应该使用 TimeSpanStopWatchDateTime。相反,他建议在我的项目中添加以下原生方法:

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

      【讨论】:

      • 他也给出了理由吗? StopWatch 只是 QueryPerformanceCounter 的一个包装器(如果它不可用,则有一个后备)。如果StopWatch.IsHighResolution 为真,则StopWatch.GetTimeStamp() 等价于QueryPerformanceCounter
      • 他引用了一本关于性能的 Microsoft 模式和实践书籍。他的理由是,在多次执行相同的操作时,您希望尽可能少的开销。性能计数器每秒可能会增加很多次。通过使用 StopWatch,您每次想要测量方法的性能并增加计数器时都将实例化一个 StopWatch 对象。然后必须对这些 Stopwatch 对象进行垃圾收集。通过直接调用QueryPerformanceCounter,你就去掉了中间人,保存了Stopwatch对象的构造和集合。
      • StopWatch.GetTimeStamp() 是一个静态方法和对QueryPerformanceCounter 的精简包装。唯一的额外成本是静态字段上的分支不会随时间变化,因此分支预测应该可以很好地工作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 2021-05-27
      • 2021-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多