6

ネイティブメソッドを使用してデフォルト値を含む配列を作成する方法を検討していたところ、最終的に

function pushMap(length, fill){
    var a = [], b = [];
    a.length = length;
    b.push.apply(b,a);
    return b.map(function(){return fill;});
}

ネイティブメソッドは2回ループする必要があるのに対し、whileループは1回だけなので、whileループよりも2倍または3倍遅くなると予想されるため、jsperfで比較しました

function whileLengthNew(len, val) {
    var rv = new Array(len);
    while (--len >= 0) {
        rv[len] = val;
    }
    return rv;
}

実際には18倍から27倍遅くなります (Ubuntu 上の Google Chrome でテスト済み、ブラウザー/OS は歓迎)。

そのような大きな違いを引き起こす何が起こっているのでしょうか?

4

4 に答える 4

2

これには、次の 2 つの主な要因があると考えています。

  1. メモリ割り当て -whileLengthNew最初に正しいサイズの配列を作成し、次にそれを操作して、pushMap一度に 1 つの要素で最終的な配列を作成しmapます。これにより、特にソース配列が大きい場合、複数の割り当てが発生する可能性があります。(初期値ab配列を作成する方法は基本的に無関係mapです。とにかく返す新しい配列を構築しているためです。実際には何も変更しませんb)

  2. 関数呼び出しのオーバーヘッド -- への呼び出しmapで、配列のすべての要素に対して関数を呼び出しています。これにはかなりのオーバーヘッドが伴います。アクティベーション レコードとスコープ チェーンの設定、スタック操作、および戻り値の受け渡し。-- これはすべて、関数内で定数である変数にアクセスするためのものです。その上、クロージャーを設定したため、変数へのアクセスもバージョンfillよりも遅くなります。whileLengthNew

于 2012-09-17T19:09:37.190 に答える
0

すべての単一の値に対して不要な関数呼び出しを行うため、JavaScript 仮想マシンがこのパターンを最適化するのが困難になり、配列は内部的に単純な配列として扱われず、複雑なデータ構造として扱われますが、while ループでは非常に単純なものを使用します配列アクセス パターンと、停止条件として rv.length を使用する for ループを使用すると、さらに高速になると思われます

于 2012-09-17T18:54:47.590 に答える
0

map() を呼び出すと、while ループと同じように実行できると思います。さらに、反復ごとに関数呼び出しを実行します。一般に、関数呼び出しは非常に遅いです。

于 2012-09-17T18:55:10.933 に答える
0

私は JavaScript の専門家ではありません。JavaScript エンジンに貢献したこともないので、以下は単なる推測です。

あなたのpushMap機能では、多くのことが起こっています。1.最初に、おそらく非常に非効率的な方法で、必要なサイズに
拡張しています。は配列の単なるプロパティであるため、基になる実装にプロパティが変更されたときのコールバックがあるか、基になる実装が長さ属性の変更を処理して、次にその中の何かがアクセスされたときにそれを処理できます。 2.特定の長さの配列を作成するためにリフレクション型のメソッドを呼び出しているため、作成はさらに効率が悪いように見えます。var alength
var bapplyb
3. 次に、基本的に機能的な方法で foreach ループを呼び出します。内部関数のオーバーヘッドのため、while ループよりも少し遅くなる可能性があります (クロージャは JS であるためだと思います)。

var bを作成するのと同じ方法で作成すると、より均等な結果が得られる場合がありますvar rv。お役に立てれば。mapは配列の初期化された値でのみ機能するため、もちろんこの状況では機能しません。これは、このアプローチが遅いもう1つの理由を意味します(そして、OPは彼の質問でこれについて言及しました)、空の値で1回、必要な値で2回、マップを初期化しています。

于 2012-09-17T18:49:05.340 に答える