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

10
我在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上速度慢那么多?

1
你为什么要在for循环内放置数学运算和数组操作?你如何知道速度差异是for循环还是循环内的操作造成的? - jfriend00
2
无论哪种方式,这仍然是一个巨大的区别。 - Vic
@Vic - 是的,但问题是关于for循环的,而这个基准测试并没有准确比较for循环。 - jfriend00
是的,这会有很大的差别,http://jsperf.com/prefix-or-postfix-increment/10。我想我应该问为什么在Rhino中数学运算如此缓慢。有趣的是,简化的for循环在Chrome中实际上比做数学的那些更慢。 - James McMahon
1
与实际问题无关,Firefox中的JavaScript引擎是SpiderMonkey(又名TraceMonkey或JägerMonkey),而不是Rhino。Rhino是一个用Java编写的独立Mozilla项目,性能绝对不能与V8或SpiderMonkey相比(但正在改进)。 - Matthew Crumley
显示剩余7条评论
2个回答

8

JavaScript中的数组有些棘手。创建数组的方式,填充数组(以及使用哪些值进行填充)都会影响其性能。

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

问题在于,JavaScript中的数组可以通过分配到任意索引来变得非常庞大,留下“空洞”。例如,假设你有一个小数组:

var array = [1,2,3];

如果你给一个很大的索引赋值:

array[1000000] = 4;

你最终会得到这样一个数组:
[1, 2, 3, undefined, undefined, undefined, ..., undefined, 4]

为了节省内存,大多数运行时会将array转换为“稀疏”数组。基本上就是哈希表,就像普通的JS对象一样。一旦这种情况发生,读取或写入索引就从简单的指针算术变成了一个更复杂的算法,可能需要动态内存分配。
当然,不同的运行时使用不同的启发式算法来决定何时从一种实现转换到另一种实现,所以在某些情况下,为Chrome进行优化可能会损害Firefox的性能。
在您的情况下,我最好的猜测是反向填充数组导致Firefox使用稀疏数组,使其变慢。

-5

1
循环中没有条件语句,这与分支预测无关。 - verdesmarald
哦,好的,那么...浏览器不是编译的吗?它们的引擎呢?我想它们也不是用C++编写的。 - alvonellos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接