IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Le Blog d'un Ninja codeur

[Actualité] [JS] Ordre d'évaluation et Performance

Note : 3 votes pour une moyenne de 2,33.
par , 22/02/2015 à 11h48 (1675 Affichages)
JavaScript étant un langage interprété, l'ordre des opérations peut avoir un impact significatif sur les performances selon le navigateur.

Par exemple, les expressions a * b + c et c + a * b bien que mathématiquement équivalentes peuvent ne pas avoir le même coût computationnel. Et pour cause.

Selon que la multiplication soit en début d'expression ou bien en fin d'expression, cela peut influencer drastiquement les performances.

Nom : JS Multiplication - 1.png
Affichages : 1230
Taille : 23,0 Ko
Sur Chrome nous n'observons pas de différences réellement significatives selon l'ordre de la multiplication par rapport à l'addition.

Nom : JS Multiplication - 2.png
Affichages : 698
Taille : 12,3 Ko
Cependant, sur Firefox, en plus de performances globales meilleures par rapport à Chrome, on peut constater que l'ordre d'évaluation des opérandes est très important. Du simple au double !

Dans le doute sur le navigateur qui exécutera le code, il vaut mieux donc privilégier la multiplication en début d'expression plutôt qu'en fin.

Ceci peut sans doute s'expliquer par l'ordre d'évaluation d'une expression qui doit probablement se faire de gauche à droite. Si la multiplication qui a une plus grande priorité sur l'addition se trouve en fin d'expression et donc à droite, cela doit probablement obliger l'interpréteur JavaScript de Firefox à empiler l'opérande gauche de l'addition avant de réaliser la multiplication puis de faire la somme proprement dite. Lorsque la multiplication se trouve en début d'expression et donc à gauche, cela permet à l'interpréteur de calculer la multiplication puis l'addition, sans passer par la pile. En tout cas sur Firefox.

Plus généralement, il est sans doute préférable d'écrire les expressions JavaScript dans l'ordre des priorités décroissantes. L'opérateur de plus forte priorité avant celui de plus faible priorité afin de limiter l'usage de la pile.

Chrome par contre normalise probablement l'ordre des opérations au préalable de l'évaluation arithmétique.

Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog Viadeo Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog Twitter Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog Google Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog Facebook Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog Digg Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog Delicious Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog MySpace Envoyer le billet « [JS] Ordre d'évaluation et Performance » dans le blog Yahoo

Mis à jour 22/02/2015 à 12h39 par yahiko

Catégories
Développement , Javascript , Développement Web

