你如何测试计算机每秒可以执行多少条指令?

19

有没有一种快速/简单的方法(至少可以提供粗略的估计)?

我正在对算法进行基准测试,我想知道我的计算机执行指令的绝对速度,并将其与我的渐近分析进行比较,这样很酷。


11
不,真的没有(简单的方法)。问题相当简单:计算机执行指令的数量(严重)取决于这些指令的混合和顺序。获得有意义的结果通常涉及某些知名基准测试的结果 - 但有意义的基准测试通常相当复杂。即使如此,你也必须小心 - 数字通常比预期的含义更少。 - Jerry Coffin
1
@DanielKO Whetstone基准测试最初是用Algol编写的,基于1970年左右英国国家物理实验室收集的统计数据,但仅使用四个元素数组来测试数组访问。然后将其翻译成Fortran。Dhrystone基于类似的原则,在不同的语言中编写,用Ada编写,但被翻译成C。我在大约30年前第一次学习时认为Whetstone已经过时和不切实际。 - Patricia Shanahan
2
@DanielKO 的观点是,四个元素的数组和访问它们的循环可以通过优化方式进行优化,而这些方式对于较大的数组并不适用,并且在 Whetstone 中,四个元素是唯一的数组大小。虽然我已经通过维基百科检查了日期,但在维基百科出现之前,我就已经知道 Whetstone 的历史了。 - Patricia Shanahan
1
这个30年的基准测试仍然被广泛使用,因此它是你能够得到的最接近“粗略估计”的东西。我是否错过了一个关键信息,使得那个结论看起来合乎逻辑? :) - jalf
3
如果你使用gcc -O3编译原始的Dhrystone基准测试(在几年前),你会得到神奇的数字,因为(至少)其中一个循环被转换为空,从而需要零时间。每秒钟的指令数是指令数/时间->无限数量。但总基准测试不为零,时间不为零,因此你最终会得到一些幻想数字,在处理器理论性能的100-1000倍之间。你可以使用一些技巧来让编译器相信你需要循环内部的代码,但这不再是原始源代码了。另外,Drhystone基于VAX指令集。 - Mats Petersson
显示剩余7条评论
5个回答

27

如果你想了解你的CPU能够做什么,那么请查看文档。你的CPU供应商会指定所有指令的延迟和吞吐量,以及各种其他信息(每个周期可以发出或撤回多少指令,缓存延迟等)。基于此,你可以计算出理论峰值吞吐量。

如果你想知道你的CPU实际上在做什么,那么运行自己的代码并测量其性能。

然而,请记住,现代CPU非常复杂,它们的性能取决于各种因素,你很少能够接近将CPU最大化,了解为什么,或者到底是什么阻碍了你的代码需要对硬件有相当透彻的理解。(我的一般经验法则是,如果你获得了持续的30-40%的理论峰值FLOPS,则表现非常好)


或者让内核给你提供BogoMIPS。它至少和任何理论(也就是与任何上下文无关的)估计一样有用。 - DanielKO
除非“文档中提到的内容”与任何上下文都“毫无关联”,否则@DanielKO并不正确。它是关于您CPU如何工作以及其能力的硬性事实信息,如果您想让您的代码执行良好,则相当相关。但是,如果您只需要一个快速的估计值,并放弃很多细节,那么该数字可能是一个非常好的选择。 - jalf
1
@jalf:指令不是独立执行的,因此即使对每个指令的执行方式进行详细描述,也很难揭示执行过程中实际发生的情况。缓存未命中、分支预测失败、数据依赖等都是我提到的上下文的一部分。 - DanielKO
2
@DanielKO 是的。这些都没有以任何方式与我的答案相矛盾,对吧?但是,如果您想知道在完美的代码情况下,您的CPU能够实现的最大理论吞吐量,那么您需要假设不存在缓存未命中、分支预测错误或数据依赖性。所有这些因素都可以帮助解释为什么您的代码比这个理论最大值慢得多,这正是重点所在 - jalf

