C#:这个基准class是否准确?

我创build了一个简单的类来testing我的一些方法。 但是准确吗? 我对基准testing,时机testing等方面有些新颖,所以我想我可以在这里要求一些反馈。 另外,如果它是好的,也许别人也可以利用它:)

public static class Benchmark { public static IEnumerable<long> This(Action subject) { var watch = new Stopwatch(); while (true) { watch.Reset(); watch.Start(); subject(); watch.Stop(); yield return watch.ElapsedTicks; } } } 

你可以像这样使用它:

 var avg = Benchmark.This(() => SomeMethod()).Take(500).Average(); 

任何反馈? 它看起来相当稳定和准确,还是我错过了什么?

这是一个准确的,你可以得到一个简单的基准。 但有一些因素不受你的控制:

  • 从其他进程加载系统
  • 基准之前/期间堆的状态

你可以做一些关于最后一点的事情,一个基准是可以为GC.Collect调用的罕见情况之一。 你可能会事先调用一个subject来消除任何JIT问题。 但是这要求呼吁要独立。

 public static IEnumerable<TimeSpan> This(Action subject) { subject(); // warm up GC.Collect(); // compact Heap GC.WaitForPendingFinalizers(); // and wait for the finalizer queue to empty var watch = new Stopwatch(); while (true) { watch.Reset(); watch.Start(); subject(); watch.Stop(); yield return watch.Elapsed; // TimeSpan } } 

对于奖金,你的class级应该检查System.Diagnostics.Stopwatch.IsHighResolution字段 。 如果closures,则只有非常粗(20毫秒)的分辨率。

但是在一台普通的PC上,有很多服务在后台运行,所以永远都不会很准确。

几个问题在这里。

首先,请记住,第一次运行代码时,会调用方法调用的传递闭包。 这意味着第一轮运行的成本很可能高于后续运行。 根据你是基准“冷”时机还是“热”时机,这可能会有所作为。 我已经看到了这种方法,其中方法的成本高于其他所有方法的成本!

其次,请记住垃圾收集器在另一个线程上运行。 如果你在一次运行中做垃圾,那么清理垃圾的成本可能不会实现,直到后续运行。 因此,你没有把一次运行的总成本考虑在内,把它推迟到以后的运行。

这两者都表明所有基准的弱点:基准本质上是不现实的,因此价值有限。 在现实世界的代码中,GC正在运行,抖动正在运行,等等。 基准性能通常与真实性能无关,因为基准没有考虑到大型系统固有的现实成本变化。 我并不是单独分析perf特性,而是倾向于观察实际客户实际面临的现实场景的特性。

你一定要返回ElapsedMilliseconds而不是ElapsedTicks。 ElapsedTicks返回的值取决于秒表频率,在不同的系统上可能会有所不同。 它不一定对应于Timespan或DateTime对象的Ticks属性。

请参阅http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.elapsedticks.aspx

如果你想要额外的Ticks分辨率,你应该返回watch.Elapsed.Ticks (即Timestamp.Ticks)而不是watch.ElapsedTicks (这可能是.Net中最微妙的潜在错误之一)。 来自MSDN:

秒表滴答不同于DateTime.Ticks。 DateTime.Ticks值中的每个记号表示一个100纳秒的间隔。 ElapsedTicks值中的每个刻度表示等于1秒的时间间隔除以频率。

除此之外,我猜你的代码是好的,虽然我认为你会在测量中包含一些方法调用开销,如果方法本身花费很less的时间来执行,这可能是非常重要的。 此外,你可能会想排除第一次调用的方法从你的平均计算,但我不知道你会怎么做你的class级。

最后一点,这可能与这个类的大多数用途无关:与系统时间相比,秒表运行有点快。 在我的电脑上,在24小时之后,它大约需要5秒钟(即 ,而不是毫秒),在其他机器上,这个漂移可能会更大。 所以说它是非常准确的 ,当它实际上只是非常精细时,有点误导。 对于时间短期的方法,这显然不会是一个重大的问题。

还有一点最后一点,这当然相关的:我经常会注意到,在基准testing中,我会得到一堆运行时间,这些时间都集中在一个很窄的范围内(例如80,80,79,82等) ,但是偶尔会有其他的事情发生在Windows中(比如打开另一个程序或者我的反病毒软件或者其他东西),而且我会从其他方面(例如80,80,79,271,80等等) )。 我认为一个简单的解决scheme,这个离群值问题是使用您的测量中位数 ,而不是平均值 。 我不知道Linq是否自动支持。

由于我不是C#程序员,所以我无法准确地说出这个类是否是计算函数执行需要多长时间的适当实现。 但是,要记住重复性和准确性。

我不了解.NET Framework的各种细节,但根据它如何编译为本地代码,可能任何编译都会影响基准testing结果。 另外,函数是否在caching中也可以有所作为。 所以你需要遍历你的函数,以确保没有编译命中,并且一切都已经加载并准备好了。 完成之后,您可能就可以开始了。

其他人可能比我有更好的.NET的信息和知识。

Interesting Posts