普通的单线程* nix程序可以用像time
这样的工具进行基准测试,即:
# how long does `seq` take to count to 100,000,000
/usr/bin/time seq 100000000 > /dev/null
输出:
1.16user 0.06system 0:01.23elapsed 100%CPU (0avgtext+0avgdata 1944maxresident)k
0inputs+0outputs (0major+80minor)pagefaults 0swaps
...但返回的数字总是依赖于系统,从某种意义上说,它也衡量用户的硬件。
是否有一些非相对基准测试方法或命令行工具会在任何系统上(或至少是一个相当大的系统子集)返回大致相同的虚拟时序数?就像grep -m1 bogo /proc/cpuinfo
返回a roughly approximate but stable unit一样,这样的基准也应该返回一个类似的持续时间单位。
假设对普通命令进行基准测试,我们有一个神奇的工具bogobench
(其中“bogo”是一个形容词,表示“有点虚假状态”,但不一定与BogoMIPs有共同的算法):
bogobench foo bar.data
我们在两个物理上独立的系统上运行它:
期望的输出将是这样的:
21 bogo-seconds
所以bogobench
应该在两种情况下返回相同的数字,即使它可能在第二个系统上完成的时间要少得多。
像qemu
这样的硬件模拟器可能是一种方法,但不一定是唯一的方法:
bogo.sh
中bogo.sh
复制到可引导的Linux磁盘映像bootimage.iso,在bogo.sh
将自动运行的目录中,然后立即关闭模拟器。在此期间,它输出某种形式的计时数据以解析为bogo-seconds。qemu
的一个更小的-machine
选项运行bootimage.iso:
qemu-system-i386 -machine type=isapc bootimage.iso
但我不知道如何让qemu
使用虚拟时钟,而不是主机CPU的时钟,而qemu
本身似乎是一个看似简单的任务的重工具。 (真的MAME或MESS将比qemu
更多才多艺的模拟器用于这样的任务 - 但我不擅长MAME,尽管MAME currently has some capacity for 80486 PC emulation。)
在线我们有时会将机器X上基于时间的基准测试与机器Y上的基准测试进行比较和对比。而我希望用户X和Y能够在虚拟机Z上进行基准测试,并为模拟X提供奖励积分或Y(如MAME),如果需要,除了不考虑X或Y的实际运行时间(与MAME不同,仿真通常可以播放)。通过这种方式,用户可以报告程序在有趣情况下的执行情况,而程序员不必担心结果会因用户硬件的特性(例如CPU怪癖,占用资源的后台进程等)而产生偏差。
实际上,即使在用户自己的硬件上,基于time
的基准测试也可能不可靠,因为用户通常不能确定某些后台进程(或错误或硬件错误,如坏扇区或病毒)可能不会降级表现的某些方面。而更虚拟的基准应该不那么容易受到这种影响。
我认为实现这一目标的唯一理智方法是使用周期精确的模拟器来进行某种硬件设计。
AFAIK,没有适用于现代x86硬件的公开可用周期精确模拟器,因为它非常复杂,尽管有很多关于x86微架构内部结构的东西(Agner Fog的东西,英特尔和AMD自己的优化指南,以及x86标签中的其他内容) wiki),足够的行为仍然是一个充满CPU设计商业秘密的黑盒子,它最多可以模拟类似的东西。 (例如,分支预测绝对是最秘密但非常重要的部分之一)。
虽然应该可以接近模拟英特尔Sandybridge或Haswell的实际管道和无序核心/ ROB / RS(远远低于实时),但我没有人做过它。
但确实存在用于其他硬件设计的周期精确模拟器:Donald Knuth's MMIX architecture是一种干净的RISC设计,实际上可以用硅片构建,但目前只存在于纸上。
从该链接:
特别感兴趣的是MMMIX元模拟器,它能够对复杂管道进行动态调度,允许超标量执行任意数量的功能单元,并具有多种缓存和分支预测等,包括两者的详细实现硬中断和软中断。
所以你可以使用它作为参考机器,让每个人都可以运行他们的基准测试,每个人都可以获得可比较的结果,告诉你在MMIX上运行的速度有多快(在使用gcc编译MMIX之后)。但不是它在x86上运行的速度有多快(大概也是用gcc编译),即使对于以不同方式执行相同工作的两个程序,这可能会有很大差异。
对于[fastest-code]
在编程难题和Code Golf网站上的挑战,@orlp created the GOLF architecture with a simulator that prints timing results,正是为此目的而设计的。它是一个玩具架构,通过存储到0xffffffffffffffff
来打印到stdout,所以它不一定会告诉你任何关于某些东西在任何真实硬件上运行的速度。
GOLF,AFAIK没有完整的C实现,所以你只能用手写的asm来实现它。这与MMIX有很大不同,MMIX是优化编译器的目标。
一种可以(可能?)扩展到更准确的实用方法是使用现有工具来测量被测代码的一些硬件不变性能度量,然后应用公式来得出你的bogoseconds
得分。
遗憾的是,大多数易于测量的硬件指标并非不变 - 相反,它们依赖于硬件。然而,一个应该是不变的明显的是“指令退休”。如果代码在每次运行时采用相同的代码路径,则退役指令计数应该在所有hardware1上相同。
然后你应用某种标称时钟速度(比如1 GHz)和标称CPI(比如1.0)来得到你的bogoseconds - 如果你测量15e9指令,你输出15 bogoseconds的结果。
这里的主要缺陷是名义CPI可能偏离实际CPI!虽然大多数程序在1 CPI左右徘徊,但很容易找到它们可以接近0.25或宽度的倒数的示例,或者如果存在许多冗长的档位则可以是10或更多。当然,这些极端程序可能是您想要进行基准测试的 - 即使不是,您也有问题,如果您使用基准来评估代码更改,它将忽略CPI中的任何改进或回归,并且只关注指令数量。
尽管如此,它可以满足您的要求,因为它可以有效地模拟每个周期执行1个指令的机器,也许这是一个合理的宽图像方法。使用像perf stat -e instructions
这样的工具很容易实现(就像单线程一样容易)。
为了修补漏洞,你可以试着让公式变得更好 - 假设你可以为缓存未命中添加一个因子来解释那个大的停顿源。不幸的是,您如何以硬件不变的方式测量缓存未命中?性能计数器无济于事 - 它们依赖于本地缓存的行为和大小。好吧,您可以使用cachegrind
以与机器无关的方式模拟缓存。事实证明,cachegrind甚至涵盖了分支预测。因此,您可以将指令计数,缓存未命中和分支未命中数插入更好的公式(例如,使用典型的L2,L3,RAM延迟和分支未命中的典型成本)。
我认为,这就是这种简单的方法会带给你的。在那之后,你可能只是撕掉任何现有的x862仿真器并在那里添加简单的机器模型。您不需要精确循环,只需选择标称宽度并对其进行建模。可能无论是什么底层仿真cachegrind都可能是一个很好的匹配,你可以免费获得缓存和分支预测建模。
1当然,这并不排除指令计数机制中的错误或不准确。
2你没有标记你的问题x86
- 但我会假设这是你的目标,因为你只提到了英特尔芯片。