Performance de Javascript: While for For Loops

L'autre jour lors d'une interview technique, l'une des questions posées était "comment pouvez-vous optimiser le code Javascript"?

À ma grande surprise, il m'a dit que si les boucles étaient généralement plus rapides que pour les boucles.

Est-ce même vrai? Et si oui, pourquoi est-ce?

Vous devriez avoir répondu que la boucle négative serait encore plus rapide! Voir: Performance de la boucle JavaScript – Pourquoi diminuer l'itérateur vers 0 plus rapidement que l'incrémentation .

En contrepartie, ces deux sources documentent bien le phénomène de la vitesse en exécutant diverses boucles dans différents navigateurs et en comparant les résultats en millisecondes: https://blogs.oracle.com/greimer/entry/best_way_to_code_a et: http: // www .stoimen.com / blog / 2012/01/24 / javascript-performance-for-vs-while / .

De manière conceptuelle, une boucle for est essentiellement une boucle emballée tout en fonction de l'incrémentation ou de la décrémentation (progressant sur la logique selon un ordre ou une certaine longueur). Par exemple,

 for(var k=0; ++k; k< 20){ ... } 

Peut être accéléré en devenant une boucle négative:

 var k = 20; 

while(--k){ ... };

Et comme vous pouvez le voir à partir des mesures dans les liens ci-dessus, le gain de temps s'accumule vraiment pour de très grands nombres.

Bien qu'il s'agisse d'une excellente réponse dans une détection minutieuse de la vitesse et de l'efficacité, je devrais faire une digression à l'énoncé original @Pointy.

La bonne réponse aurait été qu'il n'est généralement pas inutile de s'inquiéter de ces minuties, car tout effort que vous avez mis dans de telles optimisations pourrait être complètement perdu par le prochain check-in à V8 ou à SpiderMonkey

Étant donné que Javascript est déterminé par le client et que l'on devait initialement coder par navigateur pour une compatibilité complète avec les navigateurs multiples (avant même que ECMA ne soit impliqué, il était pire), la différence de vitesse peut ne pas être une réponse logique à ce stade en raison de l'optimisation significative Et l'adoption de Javascript sur les navigateurs et leurs moteurs de compilation.

Nous ne parlons même plus d'écriture non stricte que d'écriture, comme les applications dans GAS, alors que les réponses et les questions sont amusantes, elles seraient probablement plus banales que utiles dans l'application du monde réel.

Pour expliquer ce sujet, vous devez d'abord comprendre d'où vient le sujet et la compilation de ce sujet avant d'interpréter. Prenons une brève histoire de l'évolution des langues et revenons à la compilation vs l'interprétation. Bien que la lecture ne soit pas nécessaire, vous pouvez simplement lire Compiling vs Interpeting pour la réponse rapide, mais pour une compréhension approfondie, je recommanderais de lire à la fois la Compilation contre l'interprétation et l'évolution de la programmation (montrant comment elles sont appliquées aujourd'hui).

COMPILER PAR INTERPRETATION

Le codage de langage compilé est une méthode de programmation dans laquelle vous écrivez votre code d'une manière compilable qu'un compilateur comprend, certaines des langues les plus reconnues aujourd'hui sont Java, C ++ et C #. Ces langues sont écrites dans l'intention qu'un programme de compilation traduit ensuite le code dans le code machine ou le bytecode utilisé par votre machine cible.

Code interprété
Est un code qui est traité J ust I n T ime (JIT) au moment de l'exécution sans compiler en premier, il ignore cette étape et permet une écriture, un débogage, des ajouts / modifications plus rapides, etc. Il ne sera jamais stocké dans l'interprétation du script Pour une utilisation future, il réinterprète le script chaque fois qu'une méthode est appelée. Le code interprété est exécuté dans un environnement d'exécution de programme défini et prévu (car javascript est habituellement un navigateur) auquel une fois interprété par l'environnement est alors envoyé au résultat souhaité. Les scripts interprétés ne sont jamais censés être un logiciel autonome et cherchent toujours à se brancher dans un environnement d'exécution valide à interpréter. C'est pourquoi un script n'est pas exécutable. Ils ne communiqueront jamais directement à un système d'exploitation. Si vous regardez les processus du système qui se produisent, vous ne verrez jamais votre script en cours de traitement, mais vous voyez le programme en cours de traitement qui traite votre script dans son environnement d'exécution.

