2011-09-04 85 views
95

今天我读了关于字符串连接速度的this thread为什么字符串连接比数组连接更快?

出人意料的是,字符串连接是赢家:

http://jsben.ch/#/OJ3vo

结果是从我的想法相反。此外,还有很多关于此的文章,它们相对地解释为thisthis

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

+1

[此代码](https://jsfiddle.net/8jyer0tp/)应该产生500 TB的垃圾,但运行时间为200 ms。 我认为他们只是为一个字符串分配稍微更多的空间,并且当为它添加一个短字符串时,它通常会放入一个额外的空间。 –

回答

131

浏览器串的优化已经改变了字符串连接的可能性,JS引擎的许多实现对单式阵列的优化,将使所有我写没用:-)图片。

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

- Writing Efficient JavaScript: Chapter 7 – Even Faster Websites

JavaScript引擎(谷歌浏览器使用)使用this code做字符串连接的V8:

// 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, ""); 
} 

因此,在内部,他们通过创建一个InternalArray(在parts变量进行优化),然后填充。这些部分调用StringBuilderConcat函数。这很快,因为StringBuilderConcat函数是一些大大优化的C++代码。在这里引用太长,但在runtime.cc文件中搜索RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat)以查看代码。

+3

您留下了真正有趣的东西,该数组仅用于使用不同的参数计数来调用Runtime_StringBuilderConcat。但真正的工作是在那里完成的。 – evilpie

+1

添加到答案,谢谢你的头! – Daan

+1

_它的速度很快,因为[这是]大大优化_但在哪些方面?就是那个问题。 – artistoex

-1

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

2

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

如果您尝试在每个for之前添加for(j=0;j<1000;j++),您会看到(在chrome下)速度差异变小。最后,它仍然是字符串连接的1.5倍,但小于以前的2.6倍。

AND必须复制元素,Unicode字符可能小于对JS对象的引用。

要知道,有

19

Firefox很快,因为它使用了一种叫做Ropes(Ropes: an Alternative to Strings)的东西。绳索基本上只是一个DAG,每个节点都是一个字符串。

因此,例如,如果您要做a = 'abc'.concat('def'),新创建的对象将如下所示。 当然,这并不完全如何在内存中看起来像,因为你仍然需要一个字段类型,长度和其他的字段。

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

而且b = a.concat('123')

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

因此,在最简单的情况下,虚拟机都可以做几乎没有工作。唯一的问题是,这会稍微减慢产生的字符串上的其他操作。这当然也减少了内存开销。

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

0

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

我会说String.concat在最新版本的V8中有更好的表现。但对于Firefox和Opera,Array.join是赢家。

1

This test显示了实际使用由数组连接vs与array.join方法构成的字符串的惩罚。尽管Chrome v31的整体分配速度仍然是速度的两倍,但它不再像不使用结果字符串那么大。

3

这些基准是微不足道的。连接相同的三个项目将被内联,结果将被证明是确定性的和记忆的,垃圾处理程序将只是抛出数组对象(它几乎没有大小),并可能由于没有数据而被推出并弹出堆栈外部引用以及字符串永不改变。如果测试是大量随机生成的字符串,我会更加印象深刻。 正如在一个或两个值得的字符串。

Array.join FTW!

相关问题