今日、私は文字列連結の速度についてthis threadを読んでいます。文字列の連結が配列結合よりも速いのはなぜですか?
は驚くべきことに、文字列の連結は、受賞した:
結果は、私が考えたものとは逆でした。また、反対に説明するこの記事については、thisやthisなど多くの記事があります。
ブラウザは最新のバージョンでconcat
という文字列に最適化されていると推測できますが、どのようにしてそれを行うのですか?文字列を連結するときに+
を使用する方が良いと言うことができますか?
今日、私は文字列連結の速度についてthis threadを読んでいます。文字列の連結が配列結合よりも速いのはなぜですか?
は驚くべきことに、文字列の連結は、受賞した:
結果は、私が考えたものとは逆でした。また、反対に説明するこの記事については、thisやthisなど多くの記事があります。
ブラウザは最新のバージョンでconcat
という文字列に最適化されていると推測できますが、どのようにしてそれを行うのですか?文字列を連結するときに+
を使用する方が良いと言うことができますか?
ブラウザの文字列の最適化は、文字列の連結を変更した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を検索してください。
私の推測では、すべてのバージョンで多くの連結コストがかかりますが、結合バージョンではそれに加えて配列が構築されています。
私は、文字列ではより大きなバッファを事前に割り当てることが簡単だと言います。各要素は2バイトだけです(UNICODEの場合)ので、あなたが控えめな場合でも、文字列のかなり大きなバッファを事前に割り当てることができます。 arrays
では、各要素がObject
であるため、各要素はより複雑になります。したがって、保守的な実装では、より少ない要素のための領域が事前に割り当てられます。
for
の前にfor(j=0;j<1000;j++)
を追加すると、(クロムの下で)速度の差が小さくなることがわかります。結局、それは文字列の連結ではまだ1.5倍でしたが、これまでの2.6よりも小さくなりました。
要素をコピーする必要があるため、Unicode文字はおそらくJSオブジェクトへの参照よりも小さくなります。
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('')
は、メモリ内に新しい文字列をレイアウトするためにメモリを割り当てます。 (たぶんこれは最適化する必要があります)
これは明らかにjavascriptエンジンの実装に依存します。あるバージョンのエンジンでも、異なる結果が得られます。これを確認するには、独自のベンチマークを行う必要があります。
最近のバージョンのV8では、String.concat
のほうがパフォーマンスが優れています。しかし、FirefoxとOperaの場合、Array.join
が勝者です。
This testには、実際にarray.joinメソッドで代入連結された文字列を使用した場合のペナルティが表示されます。割り当ての全体的なスピードはまだChrome v31の2倍ですが、結果として得られる文字列を使用しない場合と比べてもはや巨大ではありません。
ベンチマークは些細なものです。同じ3つの項目を繰り返して連結するとインライン展開され、結果は確定的でメモりになります。ガベージハンドラは配列オブジェクトを投げ捨てるだけです(何もサイズにならないでしょう)。文字列が決して変化しないためです。テストがランダムに生成された多数の文字列であれば、私はもっと感銘を受けたでしょう。 ギグか2の弦のように。
Array.join FTW!
[This code](https://jsfiddle.net/8jyer0tp/)は500テラバイトのガベージを生成するはずですが、200ミリ秒で実行されます。 私は、文字列に少しだけスペースを割り当てていると思います。短い文字列を追加すると、通常余分なスペースに収まります。 –