为什么这些Javascript for循环在Firefox和Chrome / Safari上明显变慢?

时间:2012-09-08 04:12:47

标签: javascript performance benchmarking v8

我正在搞乱基准网站jfprefs,并在http://jsperf.com/prefix-or-postfix-increment/9创建了自己的基准。

基准测试是Javascript for循环的变体,使用前缀和后缀增量器以及不使用原位增量器的Crockford jslint样式。

for (var index = 0, len = data.length; index < len; ++index) {
  data[index] = data[index] * 2;
}

for (var index = 0, len = data.length; index < len; index++) {
  data[index] = data[index] * 2;
}

for (var index = 0, len = data.length; index < len; index += 1) {
  data[index] = data[index] * 2;
}

从基准测试的两次运行中得到数据之后,我注意到Firefox平均每秒执行大约15次操作,Chrome正在做大约300次操作。

benchmark results

我认为JaegerMonkey和v8在速度方面相当可比?我的基准测试是否存在缺陷,是Firefox在这里进行某种限制还是在Javascript解释器的性能之间存在差距?

更新:感谢jfriend00,我得出结论,性能上的差异并不完全是由于循环迭代,如this version of the test case中所示。正如您所看到的,Firefox速度较慢,但​​与初始测试用例中的差距不大。

那么为什么声明,

data[index] = data[index] * 2;

Firefox上的速度慢得多吗?

2 个答案:

答案 0 :(得分:7)

JavaScript中的数组很棘手。你创建它们的方式,你如何填充它们(以及用什么值)都会影响它们的性能。

引擎使用两种基本实现。最简单,最明显的一个是连续的内存块(就像一个C数组,有一些元数据,如长度)。这是最快的方式,理想情况下是大多数情况下你想要的实现。

问题是,JavaScript中的数组只需分配给任意索引就会变得非常大,留下“漏洞”。例如,如果您有一个小数组:

var array = [1,2,3];

并为大型索引分配值:

array[1000000] = 4;

你最终得到一个像这样的数组:

[1, 2, 3, undefined, undefined, undefined, ..., undefined, 4]

为了节省内存,大多数运行时将array转换为“稀疏”数组。基本上,哈希表,就像常规JS对象一样。一旦发生这种情况,读取或写入索引就会从简单的指针算法变为更复杂的算法,可能需要动态内存分配。

当然,不同的运行时使用不同的启发式方法来决定何时从一种实现转换为另一种实现,因此在某些情况下,例如,针对Chrome进行优化会损害Firefox中的性能。

在你的情况下,我最好的猜测是向后填充数组导致Firefox使用稀疏数组,使其变慢。

答案 1 :(得分:-4)

我讨厌给你这么简单的答案,但很简单:指令分支:http://igoro.com/archive/fast-and-slow-if-statements-branch-prediction-in-modern-processors/

从我从基准测试中获得的内容,这些引擎中有一些东西可以提供处理器地狱的指令预测功能。