294 votes

JavaScript - Les boucles sont-elles vraiment plus rapides à l'envers... ?

J'ai entendu ça plusieurs fois. Les boucles JavaScript sont-elles vraiment plus rapides lorsqu'on compte à rebours ? Si oui, pourquoi ? J'ai vu quelques exemples de suites de tests montrant que les boucles inversées sont plus rapides, mais je n'ai pas trouvé d'explication à ce sujet !

Je suppose que c'est parce que la boucle ne doit plus évaluer une propriété à chaque fois qu'elle vérifie si elle est terminée et qu'elle vérifie simplement la valeur numérique finale.

I.e.

for (var i = count - 1; i >= 0; i--)
{
  // count is only evaluated once and then the comparison is always on 0.
}

Merci d'avance à toute personne qui pourra satisfaire ma curiosité professionnelle !

6 votes

À l'envers for est plus rapide parce que la variable de contrôle de la boucle à limite supérieure (hehe, à limite inférieure) ne doit pas être définie ou récupérée à partir d'un objet ; c'est une constante zéro.

3 votes

Avez-vous essayé un fiddler ou tout autre outil de performance pour javascript ?

0 votes

@Floradu88 : Vraiment, pas encore mais je crois que c'est correct. J'ai vu cela dans plusieurs articles et suggestions d'éditeurs :)

938voto

alestanis Points 11214

Ce n'est pas que i-- est plus rapide que i++ . En fait, ils sont tous les deux aussi rapides.

Ce qui prend du temps dans les boucles ascendantes, c'est d'évaluer, pour chaque i la taille de votre tableau. Dans cette boucle :

for(var i = array.length; i--; )

Vous évaluez .length une seule fois, lorsque vous déclarez i alors que pour cette boucle

for(var i = 1; i <= array.length; i++ )

vous évaluez .length à chaque fois que vous incrémentez i lorsque vous vérifiez si i <= array.length .

Dans la plupart des cas, vous ne devrait même pas s'inquiéter de ce genre d'optimisation .

30 votes

Est-il utile d'introduire une variable pour array.length et de l'utiliser dans la tête de la boucle for ?

44 votes

@ragatskynet : Non, sauf si vous mettez en place un benchmark et que vous voulez faire un point.

2 votes

Je pense que cela peut être trompeur... i-- est toujours évalué et comparé à 0 à chaque cycle. Vous voulez dire qu'il est plus efficace de comparer i à 0 qu'à la longueur ? Cela me semble douteux.

207voto

Tom Ritter Points 44352

Ce type comparé à beaucoup de boucles en javascript, dans beaucoup de navigateurs. Il a aussi un suite de tests pour que vous puissiez les exécuter vous-même.

Dans tous les cas (sauf si j'en ai oublié un dans ma lecture) la boucle la plus rapide était :

var i = arr.length; //or 10
while(i--)
{
  //...
}

0 votes

Joli :) Mais il n'y a pas de boucle "for" inversée testée... Mais les boucles for mentionnées par peirix ou searlea devraient être à peu près les mêmes que la boucle "while" avec "i--" comme condition. Et c'était la boucle la plus rapide testée.

11 votes

Intéressant, mais je me demande si un pré-décrément serait encore plus rapide. Puisqu'il n'aura pas à stocker la valeur intermédiaire de i.

0 votes

Si je me souviens bien, mon professeur de matériel informatique m'a dit que le test pour 0 ou non 0 est le "calcul" le plus rapide possible. Dans while(i--) le test est toujours un test pour 0. C'est peut-être pour cela que c'est le plus rapide ?

132voto

Barnabas Szabolcs Points 4109

J'essaie de donner une vue d'ensemble avec cette réponse.

Les réflexions suivantes entre parenthèses était ma conviction jusqu'à ce que j'aie récemment testé le problème :

