JavaScript performance de la boucle - Pourquoi est-à décrémenter l'itérateur vers 0 plus vite que l'incrémentation
Dans son livre Encore Plus Rapide Des Sites Web 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 les for
boucle, le do-while
boucle, et le 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 de length-1
ou i>=0
, nous allons donc préciser (j'ai été trop confus).
Ici est 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 est0 == false
donc sii
enfin est égal à0
il est interprété commefalse
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 condition). 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 (la 0 == false
"truc").
La résiliation condition est
i--
. Il sera à 0 (false) quand je est 0 avant de le décrémenter. Depuis que l'état a pour effet de bord de la décrémentation de la variable elle-même, il n'est pas nécessaire pour le troisième changement/incrémenter/whatever) expression dans la déclaration.Pourquoi dois-je commencer à longueur-1? Vous pouvez remplacer les processus d'alerte à vérifier. @Dave - Le terminal de condition renvoie true si le i est égal à 0
En fait, il correspond à false si i est 0. Il évalue à true, le reste du temps.
Le premier passage dans la boucle vérifie la condition-lecture: il évalue
i--
.OriginalL'auteur Soundlink | 2010-08-19
Vous devez vous connecter pour publier un commentaire.
Je ne suis pas sûr à propos de Javascript, et sous les compilateurs modernes, il n'a probablement pas d'importance, mais dans le "vieux jours" de ce code:
serait de générer des
tandis que l'arrière-code de comptage
serait de générer des
donc à l'intérieur de la boucle, elle ne fait que deux instructions supplémentaires au lieu de quatre.
JavaScript a encore un interprète, c'est à dire le navigateur, qui doit décider de la manière d'exécuter le code. Alors qu'il n'a pas exactement obtenir "compilé", il doit être envoyé au processeur d'une certaine façon. Ce qui aurait été dit, c'est interprète au lieu de compilateur, mais de là à dire qu'il ne soit pas transformé en code machine ne peut pas être tout à fait précis. Même si c'est pour le navigateur de la discrétion. Mozilla utilise Singe-Araignée par exemple
assez sûr que nous sommes en train de dire la même chose. Original implémentations de javascript se composait d'un interprète, alors que les versions modernes tels que le Singe-Araignée et V8 de manière sélective JIT compiler le code.
OriginalL'auteur Mike Dunlavey
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 que la comparaison de nouveau
< length
(ou d'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 disponibles ici:
Quel est le Moyen le plus Rapide de coder une Boucle en JavaScript
Il semble que c'est un lien mort maintenant
Toute différence entre le pré- (++i) et après (i++) dépend du navigateur de mise en œuvre de Javascript (ou est extrêmement petite). La logique de la différence est présent de plus en plus important (Douglas Crockford a souligné qu'il est terriblement facile à obtenir de la logique de mal avec ce concept.) La pré- (++i) de la version incrémente/décrémente avant le reste de l'énoncé, tandis que le post- (i++) version incrémente/décrémente après. i-- fonctionne comme la commande (1]sortie si 0, 2]décrémentation, 3]exécuter en boucle); --je manquerais que l'ordre (1]décrémentation, 2]sortie si 0, 3]exécuter en boucle) ne serait jamais la poignée de la 0e élément.
OriginalL'auteur djdd87
Il est facile de dire qu'une itération peut avoir moins d'instructions. Nous allons donc comparer ces deux:
Lorsque vous comptez chaque variable d'accès et de chaque exploitant d'une instruction, l'ancien
for
boucle utilise 5 instructions (lirei
, lirelength
, évalueri<length
, test(i<length) == true
, incrémenteri
) alors que la deuxième utilise seulement 3 des instructions (lirei
, testi == true
, décrémenteri
). C'est un ratio de 5:3.i = length
, depuis lefor
boucle fait le test conditionnel pour la première itération?Vous avez tout à fait raison, merci pour la remarque!
Vous avez raté "Lire" i " dans le comptage du nombre d'instructions dans le dernier pour la boucle qui fait le ratio de 5:3.
OriginalL'auteur Gumbo
Que sur l'utilisation d'un revers boucle while alors:
De l'OMI, ce l'un au moins est un plus readble code de
for(i=length; i--;)
Vous avez la meilleure réponse, while(i--) est le plus rapide que par : jsperf.com/while-vs-for
OriginalL'auteur Marco Demaio
J'ai été d'explorer de vitesse en boucle, et s'est 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 d'optimisation.
OriginalL'auteur nabrown
Y est encore plus "performant" version de ce.
Étant donné que chaque argument est facultatif dans les boucles for, vous pouvez ignorer même le premier.
Avec ce que vous ignorez même le contrôle sur le
[initial-expression]
. Si vous vous retrouvez avec une seule opération gauche.aussi, si certains d'opération modifie le compteur et vous "sauter" sur le zéro, vous avez obtenu une boucle infinie. accordé, un scénario improbable, mais c'est toujours une bonne idée de faire quelque chose comme
for(;i-->=0;)
OriginalL'auteur icanhazstring
for
incrément vs décrémenter en 2017Moderne JS moteurs de l'incrémentation dans
for
boucles est généralement plus rapide que la décrémentation (basé sur le Benchmark.js les tests), aussi les plus classiques:Cela dépend de la plate-forme et la longueur du tableau si
length = array.length
a aucun effet positif considérable, mais, habituellement, il n'est pas:Dernières versions V8 (Chrome, Nœud) ont optimisations pour
array.length
, donclength = array.length
peuvent être efficacement omis là en tout cas.OriginalL'auteur estus
J'ai effectué un test sur C# et C++ (même syntaxe). Il y a, en fait, la performance diffère essentiellement dans
for
boucles, comparativement àdo while
ouwhile
. En C++, la performance est plus importante lorsque l'incrémentation. Il peut également dépendre du compilateur.En Javascript, je pense, tout dépend du navigateur (moteur Javascript), mais ce comportement est attendu. Javascript est optimisé pour travailler avec les DOM. Alors, imaginez-vous en boucle à travers une collection d'éléments DOM, vous obtenez à chaque itération, et vous incrémenter un compteur, quand vous avez à les supprimer. Vous retirez le
0
élément, puis1
élément, mais ensuite vous sautez celui qui prend0
's place. Si la boucle vers l'arrière que le problème disparaît. Je sais que l'exemple donné n'est pas seulement le droit, mais je n'ai rencontrer des situations où j'ai dû supprimer des éléments à partir d'une modification de l'objet de collection.Parce que descendante de la boucle est le plus souvent inévitable qu'un transfert en boucle, je devine que le JS moteur est optimisé pour ça.
OriginalL'auteur AlexanderMP
Avez-vous programmé le vous-même? M. Sondeurs qui ne va pas avec ce qui concerne les interprètes modernes. C'est précisément le genre d'optimisation dans lequel un bon compilateur écrivain peut faire une grande différence.
Vous avez vraisemblablement au moins un navigateur web (comme en témoigne le fait que vous êtes l'affichage sur un site web). De l'essayer. 🙂
OriginalL'auteur Crashworks
Je ne suis pas sûr si c'est plus rapide mais l'une des raisons que je vois, c'est que lorsque vous parcourez un tableau des éléments d'une taille à l'aide d'incrément vous avez tendance à écrire:
Vous êtes essentiellement l'accès à la propriété length de la matrice de N (nombre d'éléments) fois.
Alors que quand vous décrémentation, vous avez accès qu'une seule fois. Que pourrait être une raison.
Mais vous pouvez aussi écrire à l'incrémentation de la boucle comme suit:
OriginalL'auteur naikus