なぜarr = []がarr = new Arrayより速いのですか?


146

私はこのコードを実行し、以下の結果を得ました。なぜ[]速いのか知りたいです。

console.time('using[]')
for(var i=0; i<200000; i++){var arr = []};
console.timeEnd('using[]')

console.time('using new')
for(var i=0; i<200000; i++){var arr = new Array};
console.timeEnd('using new')
  • 使用[]:299ms
  • 使用new:363ms

ここレイノスのおかげで、このコードのベンチマークと、変数を定義するためのいくつかのより可能な方法があります。

ここに画像の説明を入力してください


5
jsperfに興味があるかもしれません。
先のとがった


キーワードnewに注意してください。これは「効率を下げてください」を意味します。これは意味をなさず、最適化を行うのではなく、ブラウザが通常のインスタンス化を行う必要があります。
beatgammit

2
@きなくた どちらも、等しくない新しいオブジェクトを作成します。私が意味するの[]new Array()、ソースコードの観点から同等であり、式から返されたオブジェクトではない
Raynos

1
はい、それはそれほど重要ではありません。しかし、私は知りたいです。
Mohsen

回答:


195

以前の回答をさらに拡張しています...

一般的なコンパイラの観点から、VM固有の最適化を無視して:

まず、コードをトークン化する字句解析フェーズを実行します。

例として、以下のトークンが生成されます。

[]: ARRAY_INIT
[1]: ARRAY_INIT (NUMBER)
[1, foo]: ARRAY_INIT (NUMBER, IDENTIFIER)
new Array: NEW, IDENTIFIER
new Array(): NEW, IDENTIFIER, CALL
new Array(5): NEW, IDENTIFIER, CALL (NUMBER)
new Array(5,4): NEW, IDENTIFIER, CALL (NUMBER, NUMBER)
new Array(5, foo): NEW, IDENTIFIER, CALL (NUMBER, IDENTIFIER)

うまくいけば、これで十分な視覚化が得られるので、必要な処理の量が多く(または少なく)理解できます。

  1. 上記のトークンに基づいて、事実として、ARRAY_INITは常に配列を生成します。したがって、単純に配列を作成してデータを入力します。あいまいさに関する限り、字句解析ステージでは、ARRAY_INITがオブジェクトプロパティアクセサー(例:)obj[foo]または文字列/正規表現リテラル内のブラケット(例: "foo [] bar"または/ [] /)とすでに区別されています。

  2. これはごくわずかですが、でより多くのトークンを使用することもできますnew Array。さらに、単に配列を作成したいだけであることはまだ明確ではありません。「新しい」トークンが表示されますが、「新しい」何ですか?次に、新しい「配列」が必要であることを示すIDENTIFIERトークンが表示されますが、JavaScript VMは通常、IDENTIFIERトークンと「ネイティブグローバルオブジェクト」のトークンを区別しません。したがって...

  3. IDENTIFIERトークンに遭遇するたびに、スコープチェーンを検索する必要があります。Javascript VMには、「arguments」オブジェクト、ローカルで定義された変数などを含む可能性がある各実行コンテキストの「Activationオブジェクト」が含まれています。Activationオブジェクトで見つからない場合は、グローバルスコープに到達するまでスコープチェーンの検索を開始します。 。何も見つからない場合は、をスローしReferenceErrorます。

  4. 変数宣言を見つけたら、コンストラクターを呼び出します。 new Array暗黙の関数呼び出しであり、経験則では、実行中の関数呼び出しの速度が遅くなります(そのため、静的C / C ++コンパイラーが「関数のインライン化」を許可する理由-SpiderMonkeyなどのJS JITエンジンがオンザフライで実行する必要があるため)

  5. Arrayコンストラクタはオーバーロードされます。Arrayコンストラクターはネイティブコードとして実装されるため、パフォーマンスが向上しますが、引数の長さを確認し、それに応じて動作する必要があります。さらに、引数が1つだけ指定された場合は、引数の型をさらに確認する必要があります。new Array( "foo")は["foo"]を生成し、ここでnew Array(1)は[undefined]を生成します

つまり、すべてを単純化するために、配列リテラルを使用すると、VMは配列が必要であることを認識します。でnew Array、VMはnew Array 実際にが行われるかを理解するために追加のCPUサイクルを使用する必要があります。


次の理由により、a = new Array(1000); for(から0から999){a [i] = i}より高速ではありませんa = []; for(から0から999){a [i] = i}ただし、割り当てオーバーヘッドは?
Y.吉井

テストケースを作成しました。新しい配列(n)を速くあなたが先に時間の配列のサイズを知っている例であるjsperf.com/square-braces-vs-new-array
Y.吉井

27

考えられる理由の1つnew Arrayは、名前の検索が必要Arrayです(スコープにその名前の変数を含めることができます)が、そうではあり[]ません。


4
引数をチェックすることも貢献するかもしれません。
レオニード

Array1つの引数lenと複数の引数の両方を除きます。where []は複数の引数のみを受け入れます。また、Firefoxテストでもほとんど違いはありません。
レイノス'10

それにはいくつかの真実があると思います。IIFEでOPのループテストを実行すると、(比較的)パフォーマンスに大きな影響があります。含めるvar Array = window.Arrayと、new Arrayテストのパフォーマンスが向上します。
user113716

このconsole.time( 'more vars new');ので、私はそうではないと思います。for(var i = 0; i <200000; i ++){var arr = new Array()}; console.timeEnd( 'more vars new'); more vars new:390msとこのconsole.time( 'more vars new'); var myOtherObject = {}、myOtherArray = []; for(var i = 0; i <200000; i ++){var arr = new Array()}; console.timeEnd( 'more vars new'); more vars new:369ms同時に戻る
Mohsen

2

良い質問。最初の例は、配列リテラルと呼ばれます。これは、多くの開発者の間で配列を作成するために推奨される方法です。リテラルが配列を直接作成する一方で、パフォーマンスの違いは、new Array()呼び出しの引数をチェックしてからオブジェクトを作成することが原因である可能性があります。

パフォーマンスの比較的小さな違いは、この点をサポートしていると思います。ところで、オブジェクトとオブジェクトリテラル{}を使用して同じテストを行うことができます。


1

これは理にかなっています

オブジェクトリテラルを使用すると、多くの機能をサポートしながら、コードの実装者にとって比較的単純なコードを作成できます。コンストラクタを直接呼び出したり、関数に渡される引数の正しい順序を維持したりする必要はありません。

http://www.dyn-web.com/tutorials/obj_lit.php


1

また、興味深いことに、配列長さが事前にわかっている場合(要素は作成直後に追加されます)、指定れた長さの配列コンストラクターを使用すると、最近のGoogle Chrome 70以降ではるかに高速になります。

  • " new Array( %ARR_LENGTH% " – 100%(速い)

  • []」– 160〜170%(遅い)

対策の結果を示すグラフ。

テストはここにあります-https://jsperf.com/small-arr-init-with-known-length-brackets-vs-new-array/2

注:この結果はGoogle Chrome v.70以降でテストされました。でFirefoxのv.70 IEとの両方ほぼ同じ変異体です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.