[[En termes de les langages de bas niveau comme C / C++ le code est compilé de manière à ce que le processeur dispose d'une commande de saut conditionnel spéciale lorsqu'une variable est nulle (ou non nulle).
De plus, si vous vous souciez d'une telle optimisation, vous pouvez opter pour ++i au lieu de i++ parce que ++i est une commande pour un seul processeur alors que i++ signifie j=i+1, i=j .]]

Les boucles très rapides peuvent être réalisées en les déroulant :

for(i=800000;i>0;--i)
    do_it(i);

Il peut être beaucoup plus lent que

for(i=800000;i>0;i-=8)
{
    do_it(i); do_it(i-1); do_it(i-2); ... do_it(i-7);
}

mais les raisons de cette situation peuvent être assez compliquées (pour ne citer que les problèmes de prétraitement des commandes du processeur et de gestion du cache dans le jeu).

En termes de langages de haut niveau comme JavaScript comme vous l'avez demandé, vous pouvez optimiser les choses si vous vous appuyez sur des bibliothèques, des fonctions intégrées pour le bouclage. Laissez-les décider de la meilleure façon de procéder.

Par conséquent, en JavaScript, je suggère d'utiliser quelque chose comme

array.forEach(function(i) {
    do_it(i);
});

Il est également moins sujet aux erreurs et les navigateurs ont une chance d'optimiser votre code.

REMARQUE : les navigateurs ne sont pas les seuls à disposer d'un espace à optimiser facilement, il suffit de redéfinir l'élément forEach (en fonction du navigateur) pour qu'elle utilise les meilleures astuces les plus récentes ! :) @A.M.K. dit que dans des cas particuliers, il est préférable d'utiliser plutôt array.pop ou array.shift . Si vous le faites, mettez-le derrière le rideau. Le site exagération extrême est d'ajouter des options à forEach pour sélectionner l'algorithme de bouclage].

De plus, même pour les langages de bas niveau, la meilleure pratique consiste à utiliser une fonction intelligente de la bibliothèque pour les opérations complexes et en boucle, si cela est possible.

Ces bibliothèques peuvent également mettre des choses (multithread) dans votre dos et des programmeurs spécialisés les maintiennent à jour.

J'ai fait un peu plus de recherches et il s'avère qu'en C/C++, même pour 5e9 = (50.000x100.000) opérations, il n'y a pas de différence entre monter et descendre si le test est fait par rapport à une constante comme le dit @alestanis. (Les résultats de JsPerf sont parfois incohérents mais dans l'ensemble disent la même chose : on ne peut pas faire une grande différence).
Alors --i se trouve être une chose plutôt "chic". Cela ne fait que vous faire passer pour un meilleur programmeur :)

Par contre, pour le déroulement dans cette situation de 5e9, il m'a fait passer de 12 sec à 2,5 sec quand je suis passé par 10s, et à 2,1 sec quand je suis passé par 20s. C'était sans optimisation, et l'optimisation a ramené les choses à un petit temps non mesurable :) (Le déroulement peut être fait de la manière décrite ci-dessus ou en utilisant i++ mais cela ne fait pas avancer les choses en JavaScript. )

Dans l'ensemble : garder i-- / i++ et ++i / i++ différences aux entretiens d'embauche, tenez-vous en à array.forEach ou d'autres fonctions complexes de la bibliothèque lorsqu'elles sont disponibles ;)

0 votes

Merci, j'ai eu la chance l'année dernière d'avoir un aperçu des techniques d'optimisation : on nous a donné un truc en boucle à optimiser dans un concours. Ce n'était pas beaucoup plus compliqué que cela et je ne voyais pas si je pouvais faire quelque chose de sensé. Puis des gars qui utilisaient des bibliothèques intelligentes et du for-unlooping avaient des codes 10 à 20 fois plus rapides. J'étais choqué... puis je me suis lancé dans le déverrouillage d'une fonction de filtrage d'image : 4x plus rapide. Il est surprenant que l'on puisse être plus rapide en faisant plus d'opérations unitaires.