8
这是一个典型的“理论上,理论和实践是一样的,在实践中它们并不相同”的案例。
现代CPU内部有非常复杂的逻辑,这意味着实际执行的操作数量与仅仅查看代码或思考问题时想象的数量不同[除非你有一个像小行星那么大的大脑,而且知道那个特定的CPU如何工作]。例如,处理器可能会在分支的一侧或另一侧进行推测性执行指令,即使它还没有到达分支-如果那是“错误”的一侧,则它将丢弃这些指令的结果-但是当然需要时间来执行它们。
指令也是乱序执行的,这意味着很难准确预测哪个指令将在何时执行。有一些例外情况。
只有在同时通过所有可用的执行单元推送数据和指令时,才能获得(接近)理论吞吐量-这意味着具有正确的指令混合以及当然所有缓存中的代码和数据。
因此,从理论上讲,我们可以通过编写非常聪明的代码将处理器塞满以达到最大化,但在实践中,这很快就变成了一项艰巨的任务。
然而,问题是关于测量指令吞吐量的,而在现代CPU上,通过正确的额外软件完全可以做到这一点。对于Linux perftool或oprofile,对于Windows有Intel的VTune和AMD的Code Analyst。这些将允许您(在具有足够权限的情况下)获取处理器中的“性能计数器”,其中包括“指令数量”,“浮点操作数量”,“缓存未命中数量”,“分支预测错误”等许多其他处理器性能测量。因此,给定足够长的运行时间(至少几秒钟,最好更长),您可以测量处理器执行的实际计数或时钟周期。

5
“... that turns very very very quickly.”,我们可以安装一个发电机将其作为能源来源吗? - DanielKO
对于“理论上的乐趣”;在现代CPU(例如Intel Nehalem及更高版本,具有“循环流检测器”)上,我会考虑尝试包含单字节NOP指令的循环(因此指令被前端丢弃并不进入微操作缓冲区)。我猜想你可以通过这种方式在理论上超过“每个周期100条指令”。 - Brendan
@Brendan:不,至少英特尔CPU会将NOPs运行整个流水线。它们在ROB中占据一个插槽,但在RS中为零(未融合域:不需要执行单元)。这对于SnB系列来说绝对是正确的,但我没有测试Nehalem。在将它们发放到后端之前丢弃它们可能是实际可行的,但这不是一个非常有价值的优化。也许不值得为了让NOPs后的第一条指令从不是上一条指令结尾的RIP开始,而且没有跳转。此外,“指令”的性能计数器也会出错。(虽然这不是致命问题。) - Peter Cordes

4
实际上,现在有效指令的数量主要取决于内存延迟,这是性能瓶颈。等待数据是不好的。处理器可以通过缓存、流水线和并发等技术在一定程度上缓解这个问题,但问题仍然存在,并且随着时间的推移只会变得更糟。适当的实现可以产生巨大的差异。您可能想查看关于友好缓存代码的问题。

2

2
现代CPU正在进行指令处理的流水线操作,因此没有固定的常数。
但是,您可以在算法开始和结束时读取CPU时钟数。我认为这是您可以使用的最低级别的测量方法。

http://en.wikipedia.org/wiki/Time_Stamp_Counter

注意:这个翻译可能不会100%准确,因为有很多问题,我可以提出一些,但我相信社区能够增加列表: -操作系统抢占进程 -缓存未命中(算法第一次运行速度较慢,如果随后运行,则更快) -在旧CPU上,CPU时钟不等于CPU频率

2
除非机器上几乎没有其他运行的程序,否则这通常不会非常准确。时间戳计数器对于在单个时间片中运行的非常的代码部分非常有用。对于像完整程序这样的东西,通常更有意义从操作系统获取时间(例如,在Linux上使用“times”或在Windows上使用“GetProcessTimes”)。 - Jerry Coffin
在Unix系统中,clock_gettime()是首选的方法,因为您可以指定如何测量时间(如果您真的想要它,甚至会映射到RDTSC); C++11或多或少将其合并到了std::chrono中。 - DanielKO

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