大型子串在Firefox中比Chrome快9000倍:为什么?

时间:2011-05-31 22:53:45

标签: javascript performance google-chrome firefox jsperf

基准:http://jsperf.com/substringing

所以,我正在启动我的第一个基于HTML5浏览器的客户端项目。它必须将非常非常大的文本文件解析成一个或多个对象的数组。我知道我将如何编码呢;我现在主要关注的是尽可能快地获取解析器代码,而我的主要测试平台是Chrome。然而,在查看子串方法之间的差异时(我很长时间没有触及过JavaScript),我注意到与FireFox相比,Chrome中的这个基准测试速度非常慢。为什么呢?

我的第一个假设是,它与FireFox的JS引擎处理字符串对象的方式有关,而对于FireFox,这个操作是简单的指针操作,而对于Chrome,它实际上是在进行硬拷贝。但是,我不确定为什么Chrome 不会执行指针操作或为什么FireFox 。任何人都有一些见解?

JSPerf似乎丢弃了我的FireFox结果,而不是在BrowserScope上显示它们。对我来说,我在FF4 .substr()获得了9,568,203±1.44%Ops / sec。

编辑:所以我看到实际上低于Chrome的FF3.5性能结果。所以我决定测试我的指针假设。这让我进入了2nd revision我的Substrings测试,它在FF4中的1,092,718±1.62% Ops /秒与Chrome中的1,195±3.81% Ops /秒相比,速度仅提高了1000倍,但仍然令人费解性能差异。

后记:不,我并不担心有关Internet Explorer的舔。我很关心尝试提高自己的技能,并在更深层次上了解这种语言。

2 个答案:

答案 0 :(得分:15)

对于Spidermonkey(Firefox中的JS引擎),substring()调用只是创建一个新的“依赖字符串”:一个字符串对象,存储指向它的子字符串的指针以及开始和结束抵消。这恰恰是为了使substring()更快,并且在给定不可变字符串的情况下是一种明显的优化。

至于为什么V8不这样做......可能是V8试图节省空间:在依赖字符串设置中如果你坚持子字符串却忘记原始字符串,原始字符串就无法获得GCed,因为子字符串正在使用其字符串数据的一部分。

无论如何,我只看了V8源代码,看起来它们根本就没有做任何依赖的字符串;但是,评论并没有解释为什么他们没有。

[更新,2013年12月]:在我给出上述答案后几个月,V8增加了对依赖字符串的支持,正如Paul Draper指出的那样。

答案 1 :(得分:1)

您是否从基准测试结果中删除了.length的读数?

我相信V8有一些字符串的表示形式:

1. a sequence of ASCII bytes
2. a sequence of UTF-16 code units.
3. a slice of a string (result of substring)
4. a concatenation of two strings.

第4号是使+=字符串有效的原因。

我只是在猜测但是如果他们试图将两个字符串指针和一个长度打包到一个小空间中,他们可能无法使用指针缓存大长度,因此可能最终走向加入的链接列表为了计算长度。当然,这假设Array.prototype.join从数组部分创建了形式(4)的字符串。

它确实导致了一个可检验的假设,即使没有缓冲区副本也可以解释这种差异。

编辑:

我浏览了V8源代码,StringBuilderConcat是我开始拉动的地方,尤其是runtime.cc