Donc, écrire un script hello dans Javascript signifie que le navigateur interprète le code, définit ce qu'est l'hélice et pendant que cela se produit, le navigateur transforme ce code en code de niveau de machine en disant que j'ai ce script et que mon environnement veut afficher le mot hello donc La machine traite ensuite cela dans une représentation visuelle de votre script. C'est un processus constant, qui explique pourquoi vous avez des processeurs dans les ordinateurs et une action constante de traitement sur le système. Rien n'est toujours statique, les processus sont constamment exécutés quelle que soit la situation.

Les compilateurs
Compilez généralement le code dans un système de bytecode défini, ou une langue de code de machine, qui est maintenant une version statique de votre code. Il ne sera pas réinterprété par la machine à moins que le code source soit recompilé. C'est pourquoi vous verrez une compilation de post d'erreur d'exécution qu'un programmeur doit alors déboguer dans la source et la recompiler. Les scripts conçus par l'interprète (comme Javascript ou PHP) sont simplement des instructions non compilées avant d'être exécutées, de sorte que le code source est facilement édité et corrigé sans avoir besoin d'étapes de compilation supplémentaires car la compilation est effectuée en temps réel.

Le code ne contient pas tous le code compilé.
Un moyen simple d'illustrer ceci est les systèmes de jeux vidéo. The Playstation vs Xbox. Le système Xbox est conçu pour supporter le framework .net pour optimiser le codage et le développement. C # utilise ce cadre en conjonction avec Common Language Runtime afin de compiler le code dans bytecode. Bytecode n'est pas une définition stricte du code compilé, c'est une étape intermédiaire placée dans le processus qui permet l'écriture de code plus rapidement et à plus grande échelle pour les programmes, qui est ensuite interprété lorsque le code est exécuté à l'exécution à l'aide, vous l'avez deviné, J ust I n T ime (JIT). La différence est que ce code n'est interprété qu'une seule fois, une fois qu'il a été compilé, le programme ne réinterprendra pas ce code à moins de redémarrer.

Les langages de script interprétés ne compileront jamais le code, de sorte qu'une fonction dans un script interprété est constamment traitée à nouveau alors qu'une fonction de bytecode compilée est interprétée une fois et les instructions sont stockées jusqu'à ce que l'exécution du programme soit arrêtée. Le bénéfice est que le bytecode peut être porté à l'architecture d'une autre machine, à condition que vous ayez les ressources nécessaires en place. C'est pourquoi vous devez installer .net et éventuellement des mises à jour et des frameworks dans votre système afin qu'un programme fonctionne correctement.

The Playstation n'utilise pas un framework .net pour sa machine. Vous devrez coder en C ++, C ++ doit être compilé et assemblé pour une architecture de système particulière. Le code ne sera jamais interprété et devra être exactement correct pour pouvoir fonctionner. Vous ne pouvez jamais déplacer facilement cette langue de type comme vous pourriez un langage intermédiaire . Il est spécifiquement conçu pour l'architecture de cette machine et ne sera jamais interprété autrement.