Commentaires

  1. Avatar de kolodz
    • |
    • permalink
    Je ne vais pas contesté le résultat, mais la conclusion.
    En effet, JavaScript passe par un interpréteur, mais aussi par un (Just-In-Time) compilateur lorsque celui-ci est exécuté plusieurs fois. Or le compilateur va réaliser un certain nombre d'optimisation lors de la compilation. L'une des personnes de Mozilla qui se trouve justement dans l'équipe du compilateur JavaScript pour FireFox a fait une présentation sur ce genre de test au HumanTalks de Lyon de ce mois (Février):
    Présentation du talk : http://humantalks.com/talks/567-les-...ne-rien-tester
    Slide : http://bnjbvr.github.io/humantalks-2015-feb/

    Celui-ci nous a expliqué que ce genre de "simplification"/"Optimisation" est souvent réalisé à la compilation. Et qu'il faut prendre avec précaution les résultats obtenu.
    D'ailleurs, il est totalement possible de les valeurs obtenu à partir du random, influent plus la durée du calcul que l'ordre des paramètres. Ou logiquement le résultat devrait varié à chaque test. (Je viens de faire le test sur http://jsperf.com/ avec IE 9) Le résultat change bien à chaque fois. J'ai eu 3 cas différents :
    A est plus rapide.
    B est plus rapide.
    A et B sont rapides !

    Pour moi, l'exemple n'est pas parlant. D'ailleurs, il est probable que l'interpréteur réalise de lui-même le swap de variable à l'interprétation (Une et une seule fois.)

    Cordialement,
    Patrick Kolodziejczyk.
  2. Avatar de yahiko
    • |
    • permalink
    Les résultats sont évidemment dépendant du navigateur comme je l'ai indiqué pour Chrome et Firefox.

    Cependant, la fonction Math.random() telle qu'implémentée dans Chrome et Firefox a des performances stables, ce qui ne semble donc pas être le cas pour IE9 (9 ?!!).

    Les tests répétés un grand nombre de fois sur ces navigateurs ne changent pas les tendances. Comme seul l'ordre des opérations changent dans mon test, je déduis logiquement que ce sont elles qui sont la cause de ces variations pour Firefox.

    Pour Chrome, j'évoque implicitement de la phase de compilation lorsque je parle de la normalisation probable qu'effectue le navigateur.

    Pour ce qui est du speech du gars de Firefox, à l'occasion, je jetterai un œil. Cela doit sans doute être intéressant. Merci.
  3. Avatar de kolodz
    • |
    • permalink
    Du coup, j'ai demandé à l'expert :
    Citation Envoyé par Benjamin Bouvier ‏@njbenji
    @PKolodz Résultat pas utilisé = boucle vide chronométrée ! (j'ai vérifié) Les variations sont sûrement dûes aux caches CPU, alignement pile
    Cordialement,
    Patrick Kolodziejczyk.

    Source :
    https://twitter.com/njbenji/status/569946390153056256
  4. Avatar de yahiko
    • |
    • permalink
    Tout d'abord, je remercie que l'expert ait bien voulu se pencher sur le cas. C'est un honneur

    Cependant, je ne pense pas que cela s'exécute comme une boucle vide dans jsperf.com.
    En ajoutant un test témoin, instruction null, on peut constater que le nombre d'instructions exécutées par seconde est d'un tout autre ordre de grandeur.


    Je ne suis donc pas convaincu par son explication, pour le moment.
  5. Avatar de kolodz
    • |
    • permalink
    Je ne suis absolument pas dans l’optimisation JavaScript,
    Mais, un test un peu plus correcte me semble être :
    http://jsperf.com/order-of-multiplication/3
    Où on tombe dans des cas où le Javascript retour effectivement une valeur prise en considération, qui ne peux pas feindre d'avoir calculer.
    D'ailleurs, on constate la différence net entre le random, la valeur en dur en cumul et la valeur en dur direct.
  6. Avatar de SylvainPV
    • |
    • permalink
    Je me méfie aussi du Math.random(). J'ai modifié le test pour partir de chiffres fixes, et là les résultats sont identiques sur Firefox :
    http://jsperf.com/order-of-multiplication/6

  7. Avatar de kolodz
    • |
    • permalink
    Oui, mais comme l'explique Benjamin Bouvier dans sa présentation. Le compilateur fait une analyse du code statique :
    Ce qui fait que ton exemple se transforme en :
    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    test =7* 223 + 79;
    test =79+7* 223 ;
    Qui encore simplifié en
    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    test =1640;
    test =1640 ;
    Ce qui en vitesse d'exécution (623,886,221) cela est très proche de mon test=3; (653,346,832) ...
    http://jsperf.com/order-of-multiplication/3
    Note : Il est possible que ton test ai plus de valeur si a/b/c n'était pas remis à une constante dans le setup de manière systématique !

    Il est très difficile de vérifier quelque chose d'aussi théorique comme cela. Car, le compilateur JavaScript utilisé n'est pas si idiot qu'on le pense. Il faut donc le forcer à refaire le même calcul encore et encore. Ce qui n'est pas possible en temps normal !
  8. Avatar de yahiko
    • |
    • permalink
    L'utilisation de la fonction Math.random() a pour but justement d'éviter les optimisations basiques que pourrait faire l'interpréteur JS au préalable. Donc sur ce point je rejoins kolodz.

    J'ai un peu remanié mes tests en affectant simplement le résultat à une variable puis en utilisant celle-ci juste après.
    http://jsperf.com/order-of-multiplication/7

    1. Dans ces conditions il n'y a pas de différences significatives entre la multiplication à gauche et celle à droite.
    2. Par contre on observe une différence significative entre la multiplication à gauche et celle à droite lors de ce qui est censé être une boucle vide chronométré selon Benjamin Bouvier. Je ne suis pas certain que ce rapport de 1 à 2 puisse s'expliquer par les caches CPU ou un alignement de la pile. Chrome n'affiche pas de tels écarts dans ces conditions.
  9. Avatar de SylvainPV
    • |
    • permalink
    Citation Envoyé par kolodz
    Note : Il est possible que ton test ai plus de valeur si a/b/c n'était pas remis à une constante dans le setup de manière systématique !
    j'ai pas compris le sens du mot "setup", pour moi il s'exécutait une seule fois avant la série de test, pas avant chaque test.

    Mais j'ai modifié le test et j'ai quand même un résultat similaire, à moins que je ne me sois encore gouré :
    http://jsperf.com/order-of-multiplication/8

    Le fait que j'incrémente les variables, ça oblige à refaire le calcul non ? Je ne vois pas la différence entre une référence de variable et un appel à une fonction comme Math.random()
  10. Avatar de yahiko
    • |
    • permalink
    Tel que tu l'as écrit Sylvain, a * b + c et c + a * b sont bien recalculées à chaque itération, en principe.