web-dev-qa-db-ja.com

アレイパフォーマンスに関するVSForeachの場合(AS3 / Flex)

どちらが速いですか?どうして?

var messages:Array = [.....]

// 1 - for
var len:int = messages.length;
for (var i:int = 0; i < len; i++) {
    var o:Object = messages[i];
    // ...
}

// 2 - foreach
for each (var o:Object in messages) {
    // ...
}
16
oshyshko

私が座っているところから、通常のforループは、最小限の場合のfor eachループよりも適度に高速です。また、AS2日と同様に、forループを介してデクリメントすると、通常、ごくわずかな改善が得られます。

しかし実際には、ここでのわずかな違いは、ループ内で実際に行うことの要件によって小さくなります。どちらの場合でも、より速くまたはより遅く動作する操作を見つけることができます。本当の答えは、どちらの種類のループも他のループよりも高速であると意味のある意味で言うことはできないということです。アプリケーションに表示されるコードをプロファイリングする必要があります。

サンプルコード:

var size:Number = 10000000;
var arr:Array = [];
for (var i:int=0; i<size; i++) { arr[i] = i; }
var time:Number, o:Object;

// for()
time = getTimer();
for (i=0; i<size; i++) { arr[i]; }
trace("for test: "+(getTimer()-time)+"ms");

// for() reversed
time = getTimer();
for (i=size-1; i>=0; i--) { arr[i]; }
trace("for reversed test: "+(getTimer()-time)+"ms");

// for..in
time = getTimer();
for each(o in arr) { o; }
trace("for each test: "+(getTimer()-time)+"ms");

結果:

for test: 124ms
for reversed test: 110ms
for each test: 261ms

編集:比較を改善するために、内部ループを変更して、コレクション値にアクセスするだけになるようにしました。

編集2:oshyshkoのコメントへの回答:

  1. コンパイラーは内部ループのアクセスをスキップできますが、スキップしません。もしそうなら、ループは2、3倍速く終了します。
  2. 投稿したサンプルコードでは、forループに暗黙の型変換があるため、結果が変わります。それを避けるために、割り当てをループから外しました。もちろん、「実際のコード」はとにかくそれを必要とするので、forループに余分なキャストを入れても大丈夫だと主張することもできますが、私にとっては、「一般的な答えはありません。どのループがより速くは、ループ内で何をするかに依存します。」それが私があなたに与えている答えです。 ;)
23
fenomas

私は以前に数人の同僚とこの議論をしましたが、シナリオごとに異なる結果が見つかりました。ただし、比較のために非常に雄弁であることがわかったテストが1つありました。

var array:Array=new Array();
for (var k:uint=0; k<1000000; k++) {
    array.Push(Math.random());
}

stage.addEventListener("mouseDown",foreachloop);
stage.addEventListener("mouseUp",forloop);

/////// Array /////

/* 49ms */
function foreachloop(e) {
    var t1:uint=getTimer();
    var tmp:Number=0;
    var i:uint=0;
    for each (var n:Number in array) {
        i++;
        tmp+=n;
    }
    trace("foreach", i, tmp, getTimer() - t1);
}
/***** 81ms  ****/
function forloop(e) {
    var t1:uint=getTimer();
    var tmp:Number=0;
    var l:uint=array.length;
    for(var i:uint = 0; i < l; i++)
        tmp += Number(array[i]);
    trace("for", i, tmp, getTimer() - t1);
}

このテストで私が気に入っているのは、両方のループの各反復でキーと値の両方の参照があることです(「for-each」ループでキーカウンターを削除することはそれほど重要ではありません)。また、Numberで動作します。これは、おそらく、それだけ最適化する必要がある最も一般的なループです。そして最も重要なのは、勝者は「for-each」であり、これは私のお気に入りのループです:P

ノート:

-「for-each」ループの関数内でローカル変数内の配列を参照することは関係ありませんが、「for」ループではスピードバンプが発生します(105msではなく75ms)。

function forloop(e) {
    var t1:uint=getTimer();
    var tmp:Number=0;
    var a:Array=array;
    var l:uint=a.length;
    for(var i:uint = 0; i < l; i++)
        tmp += Number(a[i]);
    trace("for", i, tmp, getTimer() - t1);
}

-Vectorクラスで同じテストを実行すると、結果は少し混乱します:S

2
Cay

配列を反復処理する場合、各ループは私のテストでははるかに高速です。

var len:int = 1000000;
var i:int = 0;
var arr:Array = [];

while(i < len) {
    arr[i] = i;
    i++;
}

function forEachLoop():void {
    var t:Number = getTimer();
    var sum:Number = 0;
    for each(var num:Number in arr) {
        sum += num;
    }
    trace("forEachLoop :", (getTimer() - t));
}

function whileLoop():void {
    var t:Number = getTimer();
    var sum:Number = 0;
    var i:int = 0;
    while(i < len) {
        sum += arr[i] as Number;                
        i++;
    }
    trace("whileLoop :", (getTimer() - t));
}

forEachLoop();
whileLoop();

これは与える:

forEachLoop:87 whileLoop:967

ここでは、おそらくほとんどのwhileループ時間が配列アイテムをNumberにキャストするのに費やされています。ただし、これは公正な比較だと思います。これは、for各ループで得られるものだからです。

私の推測では、この違いは、前述のように、as演算子は比較的高価であり、配列アクセスも比較的遅いという事実に関係していると思います。 for eachループを使用すると、Actionscriptで実行するのとは異なり、両方の操作がネイティブに処理されると思います。

ただし、型変換が実際に行われる場合、各バージョンのforははるかに遅く、whileバージョンは著しく速い場合(ただし、各ビートwhile)に注意してください。

テストするには、アレイの初期化を次のように変更します。

while(i < len) {
    arr[i] = i + "";
    i++;
}

そして今、結果は次のとおりです。

forEachLoop:328 whileLoop:366

forEachLoop:324 whileLoop:369

2

forはアレイの方が高速ですが、状況によってはforeachが最適な場合があります...これを参照してください 。netベンチマークテスト

個人的には、コードを最適化する必要が生じるまで、どちらかを使用していました。時期尚早の最適化は無駄です:-)

1
mezoid

たぶん、すべての要素がそこにあり、ゼロ(0からX)で始まる配列では、forループを使用する方が高速です。他のすべての場合(スパース配列)は、それぞれに使用する方がはるかに高速です。その理由は、配列内で2つのデータ構造を使用しているためです。HastテーブルとDebse配列です。タマリンソースを使用した私のアレイ分析を読んでください: http://jpauclair.wordpress.com/2009/12/02/tamarin-part-i-as3-array/

Forループは、未定義のインデックスをチェックします。foreachは、HastTableの次の要素にジャンプするインデックスをスキップします。

0
jpauclair

みんな!特にフアンパブロカリファノ。私はあなたのテストをチェックしました。配列アイテムの取得の主な違い。あなたが置くならvar len : int = 40000;、「while」サイクルの方が速いことがわかります。しかし、それぞれの代わりに、配列の数が多いと失われます。

0
dimpiax