为什么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( parts
variables)来优化它,然后填充。 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数组。