Les boucles sont-elles vraiment plus rapides à l'envers? [javascript]


Answers

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

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

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

J'ai entendu cela plusieurs fois. Les boucles JavaScript sont-elles vraiment plus rapides en comptant à rebours? 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 besoin d'évaluer une propriété chaque fois qu'elle vérifie si elle est terminée et qu'elle vérifie juste par rapport à 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.
}



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 de la matrice à chaque fois. Pour les grands tableaux, on peut voir la dégradation des performances.




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

a++ < length

est en fait compilé

a++
test (a-length)

Cela prend donc plus de temps sur le processeur lorsqu'il est compilé.




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

Comme il a déjà été dit, l'amélioration principale n'évalue pas la longueur du tableau à chaque itération dans la boucle for. C'est une technique d'optimisation bien connue et particulièrement efficace en JavaScript quand le tableau fait partie du document HTML (faire un for 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++)

D'où je suis, la principale amélioration de la forme dans votre question est le fait qu'elle ne déclare pas de variable supplémentaire ( len dans mon exemple)

Mais si vous me demandez, le point entier n'est pas au sujet de l'optimisation d' i++ vs i-- , mais de ne pas avoir à évaluer la longueur du tableau à chaque itération (vous pouvez voir un test d'évaluation sur jsperf ).




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 des langages de bas niveau, votre question est pour JavaScript) J'ai décidé d'écrire la mienne.

Alors, vous voilà:

Réponse simple: i-- est généralement plus rapide car il ne doit pas exécuter une comparaison à 0 à chaque fois qu'il s'exécute, les résultats des tests sur les différentes méthodes sont les suivants:

Résultats des tests: Comme "prouvé" par ce jsPerf, arr.pop() est en fait la boucle la plus rapide de loin. Mais, en mettant l'accent sur - --i , i-- , i++ et ++i comme vous l'avez demandé dans votre question, voici jsPerf (ils sont de plusieurs jsPerf, s'il vous plaît voir les sources ci-dessous) résultats résumés:

--i et i-- sont les mêmes dans Firefox alors que i-- est plus rapide dans Chrome.

Dans Chrome, une base pour loop ( for (var i = 0; i < arr.length; i++) ) est plus rapide que i-- et - --i alors que dans Firefox c'est plus lent.

Dans Chrome et Firefox, une arr.length cache est nettement plus rapide avec Chrome en avance d'environ 170 000 opérations / seconde.

Sans différence significative, ++i est plus rapide que i++ dans la plupart des navigateurs, AFAIK, ce n'est jamais l'inverse dans un navigateur.

Résumé 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.




La dernière fois que j'en ai eu la peine, c'était lors de l'écriture de l'assemblage 6502 (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 de drapeaux, l'un d'entre eux étant Z , l'indicateur «atteint zéro».

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




Cela peut s'expliquer par JavaScript (et toutes les langues) éventuellement transformé en opcodes pour fonctionner sur le CPU. Les CPU ont toujours une seule instruction pour comparer par rapport à zéro, ce qui est sacrément rapide.

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

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



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

Si vous allez le changer en 2014, vous n'avez pas besoin de penser à l'optimisation. Si vous voulez le modifier avec "Rechercher et remplacer", vous n'avez pas besoin de penser à l'optimisation. Si vous n'avez pas de temps, vous n'avez pas besoin de penser à l'optimisation. Mais maintenant, vous avez le temps d'y réfléchir.

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




La façon dont vous le faites est maintenant pas plus vite ( en dehors du fait qu'elle est une boucle indéfinie, je suppose que vous vouliez faire i--.

Si vous voulez le rendre plus rapide le faire:

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

Bien sûr, vous ne remarquerez pas sur une telle petite boucle. La raison pour laquelle il est plus rapide parce que vous êtes i décrémentation tout en vérifiant qu'il est « vrai » (il évalue à « false » lorsqu'il atteint 0)




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

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

La plupart des moteurs JavaScript construire une représentation interne du code source qu'ils interprètent ensuite (pour avoir une idée de ce que cela ressemble, jetez un oeil au bas de cette page sur SpiderMonkey de Firefox ). En général , si un morceau de code ne pratiquement la même chose , mais conduit à une représentation interne plus simple, il fonctionnera plus rapidement.

Gardez à l'esprit que des tâches simples comme l'ajout / soustraction d'une d'une variable, ou de comparer une variable à quelque chose, les frais généraux de l'interprète passer d'une « instruction » interne à l'autre est assez élevé, donc moins « instructions » qui sont utilisé en interne par le moteur JS, mieux.




Il sert à dire que -I était plus rapide (en C ++) parce qu'il n'y a qu'un seul résultat, la valeur décrémentée. i-- a besoin de stocker la valeur décrémentée retour à i et conserver aussi la valeur d' origine à la suite (j = i--;). Dans la plupart des compilateurs ce épuisé deux registres au lieu d'un qui pourrait provoquer une autre variable d'avoir à écrire à la mémoire au lieu de rester comme une variable de registre.

Je suis d'accord avec les autres qui ont dit qu'il ne fait aucune différence ces jours-ci.




++par rapport --n'a pas d' importance parce que JavaScript est un langage interprété, et non un langage compilé. Chaque instruction se traduit par plus d'une langue de la machine et vous ne devriez pas se soucier des détails sordides.

Les gens qui parlent de l' utilisation --(ou ++) d'utiliser efficacement des instructions de montage sont fausses. Ces instructions s'appliquent à arithmétique entière et il n'y a pas de nombres entiers en JavaScript, juste des chiffres .

Vous devez écrire un code lisible.




Pas beaucoup de temps est consommé par je-- ou i ++. Si vous allez au fond de l'architecture du processeur le ++est plus rapide que la --, puisque l' --opération fera le complément à 2, mais il happend à l' intérieur du matériel donc ce sera plus rapide et pas de différence majeure entre le ++et --ces opérations sont considérées comme des moins de temps consommé dans la CPU.

La boucle fonctionne comme ceci:

  • Initialiser une fois la variable au début.
  • Vérifiez la contrainte dans le second opérande de la boucle, <, >, <=, etc.
  • Appliquez ensuite la boucle.
  • Incrémenter la boucle et la boucle à nouveau lancer ces processus à nouveau.

Alors,

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

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

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

calcule la longueur à chaque boucle, il consommera beaucoup de temps.




Love it, beaucoup de marques vers le haut, mais pas de réponse: D

Tout simplement une comparaison contre zéro est toujours la comparaison la plus rapide

Donc (a == 0) est en fait plus rapide au retour est vrai que (a == 5)

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

à-dire sur une boucle jusqu'à vous pourrait dire où i <= array.length et être i incrémentant

sur une boucle vers le bas, vous pourriez dire où i> = 0 et être décrémentation i à la place.

La comparaison est plus rapide. Pas la « direction » de la boucle.




Ceci est juste une supposition, mais peut-être parce qu'il est plus facile pour le processeur de comparer quelque chose avec 0 (i> = 0) au lieu de avec une autre valeur (i <Things.length).