Donc, vous voyez que même les langages compilés ne sont pas des versions finis d'une langue compilée. Les langages compilés sont destinés, dans leur définition stricte, à être intégralement compilés. Les langues interprétées sont destinées à être interprétées par un programme mais sont aussi les langages les plus portables de la programmation en raison du besoin d'un programme installé qui comprend le script, mais ils utilisent le plus de ressources en raison de leur interprétation constante. Les langages intermédiaires (tels que Java et C #) sont des hybrides de ces 2, compilant en partie, mais nécessitant également des ressources externes afin d'être toujours fonctionnel. Une fois qu'ils ont fonctionné, ils compiler à nouveau, ce qui est une interprétation unique en cours d'exécution.

Evolution de la programmation

Langage machine
La forme la plus basse de codage, ce code est strictement binaire dans sa représentation (je n'entrerai pas dans le calcul ternaire puisqu'il est basé sur la théorie et l'application pratique pour cette discussion). Les ordinateurs comprennent les valeurs naturelles, on / off vrai / faux. Il s'agit du code numérique du niveau machine, qui est différent du niveau suivant, code d'assemblage.

Code d'assemblage
Le niveau de code suivant est le langage d'assemblage. C'est le premier point dans lequel une langue est interprétée comme utilisée par une machine. Ce code est destiné à interpréter les mnémoniques, les symboles et les opérandes qui sont ensuite envoyés à la machine dans le code de niveau machine. Ceci est important à comprendre, car lorsque vous commencez à programmer, la plupart des gens font l'hypothèse que ce soit cela ou ce que je veux dire, je compile ou interprète. Aucun langage de codage au-delà du code de la machine à bas niveau ne compile que des instructions ou interprète uniquement les instructions!

Nous sommes allés dans "Not All Compiled Code is Created Equal". Le langage d'assemblage est la première instance de ceci. Le code machine est ce que la machine lit, mais la langue d'assemblage est ce qu'un humain peut lire. Au fur et à mesure que les ordinateurs fonctionnent plus rapidement, grâce à de meilleurs progrès technologiques, nos langages de niveau inférieur commencent à être plus condensés dans la nature et ne doivent pas être mis en œuvre manuellement. Le langage d'assemblage était le langage de codage de haut niveau car c'était la méthode plus rapide pour coder une machine. C'était essentiellement un langage de syntaxe qui, une fois assemblé (la forme de compilation la plus basse) a été converti directement en langage machine. Un assembleur est un compilateur, mais tous les compilateurs ne sont pas des assembleurs.

Codage de haut niveau
Les langages de codage de haut niveau sont des langues qui sont une étape au-dessus de l'assemblage, mais peuvent même contenir un niveau encore plus élevé (ce serait des langages Bytecode / Intermediate). Ces langages sont compilés à partir de là structure de syntaxe définie dans le code machine nécessaire, le bytecode à interpréter ou un hybride de l'une des méthodes précédentes combiné à un compilateur spécial qui permet d'assembler l'assemblage en ligne. Le codage de haut niveau, tel qu'il est le prédécesseur, Assembly, vise à réduire la charge de travail du développeur et à supprimer toute possibilité d'erreurs critiques dans des tâches redondantes, comme la construction de programmes exécutables. Dans le monde d'aujourd'hui, vous verrez rarement un travail de développeur dans l'assemblage dans le but de croquer des données pour le bénéfice de la taille seule. Plus souvent qu'un développeur peut avoir une situation, comme dans le développement de la console de jeux vidéo, où ils ont besoin d'une augmentation de vitesse dans le processus. Étant donné que les compilateurs de codage de haut niveau sont des outils qui cherchent à faciliter le processus de développement, ils ne peuvent pas 100% de compiler le code de la manière la plus efficace pour cette architecture de système. Dans ce cas, le code de l'assemblage serait écrit pour maximiser les ressources du système. Mais vous ne verrez jamais une personne écrire en code machine, à moins que vous ne rencontriez un ennui.

LE RÉSUMÉ

Si vous l'avez fait jusqu'ici, félicitations! Vous avez juste écouté plus en une séance que ma femme peut, à propos de ces choses, pour toute une vie. La question de l'OP portait sur la performance de vs vs pour les boucles. La raison pour laquelle il s'agit d'un point détaillé dans les normes d'aujourd'hui est double.

Reason One
Les jours d'interprétation de Javascript sont partis. Tous les principaux navigateurs (Oui, même Opera et Netscape) utilisent un moteur Javascript qui est conçu pour compiler le script avant de le mettre en œuvre. Les réglages de performance discutés par les développeurs de JS en termes de méthodes non appelées sont des méthodes d'étude obsolètes lorsqu'on regarde les fonctions natives dans la langue. Le code est déjà compilé et optimisé pour cela avant de faire partie du DOM. Il n'est pas interprété de nouveau alors que cette page est en cours car cette page est l'environnement d'exécution. Javascript est vraiment devenu un langage intermédiaire plus que le script interprété. La raison pour laquelle il ne sera jamais appelé un langage de script intermédiaire est parce que Javascript n'est jamais compilé. C'est la seule raison. En plus, c'est une fonction dans un environnement de navigateur une version minifiée de ce qui se passe avec Bytecode.

Raison Deux Les chances que vous écriviez un script, ou une bibliothèque de scripts, qui exigeraient autant de puissance de traitement qu'une application de bureau sur un site Web est presque nulle. Pourquoi? Parce que Javascript n'a jamais été créé avec l'intention d'être un langage complet. La création était simplement de fournir une méthode de programmation de langage de niveau moyen qui permettrait de faire des processus qui n'étaient pas fournis par HTML et CSS, tout en allégeant les luttes de développement nécessitant des langages de codage de haut niveau spécialisés, en particulier Java.

CSS et JS n'a pas été pris en charge pour la plupart des premiers cycles de développement web. Jusqu'en 1997, CSS n'était pas une intégration sûre et JS se battait encore plus longtemps. Tout à part HTML est une langue supplémentaire dans le monde web.

Le HTML est spécifique pour être les éléments constitutifs d'un site. Vous n'enseignerez jamais javascript pour intégrer complètement un site Web. Au plus, vous feriez des manipulations DOM mais construisez un site.

Vous ne seriez jamais style de votre site dans JS car il n'est tout simplement pas pratique. CSS gère ce processus.

Vous ne conserverez jamais, en plus temporairement, l'utilisation de Javascript. Vous utiliserez une base de données.

Alors, qu'est-ce qu'on en reste? Des fonctions et des processus de plus en plus simples. CSS3 et ses itérations futures vont prendre toutes les méthodes de style à partir de Javascript. Vous le voyez déjà avec des animations et des états psuedo (survoler, actif, etc.).

Le seul argument valide d'optimisation du code en Javascript à ce stade concerne les fonctions, les méthodes et les opérations mal écrites qui pourraient être aidés par l'optimisation du modèle de formule / code de l'utilisateur. Tant que vous apprendrez des modèles de codage corrects et efficaces, Javascript, dans l'âge actuel, n'a aucune perte de performance de ses fonctions natives.

 for(var k=0; ++k; k< 20){ ... } 

Peut être accéléré en devenant une boucle négative:

 var k = 20; while(--k){ ... }; 

Un test plus précis serait d'utiliser pour la même mesure que pendant . La seule différence sera que l'utilisation pour les boucles offre plus de description. Si nous voulions être super fous, nous pourrions renoncer à tout le bloc;

 var k = 0; for(;;){doStuff till break} //or we could do everything for (var i=1, d=i*2, f=Math.pow(d, i); f < 1E9; i++, d=i*2, f=Math.pow(d,i)){console.log(f)} 

Quoi qu'il en soit … dans NodeJS v0.10.38 je gère une boucle de JavaScript de 10 9 dans un quart de seconde avec pour être en moyenne 13% plus rapide. Mais cela n'a vraiment aucun effet sur mes décisions futures avec lesquelles la boucle à utiliser ou le montant que je choisirai de décrire en boucle.

 > t=Date.now();i=1E9; > while(i){--i;b=i+1}console.log(Date.now()-t); 292 > t=Date.now();i=1E9; > while(--i){b=i+1}console.log(Date.now()-t); 285 > t=Date.now();i=1E9; > for(;i>0;--i){b=i+1}console.log(Date.now()-t); 265 > t=Date.now();i=1E9; > for(;i>0;){--i;b=i+1}console.log(Date.now()-t); 246 

Réponse 2016

En JavaScript, l' inverse de la boucle est le plus rapide. Pour les boucles sont trivialement plus rapides que dans les boucles. Soyez plus concentré sur la lisibilité .

Voici un marquage.

Les boucles suivantes sont testées:

 var i, len = 100000, lenRev = len - 1; i = 0; while (i < len) { 1 + 1; i += 1; } i = lenRev; while (-1 < i) { 1 + 1; i -= 1; } for (i = 0; i < len; i += 1) { 1 + 1; } for (i = lenRev; - 1 < i; i -= 1) { 1 + 1; } 

Réponse 2017

Jsperf pour vs foreach sur Chrome 59

Ici, vous pouvez voir Array.forEach est devenu le plus rapide sur la dernière version de Chrome (59) à la date écrite (31/07/17). Vous pouvez trouver des délais moyens pour les autres versions de navigateur ici: https://jsperf.com/for-vs-foreach/66 .

Cela montre que l'optimisation du moteur ES modifie ce qui est plus efficace à tout moment.

Ma recommandation est que vous utilisez le plus expressif possible pour votre cas d'utilisation.

Les différences de performance dans la même ampleur seront en grande partie non pertinentes à l'avenir, car les ordinateurs deviennent plus rapidement exponentiellement par la loi de Moore.