为啥这些 Javascript for 循环在 Firefox 上比 Chrome / Safari 慢得多?

Posted

技术标签:

【中文标题】为啥这些 Javascript for 循环在 Firefox 上比 Chrome / Safari 慢得多?【英文标题】:Why are these Javascript for loops significantly slower on Firefox then Chrome / Safari?为什么这些 Javascript for 循环在 Firefox 上比 Chrome / Safari 慢得多? 【发布时间】:2012-09-08 04:12:47 【问题描述】:

我在基准站点 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 次。

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

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

那为什么是这样的说法,

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

在 Firefox 上这么慢?

【问题讨论】:

为什么要把数学运算和数组运算放在for 循环中?怎么知道速度差是for循环还是循环内部的操作? 不管怎样,差别还是很大的 jfriend00,你必须在 for 循环中放一些东西,我想解释器可能会优化一个 for 循环,里面什么都没有。 @Vic - 是的,但问题是关于 for 循环,这个基准没有准确地比较 for 循环。 与实际问题无关,Firefox 中的 JavaScript 引擎是 SpiderMonkey(又名 TraceMonkey 或 JägerMonkey),而不是 Rhino。 Rhino 是一个独立的 Mozilla 项目,用 Java 编写,性能绝对比不上 V8 或 SpiderMonkey(但正在改进)。 【参考方案1】:

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

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

问题是,JavaScript 中的数组可以通过分配给任意索引而变得非常大,从而留下“漏洞”。例如,如果你有一个小数组:

var array = [1,2,3];

然后你给一个大索引赋值:

array[1000000] = 4;

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

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

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

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

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

【讨论】:

【参考方案2】:

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

从我从基准测试中获得的信息来看,这些引擎的内部有些东西提供了处理器地狱的指令预测功能。

【讨论】:

循环中没有条件语句,这与分支预测无关。 哦,好吧,所以...浏览器没有编译?他们的引擎呢?我想它们都不是用 C++ 编写的。

以上是关于为啥这些 Javascript for 循环在 Firefox 上比 Chrome / Safari 慢得多?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 const 在 JavaScript 的某些 for 循环中起作用?

为啥不建议数组使用 JavaScript 的 For...In 循环? [复制]

为啥我们不能循环遍历`...`?

为啥需要两个范围 for 循环来更改 C++ 中向量的这些元素?

vue中v-for循环的时候为啥要添加:key属性

为啥javascript不会在while循环中堆叠