我编写了一个小脚本,用于按执行时间比较函数的实现。它运行一个函数,例如1e6次,并对每个调用的执行时间求和。我弄不明白performance.now()
呼叫究竟应该放在哪里。第一种选择是在函数调用前后将它们放入for
循环中,然后在每次迭代后将结果相加。第二个选项是将performance.now()
调用放在循环本身之前和之后。第一种方案感觉更准确,但在我的机器上,它给出的最终结果稳定地比第二种方案高出约30%。有人能解释一下原因吗?performance.now()
的正确位置是什么?
第一个选项-performance.now()
在循环内调用。在我的机器上计算150 1e6次的阶乘大约需要1300毫秒:
function factorial(x) {
return x === 1 ? 1 : x * factorial(x - 1);
}
function measure(func, arg, times) {
let timeSpent = 0;
for(var i = times;i--;) {
let t1 = performance.now();
func(arg);
let t2 = performance.now();
timeSpent += t2 - t1;
}
console.log(`Tested function ${func.name} has been executed ${times} times with argument ${arg} and it has taken %d ms. in total.`, timeSpent);
}
measure(factorial, 150, 1e6);
第二个选项-performance.now()
在循环之外调用。在我的机器上计算150 1e6次的阶乘大约需要1000毫秒:
function factorial(x) {
return x === 1 ? 1 : x * factorial(x - 1);
}
function measure(func, arg, times) {
let t1 = performance.now();
for(var i = times;i--;) func(arg);
let t2 = performance.now();
console.log(`Tested function ${func.name} has been executed ${times} times with argument ${arg} and it has taken %d ms. in total.`, t2 - t1);
}
measure(factorial, 150, 1e6);
当我进行性能测量时,我会考虑以下几点:
-
我正在使用的时间服务的准确性是多少,它是返回"挂钟时间"还是"cpu时间">
-
测量的准确度需要多少(1%,.1%,10%?(
-
我想花多少时间进行性能测试?
-
在进行测量之前,我是否需要"预热时间"(例如,如果我正在测量事务运行所需的时间,并且我使用的是Java或javascript等JITted语言,那么我可能想放弃前N个结果的计时。另一方面,如果我在测量"批处理"程序-只运行一次-那么我不需要这样做(。
通常,时间服务的精度在数百微秒到几毫秒之间。所以,你要确保你在测量任何东西时都做了足够的循环,这样你就不会担心你的时间函数会给你带来悲伤。因此,如果我有一个精确到(比如(100us的计时器,那么我会确保我运行了足够的循环,至少需要1000us(1ms(,可能需要10ms。
在进行性能测量时,还有很多因素会导致抖动。抖动是在进行性能测量时,在实践中不可避免的变化。因此,我也会多次重复您的性能运行,并查看运行的平均值、标准偏差,以查看结果的一致性(这也有助于阐明JIT编译器之类的东西,因为当您查看结果时,会发现一些非常严重的异常值(。
为了真正回答你的问题,我会选择你的第二个选项。你想确保你有足够的工作要做,这样你的时间服务的准确性就不会成为问题。