18 votes

Le mot clé est "peut être". Votre boucle déroulée peut également être plus lente que la boucle d'origine. Lorsque vous optimisez, prenez toujours des mesures afin de connaître exactement l'impact de vos modifications.

1 votes

@jalf vrai en effet, +1. Différentes longueurs de déliaison (>=1) sont différentes en efficacité. C'est pourquoi il est plus pratique de laisser ce travail aux bibliothèques si possible, sans parler du fait que les navigateurs fonctionnent sur des architectures différentes et qu'il serait donc préférable qu'ils décident de la façon de faire array.each(...) . Je ne pense pas qu'ils essaieraient d'expérimenter le déblocage de simples boucles for.

35voto

RoManiac Points 1278

i-- est aussi rapide que i++

Le code ci-dessous est aussi rapide que le vôtre, mais il utilise une variable supplémentaire :

var up = Things.length;
for (var i = 0; i < up; i++) {
    Things[i]
};

La recommandation est de NE PAS évaluer la taille du tableau à chaque fois. Pour les grands tableaux, on peut constater une dégradation des performances.

7 votes

Vous avez tout à fait tort ici. Maintenant, le contrôle des boucles nécessite des variables internes supplémentaires (i et plus) et selon le moteur JavaScript, cela peut limiter le potentiel d'optimisation du code. Les JIT traduiront les boucles simples en opcodes machine directs, mais si les boucles ont trop de variables, le JIT ne sera pas en mesure d'optimiser aussi bien. En général, le JIT est limité par l'architecture ou les registres du processeur qu'il utilise. Initialiser une fois et descendre simplement la solution la plus propre et c'est pourquoi c'est recommandé partout.

0 votes

La variable supplémentaire sera éliminée par l'optimiseur d'un interprète/compilateur commun. Le point est la 'comparaison à 0' - voir ma réponse pour plus d'explications.

2 votes

Il vaut mieux mettre "up" à l'intérieur de la boucle : for (var i=0, up=Things.length; i<up; i++) {}

24voto

dreamcrash Points 8227

Puisque le sujet vous intéresse, jetez un coup d'œil à l'article du blog de Greg Reimer sur un benchmark de boucle JavaScript, _Quel est le moyen le plus rapide de coder une boucle en JavaScript ?_ :

J'ai construit une suite de tests d'évaluation des boucles pour différentes manières de coder les boucles en JavaScript. Il en existe déjà quelques-uns, mais je n'en ai trouvé aucun qui reconnaisse la différence entre les tableaux natifs et les collections HTML.

Vous pouvez également faire un test de performance sur une boucle en ouvrant https://blogs.oracle.com/greimer/resource/loop-test.html (ne fonctionne pas si JavaScript est bloqué dans le navigateur par, par exemple, NoScript ).

EDIT :

Un benchmark plus récent créé par Milan Adamovsky peut être exécuté en temps réel ici pour différents navigateurs.

Pour un Test dans Firefox 17.0 sur Mac OS X 10.6 J'ai obtenu la boucle suivante :

var i, result = 0;
for (i = steps - 1; i; i--) {
  result += i;
}

comme le plus rapide précédé de :

var result = 0;
for (var i = steps - 1; i >= 0; i--) {
  result += i;
}

Benchmark example.

0 votes

@dreamcash Sur Chrome 88.x, toutes les boucles inversées sont toujours plus rapides que toutes les boucles avant. jsben.ch/eng8b , measurethat.net/Benchmarks/ShowResult/162677 , jsbench.me/5ykkzsysa9 . Parfois à l'envers, parfois à l'envers optimisé, parfois à l'envers pendant.

0 votes

@johnywhy okey j'ai mal compris okey donc ça fait quand même un peu de différence. Allez-vous poster une réponse avec ces résultats ?

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