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



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.
}



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




Not a lot of time is consumed by i-- or i++. If you go deep inside the CPU architecture the ++ is more speedy than the -- , since the -- operation will do the 2's complement, but it happend inside the hardware so this will make it speedy and no major difference between the ++ and -- also these operations are considered of the least time consumed in the CPU.

The for loop runs like this:

  • Initialize the variable once at the start.
  • Check the constraint in the second operand of the loop, < , > , <= , etc.
  • Then apply the loop.
  • Increment the loop and loop again throw these processes again.

So,

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

will calculate the array length only once at the start and this is not a lot of time, but

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

will calculate the length at each loop, so it will consume a lot of time.




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++ .




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 ).




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!




Love it, lots of marks up but no answer :D

Simply put a comparison against zero is always the fastest comparison

So (a==0) is actually quicker at returning True than (a==5)

It's small and insignificant and with 100 million rows in a collection it's measurable.

ie on a loop up you might be saying where i <= array.length and be incrementing i

on a down loop you might be saying where i >= 0 and be decrementing i instead.

The comparison is quicker. Not the 'direction' of the loop.




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
}



The way you're doing it now isn't faster (apart from it being an indefinite loop, I guess you meant to do i-- .

If you want to make it faster do:

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

of course you wouldn't notice it on such a small loop. The reason it's faster is because you're decrementing i while checking that it's "true" (it evaluates to "false" when it reaches 0)




++ vs. -- does not matter because JavaScript is an interpreted language, not a compiled language. Each instruction translates to more than one machine language and you should not care about the gory details.

People who are talking about using -- (or ++ ) to make efficient use of assembly instructions are wrong. These instruction apply to integer arithmetic and there are no integers in JavaScript, just numbers .

You should write readable code.




It used to be said that --i was faster (in C++) because there is only one result, the decremented value. i-- needs to store the decremented value back to i and also retain the original value as the result (j = i--;). In most compilers this used up two registers rather than one which could cause another variable to have to be written to memory rather than retained as a register variable.

I agree with those others that have said it makes no difference these days.




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.




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 de 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 this 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 issus 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: this , http://jsperf.com/ipp-vs-ppi-2

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




In many cases, this has essentially nothing to do with the fact that processors can compare to zero faster than other comparisons.

This is because only a few Javascript engines (the ones in the JIT list) actually generate machine language code.

Most Javascript engines build an internal representation of the source code which they then interpret (to get an idea of what this is like, have a look near the bottom of this page on Firefox's SpiderMonkey ). Generally if a piece of code does practically the same thing but leads to a simpler internal representation, it will run faster.

Bear in mind that with simple tasks like adding/subtracting one from a variable, or comparing a variable to something, the overhead of the interpreter moving from one internal "instruction" to the next is quite high, so the less "instructions" that are used internally by the JS engine, the better.




This is just a guess, but maybe it's because it's easier for the processor to compare something with 0 ( i >= 0 ) instead of with another value ( i < Things.length).




Links