为什么string连接比数组连接更快?

今天,我读了关于string连接速度的这个线程 。

令人惊讶的是,string连接是胜利者:

http://jsben.ch/#/OJ3vo

结果和我想的相反。 另外,关于这个的文章有很多, 这个或者这个相反地​​解释。

我可以猜测,浏览器已经优化了string在最新版本,但他们如何做到这一点? 我们可以说,串联string时使用+更好吗?

浏览器string优化已更改string连接图片。

Firefox是第一个优化string连接的浏览器。 从版本1.0开始,数组技术实际上比在所有情况下使用加号运算符要慢。 其他浏览器也优化了string连接,因此使用加号运算符,Safari,Opera,Chrome和Internet Explorer 8也会显示更好的性能。 版本8之前的Internet Explorer没有这样的优化,所以数组技术总是比加号运算符更快。

– 编写高效的JavaScript:第7章 – 更快的网站

V8 JavaScript引擎(在Google Chrome中使用)使用此代码来执行string连接:

 // ECMA-262, section 15.5.4.6 function StringConcat() { if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]); } var len = %_ArgumentsLength(); var this_as_string = TO_STRING_INLINE(this); if (len === 1) { return this_as_string + %_Arguments(0); } var parts = new InternalArray(len + 1); parts[0] = this_as_string; for (var i = 0; i < len; i++) { var part = %_Arguments(i); parts[i + 1] = TO_STRING_INLINE(part); } return %StringBuilderConcat(parts, len + 1, ""); } 

所以在内部,他们通过创build一个InternalArray( partsvariables)来优化它,然后填充。 StringBuilderConcat函数被这些部分调用。 这很快,因为StringBuilderConcat函数是一些大量优化的C ++代码。 这里引用的时间太长,但是在runtime.cc文件中searchRUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat)来查看代码。

Firefox很快,因为它使用了一种叫做“绳索( 绳索:string的替代品 )”的东西。 一根绳子基本上只是一个DAG,每个节点都是一个string。

所以举例来说,如果你想做a = 'abc'.concat('def') ,那么新创build的对象就像这样。 当然,这并不完全如此,因为你仍然需要一个字段types,长度和其他的字段。

 a = { nodeA: 'abc', nodeB: 'def' } 

b = a.concat('123')

 b = { nodeA: a, /* { nodeA: 'abc', nodeB: 'def' } */ nodeB: '123' } 

所以在最简单的情况下,虚拟机几乎没有工作。 唯一的问题是,这会减慢结果string的其他操作一点点。 这当然也减less了内存开销。

另一方面, ['abc', 'def'].join('')通常只是分配内存来在内存中布局新的string。 (也许这应该优化)

基准是微不足道的。 连续连接相同的三个项目将被内联,结果将被certificate是确定性的和记忆的,垃圾处理程序将只是抛出数组对象(这将几乎没有大小),可能只是推动和popup堆栈,因为没有外部引用,因为string不会改变。 如果testing是大量的随机生成的string,我会更加印象深刻。 就像在一个演出或两个人的琴弦中一样。

Array.join FTW!

我会说,用string更容易预先分配一个更大的缓冲区。 每个元素只有2个字节(如果UNICODE),所以即使你是保守的,你可以预先分配一个非常大的string缓冲区。 对于arrays每个元素都是“复杂的”,因为每个元素都是一个Object ,所以一个保守的实现将为更less的元素预先分配空间。

如果你尝试在每个之前添加一个for(j=0;j<1000;j++) for你会看到(在chrome下)速度的差异变小。 最后它仍然是string连接的1.5倍,但比之前的2.6还要小。

AND不得不复制元素,一个Unicode字符可能小于对JS对象的引用。

请注意,有很多JS引擎的实现可能会对单一types的数组进行优化,使我所写的所有内容都变得无用:-)

这个testing显示了实际使用一个string与使用array.join方法进行的赋值连接的惩罚。 尽pipeChrome v31的整体分配速度仍然是速度的两倍,但不会像不使用结果string那么大。

这显然取决于JavaScript引擎的实现。 即使对于一个引擎的不同版本,你也可以得到明显不同的结果。 你应该做你自己的基准来validation这一点。

我会说,在最近版本的V8中, String.concat有更好的性能。 但是对于Firefox和Opera, Array.join是一个胜利者。

我的猜测是,虽然每个版本都要花费许多连接的代价,但连接版本除了构build数组之外,还要构build数组。