67 votes

Performances de la boucle JavaScript - Pourquoi décrémenter l'itérateur vers 0 plus rapidement que l'incrémentation

Dans son livre Encore plus Rapide des Sites Web de Steve Sounders écrit qu'un moyen simple d'améliorer les performances d'une boucle est de décrémenter l'itérateur vers 0 au lieu de l'incrémentation vers la longueur totale (en fait le chapitre a été écrit par Nicholas C. Zakas). Ce changement peut entraîner des économies de jusqu'à 50% de rabais sur l'origine du délai d'exécution, en fonction de la complexité de chaque itération. Par exemple:

var values = [1,2,3,4,5];
var length = values.length;

for (var i=length; i--;) {
   process(values[i]);
}

C'est à peu près identique pour l' for boucle, l' do-while boucle, et l' while boucle.

Je me demandais, quelle est la raison? Pourquoi est-à décrémenter l'itérateur de manière beaucoup plus rapide? (Je suis intéressé par la technique de l'arrière-plan de cette et pas dans les benchmarks de prouver cette affirmation.)


EDIT: À première vue, la boucle de la syntaxe utilisée ici semble incorrect. Il n'y a pas d' length-1 ou i>=0, nous allons donc préciser (j'ai été trop confus).

Voici la boucle de la syntaxe:

for ([initial-expression]; [condition]; [final-expression])
   statement
  • initiale-expression - var i=length

    Cette déclaration de variable est évalué en premier.

  • condition - i--

    Cette expression est évaluée avant chaque itération de boucle. Il va décrémenter la variable avant le premier passage dans la boucle. Si cette expression a pour false la boucle se termine. En JavaScript est - 0 == false si i enfin égaux 0 il est interprété comme false et la boucle se termine.

  • final-expression

    Cette expression est évaluée à la fin de chaque itération de boucle (avant la prochaine évaluation de l' état). Il n'est pas nécessaire ici, et est vide. Tous les trois expressions sont optionnels dans une boucle for.

La boucle de la syntaxe n'est pas une partie de la question, mais parce que c'est un peu rare, je pense qu'il est intéressant de le préciser. Et peut-être la raison pour laquelle il est plus rapide, car il utilise moins d'expressions (l' 0 == false "truc").

66voto

Mike Dunlavey Points 25419

Je ne suis pas sûr du Javascript, et sous les compilateurs modernes, ça n'a probablement pas d'importance, mais dans les temps anciens, ce code:

 for (i = 0; i < n; i++){
  .. body..
}
 

générerait

 move register, 0
L1:
compare register, n
jump-if-negative L2
-- body ..
increment register
jump L1
L2:
 

tandis que le code de comptage en arrière

 for (i = n; --i>=0;){
  .. body ..
}
 

générerait

 move register, n
L1:
decrement-and-jump-if-negative register, L2
.. body ..
jump L1
L2:
 

donc, dans la boucle, il ne fait que deux instructions supplémentaires au lieu de quatre.

28voto

GenericTypeTea Points 27689

Je crois que la raison est parce que vous êtes en comparant le point de fin de boucle contre 0, ce qui est plus rapide puis de les comparer à nouveau < length (ou une autre variable JS).

C'est parce que l'ordinal opérateurs <, <=, >, >= sont polymorphes, de sorte que ces opérateurs exigent des vérifications de type sur les deux côtés gauche et droit de l'opérateur de déterminer quelle comparaison du comportement doit être utilisé.

Il y a quelques très bons repères disponible ici:

Quel est le Moyen le plus Rapide de coder une Boucle en JavaScript

15voto

Gumbo Points 279147

Il est facile de dire qu'une itération pouvez avoir jusqu'à 50% moins d'instructions. Nous allons donc comparer ces deux:

for (var i=0; i<length; i++) {
}

for (var i=length; i--;) {
}

Lorsque vous comptez chaque variable d'accès et de chaque exploitant d'une instruction, l'ex - for boucle utilise 5 instructions (lire en i, lisez length, évaluer i<length, test (i<length) == true, par incrément i) alors que la deuxième utilise seulement 2 instructions (évaluer i == true, décrémenter i). C'est un ratio de 5:2. Celui-ci permet de gagner encore plus de 60%.

6voto

Marco Demaio Points 8667

Qu'en est-il d'utiliser une boucle while alors:

 var values = [1,2,3,4,5]; 
var i = values.length; 

/* i is 1st evaluated and then decremented, when i is 1 the code inside the loop 
   is then processed for the last time with i = 0. */
while(i--)
{
   //1st time in here i is (length - 1) so it's ok!
   process(values[i]);
}
 

OMI celui-ci au moins est un code plus lisible que for(i=length; i--;)

3voto

nabrown78 Points 76

J'ai été d'explorer de vitesse en boucle, et était intéressé à trouver cette friandise sur la décrémentation d'être plus rapide que l'incrémentation. Cependant, je n'ai pas encore trouvé un test qui démontre. Il y a beaucoup de boucle de repères sur jsperf. Ici est l'un des tests de décrémentation:

http://jsperf.com/array-length-vs-cached/6

La mise en cache de votre tableau de longueur, cependant (également recommandé de Steve Souders livre) ne semble pas être un gagnant de l'optimisation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X