J'ai fait des recherches sur les interprètes/compilateurs, puis je suis tombé sur JIT-Compilation - en particulier le moteur Javascript V8 de Google Chrome.
Mes questions sont -
Ma compréhension actuelle
Chaque programme Javascript commence comme code source, puis, quelle que soit la méthode d'exécution, est finalement traduit en langage machine.
Tous les deux Compilation JIT et Interprétation doit suivre ce chemin, alors comment la compilation JIT peut-elle être plus rapide (également parce que JIT est limité dans le temps, contrairement à la compilation AOT)?
Il semble que JIT-Compilation soit une innovation relativement ancienne, basé sur Wikipédia JIT-Compilation Article .
"Le premier compilateur JIT publié est généralement attribué au travail sur LISP par McCarthy dans 1960. "
"Smalltalk (c. 1983) a ouvert la voie à de nouveaux aspects des compilations JIT. Par exemple, la traduction en code machine a été effectuée à la demande et le résultat a été mis en cache pour une utilisation ultérieure. Lorsque la mémoire devenait rare, le système supprimait une partie de ce code et le régénérait lorsqu'il était à nouveau nécessaire. "
Alors, pourquoi Javascript a-t-il été interprété pour commencer?
Je suis très confus et j'ai fait beaucoup de recherches à ce sujet, mais je n'ai pas trouvé de réponses satisfaisantes.
Des réponses si claires et concises seraient appréciées. Et si des explications supplémentaires sur les interprètes, les compilateurs JIT, etc. doivent être apportées, cela est également apprécié.
La réponse courte est que JIT a des temps d'initialisation plus longs, mais est beaucoup plus rapide à long terme, et JavaScript n'était pas initialement prévu pour le long terme.
Dans les années 90, JavaScript typique sur un site Web équivaudrait à une ou deux fonctions dans l'en-tête, et une poignée de code intégré directement dans les propriétés onclick
et autres. Il était généralement exécuté correctement lorsque l'utilisateur s'attendait à un énorme délai de chargement de la page de toute façon. Pensez à une validation de formulaire extrêmement basique ou à de minuscules utilitaires mathématiques comme les calculateurs d'intérêts hypothécaires.
L'interprétation au besoin était beaucoup plus simple et fournissait des performances parfaitement adéquates pour les cas d'utilisation de la journée. Si vous vouliez quelque chose avec des performances à long terme, vous avez utilisé flash ou une applet Java.
Google Maps en 2004 a été l'une des premières applications tueuses pour une utilisation intensive de JavaScript. Il a ouvert les yeux sur les possibilités de JavaScript, mais a également mis en évidence ses problèmes de performances. Google a passé un certain temps à encourager les navigateurs à améliorer leurs performances JavaScript, puis a finalement décidé que la concurrence serait le meilleur facteur de motivation et leur donnerait également la meilleure place à la table des normes de navigateur. Chrome et V8 ont donc été lancés en 2008. Désormais, 11 ans après l'arrivée de Google Maps, nous avons de nouveaux développeurs qui ne se souviennent pas que JavaScript ait été considéré comme inadéquat pour ce type de tâche.
Supposons que vous ayez une fonction animateDraggedMap
. L'interprétation peut prendre 500 ms et 700 ms pour le compiler. Cependant, après la compilation JIT, l'exécution de 100 ms peut ne prendre que 100 ms. Si c'est les années 90 et que vous n'appelez une fonction qu'une seule fois puis que vous rechargez la page, JIT n'en vaut pas la peine. Si c'est aujourd'hui et que vous appelez animateDraggedMap
des centaines ou des milliers de fois, ces 200 ms supplémentaires à l'initialisation ne sont rien, et cela peut être fait en coulisses avant que l'utilisateur n'essaie même de faire glisser la carte.
En comprenant ce qui se passe lors de l'exécution, il est possible d'apporter des modifications au code ou à l'interprétation du code qui permettent de l'exécuter plus rapidement ou de le compiler mieux que ce qui est connu à l'avance.
On peut en dire beaucoup à ce sujet - il fait l’objet de nombreuses recherches. Ma propre explication ici que j'ai commencé à écrire pâle en comparaison avec la réponse donnée dans Comprendre les différences: interprète traditionnel, compilateur JIT, interprète JIT et compilateur AOT
Tout simplement, JavaScript n'a pas été initialement compilé ou examiné pour JIT car il n'a jamais été conçu pour être quelque chose d'aussi complexe ou important.
L'intention initiale de JavaLe script devait créer un lien vers les applets Java sur une page Web. La possibilité de cliquer sur un bouton ou d'entrer une valeur dans un champ de formulaire puis de travailler dans une méthode d'applet Java peut être vue dans Appel des méthodes d'applet à partir du code JavaScript . Il était également possible, via JavaScript, d'aller dans l'autre sens invoquer du code JavaScript à partir d'une applet .
L'intention initiale de JavaScript était de lier les applets et les pages html qui les contenaient. Pour une tâche aussi petite, on n'a pas besoin de grandes performances (si vous vouliez des performances, appelez la méthode applet qui est JIT'ed).
Ce n'est qu'après que Netscape a commencé à faire un travail important avec JavaScript comme son propre langage et à le promouvoir pour le développement (y compris Server Side JavaScript dans Netscape Enterprise Server - qui, soit dit en passant, a fait une compilation à l'avance) que JavaScript est apparu comme une cible sérieuse. Il a fallu de nombreuses années par la suite pour que les outils nécessaires le rendent utile.
Les JIT sont rapides pour JavaScript, car il est impossible de générer du code machine rapide lorsque vous ne connaissez pas le type de vos variables.
Lorsque vous ne disposez pas d'informations de type, les calculs sont coûteux. Par exemple,
x + y
est assez compliqué si vous ne savez rien de x et y. Il peut s'agir d'entiers, de doubles, de chaînes ou même d'objets où ce calcul a des effets secondaires. Comme nous n'avons pas de typage statique, c'est un calcul coûteux.
Avec la compilation juste à temps, nous pouvons utiliser les informations d'exécution et les transformer en un calcul plus rapide. Au moment de l'exécution, V8 garde une trace du type de variables. Si le code ci-dessus est exécuté plusieurs fois avec, par exemple, des chaînes, le compilateur peut exécuter les instructions beaucoup plus simples pour la concaténation de chaînes. Ainsi, lorsque le compilateur atteint x + y
, au lieu d'exécuter beaucoup de code qui se ramifie pour de nombreux types différents de x et y, le compilateur vérifie rapidement si nous avons à nouveau des chaînes, puis exécute seulement quelques lignes de code machine qui concaténent spécifiquement les chaînes.
Par exemple, en C++, le compilateur connaît à l'avance les types de x et y, car nous avons dû déclarer les variables. Il peut donc générer un code machine optimisé pour la concaténation de chaînes avant d'exécuter le code.
1) Comment cela peut-il être plus rapide que l'interprétation standard? Eh bien, un exemple pensé serait le suivant; supposons que nous ayons 2 applications ApplicationCompiled et ApplicationInterpreted. Ces deux programmes font exactement la même chose et partagent le même code source. ApplicationCompiled prend 6 secondes à compiler.
Disons que le timing du scénario A est:
Ainsi, au total, ApplicationCompiled prend 10 secondes pour exécuter le scénario A (compilation de 6 secondes, 4 secondes de fonctionnement) et ApplicationInterpreted prend 12 secondes au total pour s'exécuter. Je n'ai pas d'exemple spécifique à vous montrer, et je ne sais pas dans quels cas ce qui précède serait vrai - cela dépend aussi fortement de l'intelligence de l'interprète et du compilateur.
Évidemment, cela est très simplifié, mais à mon humble avis, la même idée peut être appliquée à la compilation/interprétation JIT. La question suivante serait alors "comment déterminer - à faible coût - si cette branche doit être compilée ou interprétée en JIT"? Je suis hors de ma ligue ici :)
2) Pourquoi la compilation JIT n'a-t-elle pas été utilisée en premier lieu? Je ne sais pas, mais je reconnais que c'est simplement une question de ressources et de maturité des progrès disponibles pour créer une solution difficile à optimiser un langage comme JavaScript applique des techniques avancées telles que celles-ci. Il y avait probablement beaucoup de fruits suspendus plus bas à l'époque.