web-dev-qa-db-fra.com

Les boucles sont-elles vraiment plus rapides en sens inverse?

J'ai entendu cela plusieurs fois. Les boucles JavaScript sont-elles vraiment plus rapides lorsque l'on compte en arrière? Si oui, pourquoi? J'ai vu quelques exemples de suites de tests montrant que les boucles inversées sont plus rapides, mais je ne trouve aucune explication quant à pourquoi!

Je suppose que c'est parce que la boucle n'a 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.

C'est à dire.

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

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

Ce qui prend du temps dans les boucles croissantes évalue, 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 devriez même pas vous soucier de ce type d’optimisation.

881
alestanis

Ce mec a comparé beaucoup de boucles en javascript, dans beaucoup de navigateurs. Il a également un suite de tests afin 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--)
{
  //...
}
194
Tom Ritter

J'essaie de donner une image large avec cette réponse.

Les pensées suivantes entre parenthèses correspondaient à jusqu'à ce que je teste le problème récemment:

[[En termes de langages de bas niveau comme C / C++ , le code est compilé de sorte que le processeur dispose d'une commande de saut conditionnel spéciale lorsqu'une variable est égale à zéro (ou différente de zéro).
De plus, si cette optimisation vous intéresse, vous pouvez utiliser ++i au lieu de i++, car ++i est une commande à processeur unique, alors que i++ signifie j=i+1, i=j.]]

Des boucles vraiment rapides peuvent être faites 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 peuvent être assez compliquées (pour ne rien dire, il y a 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 comptez sur des bibliothèques, dans les fonctions de bouclage. Laissez-les décider de la meilleure façon de le faire.

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

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

Il est également moins sujet aux erreurs et les navigateurs ont la possibilité d’optimiser votre code.

[REMARQUE: non seulement les navigateurs, mais vous aussi, vous disposez d'un espace pour optimiser facilement votre travail. Il vous suffit de redéfinir la fonction forEach (en fonction du navigateur) afin qu'elle utilise les dernières meilleures astuces! :) @ AM.K. dit dans des cas particuliers, il vaut mieux utiliser array.pop ou array.shift. Si vous faites cela, mettez-le derrière le rideau. Le plus grand dépassement consiste à ajouter des options à forEach pour sélectionner l'algorithme de mise en boucle.]

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

Ces bibliothèques peuvent également mettre des objets (multi-threads) derrière votre dos et des programmeurs spécialisés les tiennent à jour.

J'ai examiné un peu plus et il s'est avéré qu'en C/C++, même pour les opérations 5e9 = (50 000 x 100 000), , il n'y a pas de différence entre monter et descendre si le test est effectué sur une constante comme le dit @alestanis. (Les résultats de JsPerf sont parfois incohérents, mais en gros ils disent la même chose: vous ne pouvez pas faire une grande différence.)
Donc --i se trouve être plutôt une chose "chic". Cela vous donne seulement l'air d'un meilleur programmeur. :)

Par contre, dans cette situation de 5e9, cela m’a ramené de 12 secondes à 2,5 secondes lorsque j’ai progressé de 10 secondes et à 2,1 secondes lorsque j’ai dépassé les 20 ans. C'était sans optimisation, et l'optimisation a ramené les choses à un temps infranchissable. :) (Le déroulage peut être fait de la manière indiquée ci-dessus ou en utilisant i++, mais cela ne fait pas avancer les choses en JavaScript.)

En résumé: conserve i--/i++ et ++i/i++ différences par rapport à l'emploi. interviews, respectez array.forEach ou d’autres fonctions de bibliothèque complexes, le cas échéant. ;)

127
Barnabas Szabolcs

i-- est aussi rapide que i++

Ce code ci-dessous est aussi rapide que le vôtre, mais 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 voir la dégradation des performances.

33
sainiuc

Puisque vous êtes intéressé par le sujet, jetez un coup d'œil au billet de blog de Greg Reimer sur le point de repère d'une boucle JavaScript, Quel est le moyen le plus rapide de coder une boucle en JavaScript?:

J'ai construit une suite de tests comparatifs de boucles pour différentes manières de coder des 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 effectuer 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 exemple , NoScript ).

EDIT:

