web-dev-qa-db-ja.com

文字列の連結が配列結合よりも速いのはなぜですか?

今日、私は このスレッド 文字列連結の速度について読みました。

驚くべきことに、文字列の連結が勝者でした。

http://jsben.ch/#/OJ3vo

結果は、私が考えていたものと反対でした。その上、逆に this のように説明する記事がたくさんあります。

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

更新

そのため、最新のブラウザでは文字列の連結が最適化されているため、文字列をconcatenateしたい場合は、joinを使用するよりも+記号を使用する方が高速です。

しかし、 @ Arthurが指摘した セパレーターで文字列をjoinしたい場合、joinの方が高速です。

107
Sanghyun Lee

ブラウザの文字列の最適化により、文字列の連結画像が変更されました。

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

効率的なJavaScriptの記述:第7章–さらに高速なWebサイト

V8 javascriptエンジン(Google Chromeで使用)は このコード を使用して文字列の連結を行います。

// 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)を検索してください。

143
Daan

Firefoxは、Ropes( Ropes:an Alternative to Strings )と呼ばれるものを使用するため、高速です。ロープは基本的にDAGであり、すべてのNodeは文字列です。

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

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

そしてb = a.concat('123')

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

そのため、最も単純なケースでは、VMはほとんど作業を行う必要がありません。唯一の問題は、結果の文字列に対する他の操作が少し遅くなることです。また、これはもちろんメモリのオーバーヘッドを削減します。

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

22
evilpie

これは古いスレッドですが、テストが正しくありません。あなたはoutput += myarray[i];をしているが、それはoutput += "" + myarray[i];に似ているはずだが、忘れてしまったので、アイテムを何かと接着する必要がある。連結コードは次のようになります。

var output = myarray[0];
for (var i = 1, len = myarray.length; i<len; i++){
    output += "" + myarray[i];
}

そうすれば、要素を接着するため、1つではなく2つの操作を実行します。

Array.join()は高速です。

5
Arthur

ベンチマークは簡単です。同じ3つの項目を繰り返し連結すると、インライン化され、結果が確定的であり、メモされていることが証明され、ガベージハンドラーは配列オブジェクト(サイズがほとんどない)を破棄し、おそらく外部参照。文字列は決して変更されないため。テストが多数のランダムに生成された文字列であった場合、私はより感銘を受けるでしょう。ギグや2つ分のストリングスのように。

Array.join FTW!

3
Jeremy Moritz

文字列を使用すると、より大きなバッファを事前に割り当てるのが簡単になります。各要素は2バイトしかないため(UNICODEの場合)、保守的であっても、文字列にかなり大きなバッファーを事前に割り当てることができます。 arraysの場合、各要素はObjectであるため、各要素はより「複雑」です。したがって、保守的な実装では、より少ない要素にスペースを事前に割り当てます。

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

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

JSエンジンの多くの実装では、単一タイプの配列が最適化されている可能性があることに注意してください。

2
xanatos

大量のデータの結合はより高速であるため、質問の記述が間違っています。

let result = "";
let startTime = new Date().getTime();
for (let i = 0; i < 2000000; i++) {
    result += "x";
}
console.log("concatenation time: " + new Date().getTime() - startTime);

startTime = new Date().getTime();
let array = new Array(2000000);
for (let i = 0; i < 2000000; i++) {
    array[i] = "x";
}
result = array.join("");
console.log("join time: " + new Date().getTime() - startTime);

Chrome 72.0.3626.119、Firefox 65.0.1、Edge 42.17134.1.0でテスト済み。配列の作成が含まれていても高速であることに注意してください!

1
almostA

このテスト は、割り当て連結で作成された文字列とarray.joinメソッドで作成された文字列を実際に使用した場合のペナルティを示しています。割り当ての全体的な速度はChrome v31ではまだ2倍ですが、結果の文字列を使用しない場合ほど大きくはありません。

1
srgstm

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

String.concatは、V8の最近のバージョンでパフォーマンスが向上していると思います。しかし、FirefoxとOperaでは、Array.joinが勝者です。

0
Vanuan