2011-09-04 134 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!