我一直在尝试对 Lua 代码进行一些微基准测试,但遇到了一个非常烦人的问题:我似乎无法获得一致的结果。
示例:这是一个非常简单的 Lua 程序,应该对简单的斐波那契函数进行计时:
function pfibs(n)
if n ~= math.floor(n) then return 0
elseif n < 0 then return pfibs(n + 2) - pfibs(n + 1)
elseif n < 2 then return n
else return pfibs(n - 1) + pfibs(n - 2)
end
end
t = os.clock()
pfibs(30)
t = os.clock() - t
print("time: "..t)
当我尝试连续运行几次时,会发生这样的事情:
$ lua fib.lua
time: 1.265
$ lua fib.lua
time: 1.281
$ lua fib.lua
time: 1.343
$ lua fib.lua
time: 1.437
$ lua fib.lua
time: 1.562
$ lua fib.lua
time: 1.578
$ lua fib.lua
time: 1.64
$ lua fib.lua
time: 1.703
$ lua fib.lua
time: 1.75
$ lua fib.lua
time: 1.797
$ lua fib.lua
time: 1.796
$ lua fib.lua
time: 1.812
$ lua fib.lua
time: 1.89
(这只是一个例子,为简洁起见而进行了剪裁,但代表了我所看到的那种减速曲线)
开始时间约为 1.1 秒,最终远高于 2 秒。我所做的就是坐在这里反复按上回车键。如果我将测试包装在对
time
的调用中而不是使用 Lua 时钟,或者如果我让它循环几次以多花几秒钟,也会发生同样的事情;它似乎成比例地减慢。如果我离开一段时间,有时时间会倒退。有时不会(可能是因为我不知道要离开多久)。
这是在 Windows+MSYS 上。在 Ubuntu(同一台机器)上尝试此操作会导致不同的模式,但结果仍然非常不一致且无法使用(例如,测试需要 2 秒,然后 3.5 秒,然后 4 秒,然后 2.5 秒......)。在这两种情况下,任务管理器/top 都表明后台没有任何占用 CPU 的情况。 CPU 速度切换已禁用。
我做错了什么?我的机器很旧,但它不可能坏掉(如果是机器的故障,而且每个程序每秒都慢得多,我肯定会注意到它无法使用......)。
我想要做的是了解解释器的实现,从普通 Lua 开始,然后对其进行调整,看看变化对解释器性能有何影响。正如你所看到的,我还没有通过“建立控制”,所以我实际上还没有做任何事情 - 由于基准方差与上述一样高,我所做的任何更改都将完全丢失在噪音。我选择 Lua 是因为虽然它是一个现实世界的程序,但它也很小并且易于阅读和修改。如果有更好的基础解释器可以执行此操作,或者有确定的最佳方法来对解释器性能进行基准测试,请随时在答案中添加相关建议。
编辑:
添加C
标签,因为使用传统 C 计时实用程序的 C 程序中也会发生同样的事情,例如:
#include <time.h>
#include <stdio.h>
int fib(int n) {
return n > 2 ? fib(n - 1) + fib(n - 2) : n;
}
int main(void) {
clock_t t1, t2; const int ITNS = 30;
for (int i = 0; i < ITNS; i ++) {
t1 = clock();
fib(38);
t2 = clock();
printf("time: %d\n", (int)((t2 - t1) / (CLOCKS_PER_SEC / 1000)));
}
return 0;
}
...打印以下内容:
time: 687
time: 688
time: 687
time: 688
time: 671
time: 688
time: 687
time: 688
time: 672
time: 687
time: 688
time: 687
time: 672
time: 688
time: 687
time: 688
time: 672
time: 796
time: 766
time: 719
time: 969
time: 1000
time: 1015
time: 1000
time: 1016
time: 1000
time: 1000
time: 1015
time: 1000
time: 1000
这表明效果不限于单独运行。我想这意味着机器或操作系统有问题。
你的程序在我的 Xeon E7-4850 机器上似乎非常稳定
但是,我建议您在运行基准测试时检查是否启用了
cpu-Frequency-scaling或类似turbo boost
的功能。我们之前也遇到过类似的问题,但是当我们关闭 cpu 频率缩放时,基准测试变得稳定。在 Linux 上,您可以使用 cpufrequtils 将其关闭。 另外,如果你使用的是 AMD 机器,我建议直接换成 intel 机器,因为即使 cpu 频率是固定的,lua 程序的性能对于我们的 Opteron 8431 来说仍然不稳定。所以这个问题可能取决于硬件平台,但不是 Lua 解释器本身。
编辑:我认为最好在每次迭代后读取并打印当前的cpu频率(来自/proc/cpuinfo或/dev/cpu/#/msr)以确保频率稳定。 C 程序的结果有两个明显的稳定阶段。看起来 run19 后发生了一些事情,CPU 频率降低了。
不确定这在技术上在多大程度上是一个“解决方案”,但是:
os.clock
函数在 LuaJIT 中尝试基准测试(只需设置
CPU_speed
常量即可显示合理的时间值):
-- New os.clock() implementation based on rdtsc instruction, LuaJIT required
do
local CPU_speed = 3.0 -- Your CPU TSC speed in GHz (may differ from CPU core speed)
local rdtsc = require'ffi'.cast(
'__cdecl uint64_t(*)()',
'\x0F\x31\xC3' -- rdtsc, ret
) -- This trick may not work on modern 64-bit OS
local rdtsc0 = rdtsc()
os.clock = function()
return tonumber(rdtsc() - rdtsc0)/(CPU_speed * 10^9)
end
end