如何衡量冷启动代码性能?

3
假设我有两个方法FooBar,它们做的事情差不多,我想测量哪一个更快。此外,单次执行FooBar都太快了,无法可靠地测量。

通常,我会像这样运行它们很多次:

var sw=new Stopwatch();
sw.Start();
for(int ii=0;ii<HugeNumber;++ii)
    Foo();
sw.Stop();
Console.WriteLine("Foo: "+sw.ElapsedMilliseconds);
// and the same code for Bar

但是这种方式,除了第一次运行Foo之外,每次运行都可能使用处理器缓存而不是实际内存。这可能比在真实应用程序中快得多。我该怎么做才能确保每次都运行我的方法呢?
澄清一下,所谓的“大致相同”,是指两种方法的使用方式相同,但实际算法可能有很大的差异。例如,Foo可能正在进行一些棘手的数学计算,而Bar则通过使用更多的内存来跳过它。
是的,我知道在冷启动时运行的方法对整体性能没有太大的影响。我仍然想知道哪个更快。

2
它被缓存了有什么关系呢?它们两个都会被缓存,所以你仍然知道哪一个更快,对吧? - Rob
或者你可以在第一次迭代之后开始计时器...但正如Rob指出的那样,这并不是必要的。 - Kornelije Petak
@Rob 两者都将被缓存,且都会更快。但是性能上的确切收益不会相等:假设Foo进行了一些棘手的数学计算,而Bar通过使用更多的内存来避免这种情况。在这种情况下,Bar将从运行热中获得更多的性能提升。 - Nevermind
@Nevermind引用:“做大致相同的事情”,说得够清楚了吧?虽然这并没有回答你最初的问题,但这就是一个评论:D - Rob
在启动秒表之前,您实际上需要调用您的方法一次,否则可能会将JIT添加到您的度量中。 - Guillaume
显示剩余3条评论
2个回答

1
首先,如果Foo正在使用处理器缓存,则Bar也将使用处理器缓存。不是这样吗?所以你的两个函数都得到了相同的特权。现在假设第一次foo时间为A,然后它以平均时间B运行,因为它正在使用处理器缓存。所以总时间将是:
A + B*(hugenumber-1)

同样地,对于 Bar 它将是这样的。
C + D*(hugenumber-1) //where C is the first runtime and D is the avg runtime using prscr cache

如果我没错的话,结果取决于B和D,它们都是使用处理器缓存的平均运行时间。因此,如果您想计算哪个函数更好,我认为处理器缓存不是问题,因为两个函数都应该使用它。

编辑:

我现在认为很清楚了。由于Bar通过使用内存跳过了一些棘手的数学问题,它将具有一点优势(可能是纳秒/皮秒)。因此,为了限制这一点,您必须在for循环中刷新CPU缓存。由于在两个循环中您将执行相同的操作,我认为现在您会对哪个函数更好有更好的理解。已经有一个关于如何刷新CPU缓存的堆栈溢出讨论。请访问此link,希望能有所帮助。

编辑详情:改进答案并更正拼写错误


我特别关注你示例中的 C 中的 A,而不是 B 和 D。 - Nevermind
那个链接确实很有帮助,虽然我希望有一个更简单的解决方案……至少,留在“托管”方面。 (-8 - Nevermind

0
假设FooBar 相似,任何缓存加速(或其他任何环境因素)都应该对两者产生相同的影响。 所以即使您可能没有获得冷启动性能的准确绝对度量,如果存在,则仍应观察算法之间的相对差异。
另外请记住,如果这些功能在系统的内部循环中被调用(否则你为什么会那么在意它们的性能),在现实世界中它们可能已经被保留在缓存中了,所以通过使用您的代码,您可能会得到一个接近真实世界性能的不错近似值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接