web-dev-qa-db-fra.com

Pour les performances VS Foreach on Array (en AS3/Flex)

Lequel est le plus rapide? Pourquoi?

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

De là où je suis assis, les boucles for normales sont modérément plus rapides que les boucles for each dans le cas minimal. De même, comme pour les jours AS2, le fait de décrémenter votre chemin à travers une boucle for fournit généralement une amélioration très mineure.

Mais vraiment, toute légère différence ici sera minimisée par les exigences de ce que vous faites réellement dans la boucle. Vous pouvez trouver des opérations qui fonctionneront plus vite ou plus lentement dans les deux cas. La vraie réponse est qu’aucun type de boucle ne peut être considéré comme plus rapide que l’autre - vous devez profiler votre code tel qu’il apparaît dans votre application.

Exemple de code:

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");

Résultats:

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

Edit: Pour améliorer la comparaison, j'ai modifié les boucles internes afin qu'elles ne fassent que accéder à la valeur de la collection.

Edit 2: Réponses au commentaire de oshyshko:

  1. Le compilateur peut ignorer les accès de mes boucles internes, mais ce n'est pas le cas. Les boucles sortiraient deux ou trois fois plus vite si c'était le cas.
  2. Les résultats changent dans l'exemple de code que vous avez publié car dans cette version, la boucle for a maintenant une conversion de type implicite. J'ai laissé des tâches en dehors de mes boucles pour éviter cela. Bien sûr, on pourrait dire qu’il est normal d’avoir une distribution supplémentaire dans la boucle for car le "code réel" en aurait besoin de toute façon, mais pour moi, c’est une autre façon de dire "il n’ya pas de réponse générale; la boucle la plus rapide dépend de ce que vous faire dans votre boucle ". Quelle est la réponse que je vous donne. ;)
23
fenomas

J'ai eu cette discussion avec quelques collègues avant, et nous avons tous trouvé des résultats différents pour différents scénarios. Cependant, il y avait un test que j'ai trouvé assez éloquent à des fins de comparaison:

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);
}

Ce qui me plaît dans ces tests, c’est que vous avez une référence à la fois pour la clé et pour la valeur dans chaque itération des deux boucles (la suppression du compteur de clé dans la boucle "pour-chaque" n’est pas pertinente). En outre, il fonctionne avec Number, qui est probablement la boucle la plus courante que vous souhaiterez optimiser autant. Et plus important encore, le gagnant est le "pour-chacun", qui est ma boucle préférée: P

Remarques:

-Référencer le tableau dans une variable locale dans la fonction de la boucle "pour-chaque" n'a aucune importance, mais dans la boucle "pour", vous obtenez un ralentissement (75 ms au lieu de 105 ms):

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);
}

-Si vous exécutez les mêmes tests avec la classe Vector, les résultats sont un peu déroutants: S

2
Cay

Lors de l'itération sur un tableau, chaque boucle est beaucoup plus rapide dans mes tests.

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();

Cela donne:

pourChaqueLoop: 87 whileLoop: 967

Ici, la majeure partie de la boucle while est probablement consacrée à la conversion de l'élément de tableau en nombre. Cependant, je considère que c'est une comparaison juste, puisque c'est ce que vous obtenez dans chaque boucle.

Je suppose que cette différence tient au fait que, comme mentionné, l’opérateur as est relativement coûteux et que l’accès au réseau est également relativement lent. Avec une boucle pour chaque boucle, les deux opérations sont gérées de manière native, comme je le suppose, dans Opérations.

Notez, cependant, que si la conversion de type a effectivement lieu, le pour chaque version est beaucoup plus lent et la version tant que si sensiblement plus rapide (cependant, pour chaque temps):

Pour tester, changez l'initialisation du tableau en ceci:

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

Et maintenant, les résultats sont:

pourChaqueLoop: 328 whileLoop: 366

pourEachLoop: 324 whileLoop: 369

2

car serait plus rapide pour les tableaux ... mais en fonction de la situation, cela peut être le meilleur choix ... voir ce test de référence .net .

Personnellement, je l’utilisais jusqu’au moment où j’ai eu l’obligation d’optimiser le code. L'optimisation prématurée est un gaspillage :-)

1
mezoid

Peut-être que dans un tableau où tous les éléments sont présents et commencent à zéro (0 à X), il serait plus rapide d'utiliser une boucle for. Dans tous les autres cas (tableau fragmenté), il peut être beaucoup plus rapide à utiliser pour chacun. La raison en est l'utilisation de deux structures de données dans le tableau: Hast table et Debse Array. Veuillez lire mon analyse Array en utilisant la source Tamarin: http://jpauclair.wordpress.com/2009/12/02/tamarin-part-i-as3-array/

La boucle for vérifiera à l'index non défini où le pour chacun sautera à celui qui sautera au prochain élément de la table Hast

0
jpauclair

les mecs! Surtout Juan Pablo Califano . J'ai vérifié ton test. La principale différence dans l'obtention d'un élément de tableau. Si vous mettez var len : int = 40000;, vous verrez que le cycle 'while' est plus rapide. Mais il perd avec un grand nombre de tableaux, au lieu de .. chacun.

0
dimpiax