2011-09-04 7 views
95

今日、私は文字列連結の速度についてthis threadを読んでいます。文字列の連結が配列結合よりも速いのはなぜですか?

は驚くべきことに、文字列の連結は、受賞した:

http://jsben.ch/#/OJ3vo

結果は、私が考えたものとは逆でした。また、反対に説明するこの記事については、thisthisなど多くの記事があります。

ブラウザは最新のバージョンでconcatという文字列に最適化されていると推測できますが、どのようにしてそれを行うのですか?文字列を連結するときに+を使用する方が良いと言うことができますか?

+1

[This code](https://jsfiddle.net/8jyer0tp/)は500テラバイトのガベージを生成するはずですが、200ミリ秒で実行されます。 私は、文字列に少しだけスペースを割り当てていると思います。短い文字列を追加すると、通常余分なスペースに収まります。 –

答えて

131

ブラウザの文字列の最適化は、文字列の連結を変更したJSエンジンの多くの実装がすべてになるだろう、シングル型の配列の最適化、私が書かれている役に立たない:-)を持っている可能性があることに注意してください画像。

Firefoxは、文字列の連結を最適化する最初のブラウザでした。バージョン1.0から、配列手法は実際にはすべての場合にプラス演算子を使用するよりも遅くなります。他のブラウザも最適化された文字列連結を持っているため、Safari、Opera、Chrome、Internet Explorer 8では、プラス演算子を使用した方がパフォーマンスが向上します。バージョン8より前のInternet Explorerでは最適化が行われていなかったため、配列手法は常にplus演算子より高速でした。

からWriting Efficient JavaScript: Chapter 7 – Even Faster Websites

(Google Chromeで使用される)エンジンは、文字列連結を行うためにthis codeを使用してJavaScriptの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_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat)のファイルruntime.ccを検索してください。

+3

あなたは本当に面白いことを残しました。配列は、異なる引数の数でRuntime_StringBuilderConcatを呼び出すためにのみ使用されます。しかし、実際の作業はそこで行われます。 – evilpie

+1

答えに追加しました、ヘッドアップのおかげで! – Daan

+1

_非常に最適化されているため速いですがどんな方法でですか?それが問題です。 – artistoex

-1

私の推測では、すべてのバージョンで多くの連結コストがかかりますが、結合バージョンではそれに加えて配列が構築されています。

2

私は、文字列ではより大きなバッファを事前に割り当てることが簡単だと言います。各要素は2バイトだけです(UNICODEの場合)ので、あなたが控えめな場合でも、文字列のかなり大きなバッファを事前に割り当てることができます。 arraysでは、各要素がObjectであるため、各要素はより複雑になります。したがって、保守的な実装では、より少ない要素のための領域が事前に割り当てられます。

forの前にfor(j=0;j<1000;j++)を追加すると、(クロムの下で)速度の差が小さくなることがわかります。結局、それは文字列の連結ではまだ1.5倍でしたが、これまでの2.6よりも小さくなりました。

要素をコピーする必要があるため、Unicode文字はおそらくJSオブジェクトへの参照よりも小さくなります。

19

FirefoxはRopes(Ropes: an Alternative to Strings)という名前を使用しているため、高速です。ロープは基本的に単なるDAGです。すべてのノードは文字列です。

たとえば、a = 'abc'.concat('def')を実行すると、新しく作成されたオブジェクトは次のようになります。 もちろん、文字列型、長さ、多分その他のフィールドを持つ必要があるので、これはメモリ内でどのように見えるか正確には分かりません。

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

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

そしてb = a.concat('123')だから、最も簡単な場合にはVMはほとんどありません作業を行う必要があります。唯一の問題は、結果として得られる文字列の他の操作が少し遅くなることです。もちろん、これによりメモリオーバーヘッドが削減されます。

一方、通常、['abc', 'def'].join('')は、メモリ内に新しい文字列をレイアウトするためにメモリを割り当てます。 (たぶんこれは最適化する必要があります)

0

これは明らかにjavascriptエンジンの実装に依存します。あるバージョンのエンジンでも、異なる結果が得られます。これを確認するには、独自のベンチマークを行う必要があります。

最近のバージョンのV8では、String.concatのほうがパフォーマンスが優れています。しかし、FirefoxとOperaの場合、Array.joinが勝者です。

1

This testには、実際にarray.joinメソッドで代入連結された文字列を使用した場合のペナルティが表示されます。割り当ての全体的なスピードはまだChrome v31の2倍ですが、結果として得られる文字列を使用しない場合と比べてもはや巨大ではありません。

3

ベンチマークは些細なものです。同じ3つの項目を繰り返して連結するとインライン展開され、結果は確定的でメモりになります。ガベージハンドラは配列オブジェクトを投げ捨てるだけです(何もサイズにならないでしょう)。文字列が決して変化しないためです。テストがランダムに生成された多数の文字列であれば、私はもっと感銘を受けたでしょう。 ギグか2の弦のように。

Array.join FTW!