Un repère plus récent créé par Milan Adamovsky peut être exécuté au moment de l'exécution ici pour différents navigateurs.

Pour un test dans Firefox 17.0 sous Mac OS X 10.6 , j'ai 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.

21
dreamcrash

Ce n'est pas le -- ou ++, c'est l'opération de comparaison. Avec --, vous pouvez utiliser une comparaison avec 0, tandis qu'avec ++, vous devez le comparer à la longueur. Sur le processeur, la comparaison avec zéro est normalement disponible, tandis que la comparaison avec un entier fini nécessite une soustraction.

a++ < length

est en fait compilé comme

a++
test (a-length)

Donc, cela prend plus de temps sur le processeur lors de la compilation.

19
Dr BDO Adams

Réponse courte

Pour le code normal, en particulier dans un langage de haut niveau tel que JavaScript , il n'y a pas de différence de performance entre i++ et i--.

Le critère de performance est l'utilisation dans la boucle for et dans l'instruction compare.

Ceci s'applique à tous les langages de haut niveau et est en grande partie indépendant de l'utilisation de JavaScript. L'explication est le code d'assembleur résultant sur la dernière ligne.

Explication détaillée

Une différence de performance peut se produire dans une boucle. Le contexte est que, au niveau du code de l'assembleur , vous pouvez voir qu'un compare with 0 n'est qu'une instruction qui ne nécessite pas de registre supplémentaire.

Cette comparaison est émise à chaque passage de la boucle et peut entraîner une amélioration mesurable des performances.

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

sera évalué à un pseudo code comme ceci:

 i=array.length
 :LOOP_START
 decrement i
 if [ i = 0 ] goto :LOOP_END
 ... BODY_CODE
 :LOOP_END

Notez que 0 est un littéral ou, en d'autres termes, une valeur constante.

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

