大型子字符串在Firefox中比Chrome快9000倍:原因



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

因此,我开始了我的第一个基于HTML5浏览器的客户端项目。它必须将非常非常大的文本文件解析为一个或多个对象数组。我知道我将如何对它进行编码;我现在主要关心的是以最快的速度获得解析器代码,而我的主要测试平台是Chrome。然而,在研究子字符串方法之间的差异时(我已经很久没有接触过JavaScript了),我注意到与FireFox相比,Chrome中的这个基准测试速度慢得令人难以置信。为什么?

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

JSPerf似乎抛出了我的FireFox结果,而没有在BrowserScope上显示它们。对我来说,我在FF4中的.substr()上获得了9568203±1.44%的操作/秒。

编辑:所以我在Chrome下面看到了FF3.5的性能结果。所以我决定检验我的指针假说。这让我对我的Substrings测试进行了第二次修订,即在FF4中执行1,092,718±1.62% Ops/sec,而在Chrome中执行1,195±3.81% Ops/sec的速度仅快1000倍,但性能上仍然存在无法解释的差异。

附言:不,我一点也不关心Internet Explorer。我关心的是努力提高我的技能,更深入地了解这种语言。

在Spidermonkey(Firefox中的JS引擎)的情况下,substring()调用只会创建一个新的"依赖字符串":一个字符串对象,它存储一个指向它是子字符串的东西的指针以及开始和结束偏移。这正是为了使substring()快速,并且在给定不可变字符串的情况下是一个明显的优化。

至于V8为什么不这么做。。。一种可能性是V8试图节省空间:在依赖字符串设置中,如果您保留子字符串但忘记了原始字符串,则原始字符串无法获得GCed,因为子字符串正在使用其部分字符串数据。

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

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

您是否从基准测试结果中消除了.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

相关内容

最新更新