我已经在评论中写下了这个答案的大部分内容,因为在撰写这些评论时,这个问题被锁定了。
我用更好的时间测量调用扩展了原始代码,这些调用performance.now
在现代浏览器中可用或作为节点模块使用performance-now
。代码作为这个 gist上传。
我改变了什么:
- 添加
function init()
,它(重新)初始化所有全局变量,否则程序将无法在多次运行中运行。
复制function breadthFirstSearch
到breadthFirstSearchUncommented
and中,第一个在每次调用时breadthFirstSearchCommented
初始化,第二个按原样使用全局变量,即前面提到的语句被注释/删除。values = new Array(1000000);
values
修改function time
为接受应该使用哪个 BFS 函数。
添加了function measureTime
,它time
使用给定的 BFS 函数参数调用并测量runs
(参数)测量的最小值、最大值和平均值。最后,它输出总时间(所有time
调用的时间总和)。这个函数的代码在下面,当然在index.js
文件末尾。
删除添加日志消息,只剩下日志消息是每次measureTime
通话的开始和结束。
功能measureTime
:
function measureTime(bfsFn, label, runs) {
console.log('starting', runs, 'measures for', label);
var diff = 0;
var min = Number.MAX_VALUE, max = Number.MIN_VALUE;
for (var i = 0; i < runs; i++) {
init();
var start = now();
time(bfsFn);
var d = now() - start;
diff += d;
min = Math.min(d, min);
max = Math.max(d, max);
}
var avg = diff / runs;
console.log('%s - total time for %d measures: %sms', label, runs, diff.toFixed(3));
console.log('%s - avg time: %sms (%sms - %sms, Δt = %sms)',
label, avg.toFixed(3), min.toFixed(3), max.toFixed(3), (max - min).toFixed(3));
}
实际答案:问题是为什么程序定义新数组两次,即在开始时一次,在每个 BFS 函数调用时一次,比初始化代码被注释时更快。
答案是对此没有任何解释。这不是 JavaScript VM 工作方式的结果,也不是任何 JavaScript 怪癖,它更简单:有问题的陈述实际上是错误的。
OP 所说的,后来甚至还提供了视频解释,仅适用于单次运行。最重要的原因是,众所周知,现代操作系统正在同时进行大量工作——我知道这并不完全正确,但为了这个解释,请耐心等待。这意味着在多次运行中很难获得完全相同的运行时间,因此我们看到多次运行之间的执行时间有些波动。
为了获得更准确的测量结果,我measureTime
为每个注释的函数调用了 100 次,为未注释的函数调用了另外 100 次。这创建了一个最小化波动的样本。
我为不同的执行环境创建了比较表。所有测试均在 OS X Yosemite 上运行,使用截至 2015 年 6 月 18 日的最新稳定版本的浏览器和 io.js。
| Environment | Uncommented time [ms] | Commented time [ms] |
| :-------------- | :---------------------- | :-------------------- |
| Chrome | 460.254 | 424.725 |
| Firefox | 796.471 | 781.662 |
| Safari | 529.492 | 474.580 |
| Node.js (io.js) | 411.804 | 394.807 |
这是我能做的最好的表格,因为 Stack Overflow 不支持表格降价。有关实际的表格样式,请查看gist。
在这个表中,很明显,原始语句表示未注释数组重新初始化的代码更快,因为我们可以看到这对于我测试的所有四个环境都是错误的。
这里要吸取的教训是不要仅根据几次运行就得出性能结论。应收集多次测试运行的数据并将其解释为所有值的平均值,或者在某些情况下,应使用更复杂的统计方法以获得更准确的结果。