Svi*_*ish 35 c# benchmarking feedback
我创建了一个简单的类来对我的一些方法进行基准测试 但它准确吗?我对基准测试,计时等等都不熟悉,所以我想在这里可以请一些反馈.此外,如果它是好的,也许其他人也可以使用它:)
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它:
var avg = Benchmark.This(() => SomeMethod()).Take(500).Average();
Run Code Online (Sandbox Code Playgroud)
任何反馈?它看起来是非常稳定和准确的,还是我错过了什么?
Hen*_*man 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
}
}
Run Code Online (Sandbox Code Playgroud)
对于奖金,您的班级应该检查该System.Diagnostics.Stopwatch.IsHighResolution字段.如果它关闭,则只有非常粗糙(20 ms)的分辨率.
但是在普通的PC上,许多服务在后台运行,它永远不会非常准确.
Eri*_*ert 10
这里有几个问题.
首先,请记住,第一次运行代码时,其方法调用的传递闭包将被jitted.这意味着第一次运行的成本可能高于每次后续运行.根据您是否对"冷"时间或"热"时间进行基准测试,这可能会有所不同.我已经看到了一些方法,其中jitting方法的成本高于其他每次调用它的成本!
其次,请记住垃圾收集器在另一个线程上运行.如果你在一次运行中制造垃圾,那么直到后续运行才能实现清理垃圾的成本.因此,您无法将一次运行的总成本计算在内,而是将其强制转换为以后的运行.
这两个都表明了所有基准测试的弱点:基准测试本质上是不现实的,因此价值有限.在实际代码中,GC将运行,抖动将继续运行,依此类推.通常情况下,基准性能与现实世界的性能完全不同,因为基准测试没有考虑大型系统固有的实际成本的可变性.我不是单独分析性能特征,而是更倾向于研究真实客户实际面临的现实场景的性能特征.
你肯定应该返回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等)一起疯狂地获得价值).我认为这个异常问题的一个简单解决方案是使用测量的中位数而不是平均值.我不知道Linq是否自动支持.