我创建了一个简单的类来对我的一些方法进行基准测试。但它准确吗?我对基准测试,计时等等都不熟悉,所以我想在这里可以请一些反馈。此外,如果它是好的,也许其他人也可以使用它:)
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();
有任何反馈意见吗?它看起来是非常稳定和准确的,还是我错过了什么?
答案 0 :(得分:21)
它与您可以获得的简单基准测试一样准确。但是有一些因素不在你的控制之下:
你可以对最后一点做点什么,基准是可以防御调用GC.Collect
的罕见情况之一。您可以事先调用subject
一次以消除任何JIT问题。但这要求subject
的呼叫是独立的。
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
}
}
对于奖金,您的班级应该检查System.Diagnostics.Stopwatch.IsHighResolution
field。如果它关闭,则只有非常粗糙(20 ms)的分辨率。
但是在普通的PC上,许多服务在后台运行,它永远不会非常准确。
答案 1 :(得分:10)
在这里遇到问题。
首先,请记住,第一次运行代码时,其方法调用的传递闭包将被jitted。这意味着第一次运行的成本可能高于每次后续运行。根据您是否对“冷”时间或“热”时间进行基准测试,这可能会有所不同。我已经看到了一些方法,其中jitting方法的成本高于其他每次调用它的成本!
其次,请记住垃圾收集器在另一个线程上运行。如果你在一次运行中制造垃圾,那么直到后续运行才能实现清理垃圾的成本。因此,您无法将一次运行的总成本计算在后期运行中。
这两个都表明了所有基准测试的弱点:基准测试本质上是不现实的,因此价值有限。在实际代码中,GC将运行,抖动将继续运行,依此类推。通常情况下,基准性能与现实世界的性能完全不同,因为基准测试没有考虑大型系统固有的实际成本的可变性。我不是单独分析性能特征,而是更倾向于研究真实客户实际面临的现实场景的性能特征。
答案 2 :(得分:7)
你绝对应该返回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秒 除以频率。
除此之外,我猜你的代码很好,虽然我认为你会在测量中包含一些方法调用开销,如果这些方法本身只需要很少的时间来执行,这可能很重要。此外,您可能希望从计算出的平均值中排除对该方法的第一次调用,但我不确定您是如何在班级中这样做的。
最后一点,这可能与此类的大多数用途无关:与系统时间相比,秒表运行速度有点快。在我的计算机上,它在24小时后提前约5秒(即秒,而不是毫秒),而在其他机器上,这种漂移可能更大。因此,当它实际上只是高度粒度时,说它高度准确会有点误导。对于定时短期方法,这显然不是一个重大问题。
还有一点,当然 相关:我经常注意到,在基准测试时,我会得到一堆运行时间,这些运行时间都聚集在一个狭窄的值范围内(例如80,80,79,82等),但偶尔会在Windows中发生其他事情(比如打开另一个程序或我的反病毒或其他东西)并且我会从其他人那里得到一个非常重要的价值(例如80,80,79,271,80等)。我认为这个异常问题的简单解决方案是使用测量的中值而不是 mean 。我不知道Linq是否自动支持。
答案 3 :(得分:2)
由于我不是C#程序员,我不能准确地说该类是否是计算函数执行所需时间的适当实现。但是,重复性和准确性需要牢记。
我不了解.NET Framework的各种细节,但取决于它如何编译为本机代码,任何编译都可能会影响基准测试结果。此外,功能是否在缓存中也会产生影响。因此,您需要遍历您的函数以确保编译没有命中,并且所有内容都已加载并准备就绪。一旦完成,您就可以开始了。
其他人可能拥有比我更好的.NET信息和知识。