sera évalué à un pseudo code comme ceci (optimisation d'interprète normale supposée):

 end=array.length
 i=0
 :LOOP_START
 if [ i < end ] goto :LOOP_END
 increment i
 ... BODY_CODE
 :LOOP_END

Notez que end est une variable qui nécessite un registre CPU . Cela peut appeler un échange de registre supplémentaire dans le code et nécessite un instruction de comparaison plus coûteuse dans l'instruction if.

Juste mes 5 cents

Pour un langage de haut niveau, la lisibilité, qui facilite la maintenabilité, est plus importante en tant qu'amélioration de performance mineure.

Normalement, l'itération classique du tableau début à la fin est meilleure.

L'itération la plus rapide du tableau fin du début ​​entraîne la séquence inversée éventuellement indésirable.

Post Scriptum

Comme demandé dans un commentaire: La différence de --i et i-- réside dans l'évaluation de i avant ou après la décrémentation.

La meilleure explication est de l'essayer ;-) Voici un exemple Bash .

 % i=10; echo "$((--i)) --> $i"
 9 --> 9
 % i=10; echo "$((i--)) --> $i"
 10 --> 9
15
H.-Dirk Schmitt

J'ai vu la même recommandation dans Sublime Text 2.

Comme il a déjà été dit, l’amélioration principale n’est pas l’évaluation de la longueur du tableau à chaque itération de la boucle for. C'est une technique d'optimisation bien connue et particulièrement efficace en JavaScript lorsque le tableau fait partie du document HTML (faire un for pour tous les éléments li.).

Par exemple,

for (var i = 0; i < document.getElementsByTagName('li').length; i++)

est beaucoup plus lent que

for (var i = 0, len = document.getElementsByTagName('li').length; i < len; i++)

De mon état actuel, l’amélioration principale de la forme de votre question réside dans le fait qu’elle ne déclare pas de variable supplémentaire (len dans mon exemple).

Mais si vous me demandez, l’important n’est pas d’optimisation i++ vs i--, mais de ne pas avoir à évaluer la longueur du tableau à chaque itération (vous pouvez voir un test de référence sur - jsperf ).

14
BBog

Comme aucune des autres réponses ne semble répondre à votre question spécifique (plus de la moitié d'entre elles montrent des exemples en C et discutent de langages de niveau inférieur, votre question concerne JavaScript). J'ai décidé d'écrire le mien.

Alors, voilà:

Réponse simple: i-- est généralement plus rapide, car il n'est pas nécessaire de faire une comparaison à 0 à chaque exécution, il teste les résultats les méthodes sont ci-dessous:

Résultats du test: Comme "prouvé" par this jsPerf, arr.pop() est en fait la boucle la plus rapide de loin. Mais, en vous concentrant sur --i, i--, i++ et ++i, comme vous l'avez demandé dans votre question, voici jsPerf (ils proviennent de plusieurs jsPerf, veuillez consulter les sources ci-dessous) résumé:

--i et i-- sont identiques dans Firefox, tandis que i-- est plus rapide dans Chrome.

Dans Chrome une boucle for de base (for (var i = 0; i < arr.length; i++)) est plus rapide que i-- et --i tandis que dans Firefox, elle est plus lente.

Dans Chrome et Firefox, un arr.length mis en cache est nettement plus rapide avec Chrome à l'avance d'environ 170 000 opérations par seconde.

Sans une différence significative, ++i est plus rapide que i++ dans la plupart des navigateurs, autant que je sache, n’est jamais le contraire dans aucun navigateur.

Résumé plus court: arr.pop() est de loin la boucle la plus rapide; pour les boucles spécifiquement mentionnées, i-- est la boucle la plus rapide.

Sources: http://jsperf.com/fastest-array-loops-in-javascript/15 , http://jsperf.com/ipp-vs-ppi-2

J'espère que cela répond à votre question.

13
A.M.K

Je ne pense pas qu'il soit logique de dire que i-- est plus rapide que i++ en JavaScript.

Tout d'abord, cela dépend totalement de la mise en oeuvre du moteur JavaScript.

Deuxièmement, à condition que les constructions les plus simples JIT 'et traduites en instructions natives, alors i++ vs i-- dépendra totalement de la CPU qui l'exécute. Autrement dit, sur les ARM (téléphones mobiles), il est plus rapide de descendre à 0 car la décrémentation et la comparaison à zéro sont exécutées en une seule instruction.

Probablement, vous pensiez que l’un était plus gaspillé que l’autre parce que la manière suggérée est:

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

mais le chemin suggéré n’est pas parce que l’un est plus rapide que l’autre, mais simplement parce que si vous écrivez

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

ensuite, à chaque itération, array.length devait être évalué (un moteur JavaScript plus intelligent pourrait peut-être comprendre que la boucle ne changera pas la longueur du tableau). Même si cela ressemble à une simple déclaration, il s’agit en fait d’une fonction appelée sous le capot par le moteur JavaScript.

L'autre raison pour laquelle i-- pourrait être considéré comme "plus rapide" est que le moteur JavaScript n'a besoin que d'une seule variable interne pour contrôler la boucle (variable associée à var i). Si vous compariez à array.length ou à une autre variable, il devait exister plus d'une variable interne pour contrôler la boucle, et le nombre de variables internes constituait l'actif limité d'un moteur JavaScript. Moins de variables sont utilisées dans une boucle, plus le JIT a de chances d'être optimisé. C'est pourquoi i-- pourrait être considéré plus rapidement ...

13
Pavel

Cela dépend de l'emplacement de votre tableau en mémoire et du taux de correspondance des pages de mémoire pendant que vous accédez à ce tableau.

Dans certains cas, l'accès aux membres du tableau dans l'ordre des colonnes est plus rapide que l'ordre des lignes en raison de l'augmentation du taux de réussite.

12
Akbar Bayati

La dernière fois que je m'en suis inquiété, c'était lors de l'écriture de 6502 Assembly (8 bits, ouais!). Le gros avantage est que la plupart des opérations arithmétiques (en particulier les décréments) ont mis à jour un ensemble d'indicateurs, l'un d'entre eux étant Z, l'indicateur "atteint le zéro".

Donc, à la fin de la boucle, vous venez de faire deux instructions: DEC (décrémentation) et JNZ (saut sinon zéro), aucune comparaison n'est nécessaire!

10
Javier

Pour faire court: Il n’ya absolument aucune différence à faire cela en JavaScript.

Tout d'abord, vous pouvez le tester vous-même:

Vous pouvez non seulement tester et exécuter n'importe quel script dans n'importe quelle bibliothèque JavaScript, mais vous avez également accès à l'ensemble des scripts déjà écrits, ainsi qu'à la possibilité de voir les différences entre les temps d'exécution dans différents navigateurs sur différentes plates-formes.

Donc, autant que vous puissiez voir, il n'y a pas de différence entre les performances dans n'importe quel environnement.

Si vous souhaitez améliorer les performances de votre script, vous pouvez essayer les choses suivantes:

  1. Avoir une instruction var a = array.length; afin que vous ne calculiez pas sa valeur à chaque fois dans la boucle
  2. Effectuez le déroulement de la boucle http://en.wikipedia.org/wiki/Loop_unwinding

Mais vous devez comprendre que les améliorations que vous pouvez obtenir seront tellement insignifiantes que vous ne devriez surtout pas vous en soucier.

Mon opinion personnelle pourquoi une telle idée fausse (Dec vs Inc) est apparue

Il y a très longtemps, il y avait une instruction machine commune, DSZ (Décrémenter et Sauter à zéro). Les personnes qui ont programmé en langage Assembly ont utilisé cette instruction pour mettre en place des boucles afin de sauvegarder un registre. Maintenant, ces faits antiques sont obsolètes et je suis presque certain que vous n'obtiendrez aucune amélioration des performances dans aucun langage utilisant cette pseudo amélioration.

Je pense que la seule façon dont une telle connaissance peut se propager à notre époque est lorsque vous lisez le code d'une autre personne. Voyez une telle construction et demandez pourquoi elle a été mise en œuvre et voici la réponse: "cela améliore les performances car il se compare à zéro". Vous êtes devenu désemparé d'une connaissance supérieure de votre collègue et pensez à l'utiliser pour être beaucoup plus intelligent :-)

7
Salvador Dali

Cela ne dépend pas du signe -- ou ++, mais cela dépend des conditions que vous appliquez dans la boucle.

Par exemple: Votre boucle est plus rapide si la variable a une valeur statique que si votre boucle vérifie à chaque fois les conditions, comme la longueur d'un tableau ou d'autres conditions.

Mais ne vous inquiétez pas de cette optimisation, car cette fois-ci, son effet est mesuré en nanosecondes.

6
Arvind Kanjariya

for(var i = array.length; i--; ) n'est pas beaucoup plus rapide. Mais lorsque vous remplacez array.length par super_puper_function(), cela peut être considérablement plus rapide (puisqu'il est appelé à chaque itération). C'est la différence.

Si vous voulez changer cela en 2014, vous n'avez pas besoin de penser à l'optimisation. Si vous voulez le changer avec "Search & Replace", vous n'avez pas besoin de penser à l'optimisation. Si vous n'avez pas le temps, vous n'avez pas besoin de penser à l'optimisation. Mais maintenant, vous avez le temps d'y réfléchir.

P.S .: i-- n'est pas plus rapide que i++.

6
Dmitry Isaev

Cela peut s'expliquer par le fait que JavaScript (et toutes les langues) soit éventuellement transformé en code d'opération à exécuter sur le processeur. Les processeurs ont toujours une seule instruction pour comparer à zéro, ce qui est sacrément rapide.

En passant, si vous pouvez garantir que count est toujours >= 0, vous pouvez simplifier pour:

for (var i = count; i--;)
{
  // whatever
}
6
searlea

J'ai fait un comparaison sur jsbench .

Comme Alestani l'a fait remarquer, une chose qui prend du temps dans les boucles ascendantes est d'évaluer, pour chaque itération, la taille de votre tableau. Dans cette boucle:

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

vous évaluez .length à chaque fois que vous incrémentez i. Dans celui-ci:

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

vous évaluez .length une seule fois, lorsque vous déclarez i. Dans celui-ci:

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

la comparaison est implicite, elle se produit juste avant la décrémentation de i, et le code est très lisible. Cependant, ce qui peut faire toute une différence, c'est ce que vous mettez dans la boucle.

Boucle avec appel à fonction (défini ailleurs):

for (i = values.length; i-- ;) {
  add( values[i] );
}

Boucle avec code en ligne:

var sum = 0;
for ( i = values.length; i-- ;) {
  sum += values[i];
}

Si vous pouvez intégrer votre code, au lieu d'appeler une fonction, sans sacrifier la lisibilité, vous pouvez obtenir une boucle d'un ordre de grandeur plus rapide!


Note : comme les navigateurs sont devient bon en ligne simples fonctions, cela dépend vraiment de la complexité de votre code. Alors, profil avant d’optimiser, car

  1. Le goulot d'étranglement peut être ailleurs (ajax, refusion, ...)
  2. Vous pouvez choisir un meilleur algorithme
  3. Vous pouvez choisir une meilleure structure de données

Mais rappelles-toi:

Le code est écrit pour que les gens lisent et, accessoirement, pour que les machines s’exécutent.

5
Spyryto

++ vs. -- n'a pas d'importance, car JavaScript est un langage interprété, pas un langage compilé. Chaque instruction est traduite dans plus d'une langue de la machine et vous ne devez pas vous soucier des détails sanglants.

Les personnes qui parlent d'utiliser -- (ou ++) pour une utilisation efficace des instructions d'assemblage sont fausses. Ces instructions s'appliquent à l'arithmétique des nombres entiers et il y a pas de nombres entiers en JavaScript, mais uniquement des nombres .

Vous devriez écrire du code lisible.

4
Salman A

Parfois, apporter des modifications très mineures à la manière dont nous écrivons notre code peut avoir un impact important sur la rapidité de notre code. Un domaine dans lequel une modification mineure du code peut avoir une grande incidence sur les temps d'exécution est celui dans lequel nous avons une boucle for qui traite un tableau. Lorsque le tableau contient des éléments de la page Web (tels que des boutons radio), le changement a le plus grand effet, mais il est toujours utile d'appliquer ce changement même lorsque le tableau est interne au code Javascript.

La manière conventionnelle de coder une boucle for pour traiter un tableau est la suivante:

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

Le problème avec ceci est que l'évaluation de la longueur du tableau en utilisant myArray.length prend du temps et la façon dont nous avons codé la boucle signifie que cette évaluation doit être effectuée à chaque fois autour de la boucle. Si le tableau contient 1000 éléments, sa longueur sera évaluée 1001 fois. Si nous examinions les boutons radio et que nous avions myForm.myButtons.length, l'évaluation prendrait encore plus de temps, car le groupe de boutons approprié dans le formulaire spécifié devait d'abord être localisé avant que la longueur puisse être évaluée à chaque fois autour de la boucle.

Évidemment, nous ne prévoyons pas que la longueur du tableau changera pendant le traitement, de sorte que tous ces recalculs de la longueur ne font qu’ajouter inutilement au temps de traitement. (Bien sûr, si vous avez du code dans la boucle qui ajoute ou supprime des entrées de tableau, la taille du tableau peut changer entre les itérations et nous ne pouvons donc pas changer le code qui le teste)

Ce que nous pouvons faire pour corriger cela pour une boucle où la taille est fixée consiste à évaluer la longueur une fois au début de la boucle et à la sauvegarder dans une variable. Nous pouvons ensuite tester la variable pour décider quand terminer la boucle. Cela est beaucoup plus rapide que d'évaluer la longueur du tableau à chaque fois, en particulier lorsque le tableau contient plus que quelques entrées ou fait partie de la page Web.

Le code pour faire ceci est:

for (var i = 0, var j = myArray.length; i < j; i++) {...

Alors maintenant, nous évaluons une seule fois la taille du tableau et testons notre compteur de boucle par rapport à la variable qui contient cette valeur à chaque fois autour de la boucle. L'accès à cette variable supplémentaire est beaucoup plus rapide que l'évaluation de la taille du tableau. Notre code s'exécutera donc beaucoup plus rapidement qu'auparavant. Nous avons juste une variable supplémentaire dans notre script.

Souvent, l'ordre de traitement du tableau n'a pas d'importance tant que toutes les entrées du tableau sont traitées. Dans ce cas, nous pouvons accélérer légèrement notre code en supprimant la variable supplémentaire que nous venons d'ajouter et en traitant le tableau dans l'ordre inverse.

Le code final qui traite notre tableau de la manière la plus efficace possible est:

for (var i = myArray.length-1; i > -1; i--) {...

Ce code n'évalue toujours la taille du tableau qu'une seule fois au début, mais au lieu de comparer le compteur de boucles avec une variable, nous le comparons avec une constante. Puisqu'une constante est encore plus efficace pour accéder qu'une variable et que nous avons une instruction d'affectation de moins qu'avant, notre troisième version du code est maintenant légèrement plus efficace que la deuxième version et beaucoup plus efficace que la première.

4
Sid

La façon dont vous le faites maintenant n’est pas plus rapide (à part le fait qu’il s’agisse d’une boucle indéfinie, je suppose que vous vouliez faire i--.

Si vous voulez faire plus vite, faites:

for (i = 10; i--;) {
    //super fast loop
}

bien sûr, vous ne le remarqueriez pas sur une si petite boucle. La raison est plus rapide parce que vous décrémentez i tout en vérifiant qu'il est "vrai" (la valeur est "faux" lorsqu'il atteint 0)

4
peirix

Il tilisé à dire que --i était plus rapide (en C++) car il n'y a qu'un seul résultat, la valeur décrémentée. i-- doit stocker la valeur décrémentée dans i et conserver la valeur d'origine comme résultat (j = i--;). Dans la plupart des compilateurs, cela utilisait deux registres plutôt qu'un seul, ce qui pourrait entraîner l'écriture d'une autre variable en mémoire plutôt que de le conserver en tant que variable de registre.

Je suis d'accord avec ceux qui ont dit que cela ne faisait aucune différence ces jours-ci.

3
Ant

I-- ou i ++ ne prend pas beaucoup de temps. Si vous allez profondément dans l'architecture de la CPU, le ++ est plus rapide que le --, car l'opération -- remplacera le complément à 2, mais elle se produira à l'intérieur du matériel. rapide et aucune différence majeure entre le ++ et le -- également ces opérations sont considérées comme du moins de temps consommé dans la CPU.

Le pour la boucle fonctionne comme ceci:

  • Initialisez la variable une fois au début.
  • Vérifiez la contrainte dans le deuxième opérande de la boucle, <, >, <=, etc.
  • Ensuite, appliquez la boucle.
  • Incrémentez la boucle et bouclez à nouveau ces processus.

Alors,

for (var i = Things.length - 1; i >= 0; i--) {
    Things[i]
}; 

calculera la longueur du tableau seulement une fois au début et ce n'est pas beaucoup de temps, mais

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

va calculer la longueur à chaque boucle, donc cela prendra beaucoup de temps.

3
Omar Freewan

En des mots très simples

"i-- et i ++. En fait, ils prennent tous les deux le même temps".

mais dans ce cas, lorsque vous avez une opération incrémentielle, le processeur évalue la longueur chaque fois que la variable est incrémentée de 1 et en cas de décrémentation, en particulier dans ce cas, elle évalue la longueur seulement une fois, jusqu'à obtenir 0.

3
Ravi_Parmar

Dans de nombreux cas, cela n’a essentiellement rien à voir avec le fait que les processeurs peuvent se comparer à zéro plus rapidement que d’autres comparaisons.

En effet, seulement quelques moteurs Javascript (ceux de la liste JIT) génèrent du code en langage machine.

La plupart des moteurs Javascript construisent une représentation interne du code source qu'ils interprètent ensuite (pour avoir une idée de ce à quoi cela ressemble, jetez un coup d'œil au bas de cette page sur SpiderMonkey de Firefox ). Généralement, si un morceau de code fait pratiquement la même chose mais conduit à une représentation interne plus simple, il sera exécuté plus rapidement.

Gardez à l'esprit qu'avec des tâches simples comme ajouter/soustraire une variable à une variable ou comparer une variable à quelque chose, le temps système de l'interpréteur se déplaçant d'une "instruction" interne à la suivante est assez élevé, de sorte que les "instructions" moins utilisé en interne par le moteur JS, mieux c'est.

3
Artelius

Tout d'abord, i++ et i-- prennent exactement le même temps sur tous les langages de programmation, y compris JavaScript.

Le code suivant prend beaucoup de temps.

Rapide:

for (var i = 0, len = Things.length - 1; i <= len; i++) { Things[i] };

Lent:

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

Par conséquent, le code suivant prend aussi un temps différent.

Rapide:

for (var i = Things.length - 1; i >= 0; i--) { Things[i] };

Lent:

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

P.S. Slow n'est lent que pour quelques langages (moteurs JavaScript) en raison de l'optimisation du compilateur. Le meilleur moyen est d'utiliser '<' à la place de '<=' (ou '=') et '- i' à la place de 'i -'.

3
Dmitry

Eh bien, je ne connais pas JavaScript, il devrait s'agir simplement d'une question de réévaluation de la longueur d'un tableau et peut-être d'un lien avec les tableaux associatifs (si vous ne faites que décrémenter, il est peu probable que de nouvelles entrées soient attribuées - si le tableau est dense, c’est-à-dire que l’on peut l’optimiser).

Dans l’assemblage de bas niveau, il existe une instruction de mise en boucle, appelée DJNZ (décrémentation et saut si différente de zéro). Ainsi, la décrémentation et le saut sont tous dans une instruction, ce qui le rend éventuellement un peu plus rapide que INC et JL/JB (incrément, saut si inférieur à/saut si inférieur à). De plus, comparer avec zéro est plus simple que de comparer avec un autre nombre. Mais tout cela est vraiment marginal et dépend également de l’architecture cible (pourrait faire la différence, par exemple sur Arm dans un smartphone).

Je ne m'attendrais pas à ce que ces différences de bas niveau aient un impact aussi important sur les langages interprétés. Je n'avais tout simplement pas vu DJNZ parmi les réponses. J'ai donc pensé partager une pensée intéressante.

3
the swine

C'est une supposition, mais c'est peut-être parce qu'il est plus facile pour le processeur de comparer quelque chose avec 0 (i> = 0) plutôt qu'avec une autre valeur (i <Things.length).

1
Rorchackh

Le compilateur ne cache-t-il pas .length, et par conséquent cela ne fait aucune différence si vous comparez 0 ou .length? Je suppose que cela est très spécifique au compilateur ou à l’interprète auquel vous faites face.

Je dirais que si vous utilisez un compilateur ou un interpréteur bien optimisé, ne vous inquiétez pas pour cela, mais pour les développeurs de langage.

1
Daniel

La meilleure façon de répondre à ce type de question est de l'essayer. Créez une boucle qui compte un million d’itérations ou autre, et faites-le dans les deux sens. Chronométrez les deux boucles et comparez les résultats.

La réponse dépendra probablement du navigateur que vous utilisez. Certains auront des résultats différents des autres.

1
Greg Hewgill

AIDEZ LES AUTRES A ÉVITER LES MAUVAIS HEADACHE --- VOTEZ-LE !!!

La réponse la plus populaire sur cette page ne fonctionne pas pour Firefox 14 et ne passe pas le jsLinter. Les boucles "While" nécessitent un opérateur de comparaison et non une affectation. Cela fonctionne sur le chrome, le safari, et même. Mais meurt dans Firefox.

CECI IS CASSÉ!

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

CELA FONCTIONNERA! (fonctionne sur firefox, passe le jsLinter)

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

J'adore, beaucoup de marques mais pas de réponse: D

Mettre simplement une comparaison à zéro est toujours la comparaison la plus rapide

Donc (a == 0) est en fait plus rapide à renvoyer True que (a == 5)

C'est petit et insignifiant et avec 100 millions de lignes dans une collection, il est mesurable.

c'est-à-dire sur une boucle, vous pourriez dire où i <= array.length et être incrémenté i

sur une boucle basse, vous pourriez dire où i> = 0 et décrémenter i à la place.

La comparaison est plus rapide. Pas la "direction" de la boucle.

1
Robert

L'utilisation de l'opérateur d'incrément de préfixe est un peu plus rapide. Avec le suffixe, le compilateur doit conserver la valeur précédente comme résultat de l'expression.

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

Un interprète ou un compilateur intelligent verra que le résultat de i ++ n'est pas utilisé et ne stockera pas le résultat de l'expression, mais tous les moteurs Js ne le feront pas.

0
Jeff Ober

Je veux contribuer à ce fil la boucle la plus rapide en JavaScript qui soit multi-navigateur! Cette boucle produit une amélioration de plus de 500% par rapport à la boucle while tout.

Mon blog: la boucle la plus rapide en JavaScript

0
Milan Adamovsky