J'y ai pensé récemment, et il me semble que la plupart des avantages accordés à la compilation JIT devraient plutôt être attribués au format intermédiaire, et que jit n'est en soi pas très utile moyen de générer du code.
Voici donc les principaux arguments de compilation pro-JIT que j'entends habituellement:
Ce n'est pas comme si ahead-of-time la compilation n'avait aucun avantage non plus. La compilation Just-in-time est soumise à des contraintes de temps: vous ne pouvez pas laisser l'utilisateur final attendre indéfiniment pendant le lancement de votre programme, il a donc un compromis à faire quelque part. La plupart du temps, ils optimisent simplement moins. Un de mes amis avait une preuve de profilage que le fait d’inclure des fonctions et de dérouler des boucles "manuellement" (obscurcir le code source dans le processus) avait un impact positif sur les performances de son programme de traitement de chiffres C #; faire la même chose de mon côté, avec mon programme C remplissant la même tâche, n'a donné aucun résultat positif, et je pense que cela est dû aux nombreuses transformations que mon compilateur a été autorisé à effectuer.
Et pourtant, nous sommes entourés de programmes jittés. C # et Java sont omniprésents, les scripts Python peuvent compiler en un certain bytecode, et je suis sûr que de nombreux autres langages de programmation font de même. Il doit y avoir une bonne raison pour que je manque. Qu'est-ce qui rend la compilation just-in-time aussi supérieure à la compilation ahead-of-time?
EDIT Pour dissiper un peu la confusion, il serait peut-être important de préciser que je suis tout pour une représentation intermédiaire des exécutables. Cela présente de nombreux avantages (et en réalité, la plupart des arguments pour la compilation just-in-time sont en réalité des arguments pour une représentation intermédiaire). Ma question concerne la manière dont ils devraient être compilés en code natif.
La plupart des runtimes (ou des compilateurs d'ailleurs) préféreront les compiler juste à temps ou en avance. Comme la compilation ahead-of-time me semble une meilleure alternative, car le compilateur a plus de temps pour effectuer des optimisations, je me demande pourquoi Microsoft, Sun et tous les autres agissent dans le sens inverse. Je ne suis pas très sûr des optimisations liées au profilage, mon expérience avec les programmes compilés juste-à-temps n'affichant que de faibles optimisations.
J'ai utilisé un exemple avec du code C uniquement parce que j'avais besoin d'un exemple de compilation ahead-of-time ou de compilation just-in-time. Le fait que le code C n'ait pas été émis dans une représentation intermédiaire n'a aucune incidence sur la situation; il me suffisait de montrer que la compilation ahead-time pouvait produire de meilleurs résultats immédiats.
La page ngen tool a fait déborder le vase (ou a au moins fourni une bonne comparaison des images natives par rapport aux images compilées par JIT). Les exécutables compilés à l'avance présentent généralement les avantages suivants:
Les exécutables compilés juste à temps ont généralement l'avantage sur:
La nécessité de régénérer une image compilée à l'avance chaque fois que l'un de ses composants constitue un inconvénient énorme pour les images natives. D'un autre côté, le fait que les images compilées par JIT ne puissent pas partager le code de la bibliothèque peut causer de graves problèmes de mémoire. Le système d'exploitation peut charger n'importe quelle bibliothèque native sur un emplacement physique et en partager les parties immuables avec tous les processus qui le souhaitent, ce qui permet de réaliser d'importantes économies de mémoire, notamment avec les frameworks système utilisés par pratiquement tous les programmes. (J'imagine que ceci est quelque peu compensé par le fait que les programmes compilés par JIT ne compilent que ce qu'ils utilisent réellement.)
La considération générale de Microsoft à ce sujet est que les grandes applications bénéficient généralement d’une compilation à l’avance, alors que les petites n'en bénéficient généralement pas.
Plus grande portabilité: le Livrable (code octet) reste Portable
Dans le même temps, plus spécifique à la plate-forme: Parce que la compilation JIT a lieu sur le même système que le code, il peut être très, très bien réglé pour système. Si vous faites une compilation à l'avance (et que vous voulez toujours envoyer le même paquet à À tout le monde), vous devez faire des compromis.
Les améliorations apportées à la technologie du compilateur peuvent avoir un impact sur les programmes existants. Un meilleur compilateur C Ne vous aide pas du tout Avec des programmes déjà déployés. Un meilleur compilateur JIT améliorera les performances des programmes existants Le code Java que vous avez écrit il y a dix ans sera plus rapide aujourd'hui.
S'adapter aux métriques d'exécution. Un compilateur JIT peut non seulement regarder Le code et le système cible, mais aussi Comment le code est utilisé. Il peut Instrumenter le code en cours, et Prendre des décisions sur la manière d'optimiser En fonction, par exemple, de ce que Valorise généralement les paramètres de la méthode
Vous avez raison de dire que JIT ajoute aux coûts de démarrage, il existe donc une contrainte de temps, alors que la compilation en avance peut prendre tout le temps voulue. Cela rend Plus approprié pour les applications de type serveur, où le temps de démarrage n’est pas si important Et une "phase de préchauffage" avant que le code ne devienne vraiment rapide est acceptable.
Je suppose qu'il serait possible de stocker le résultat d'une compilation JIT quelque part, afin qu'il puisse être réutilisé la prochaine fois. Cela vous donnerait une compilation "en avance sur le temps" pour la deuxième exécution du programme. Peut-être que les gens intelligents de Sun et de Microsoft pensent qu'un nouveau JIT est déjà assez bon et que la complexité supplémentaire ne vaut pas la peine.
Une logique simple nous dit que la compilation d'un programme d'une taille énorme, même à partir de codes d'octets, prend tout simplement trop de temps. Vous allez vous retrouver avec un temps de démarrage énorme et cela effraiera tout votre produit. Bien sûr, vous pouvez précompiler pendant l’installation, mais cela a également des conséquences.
Une autre raison est que toutes les parties de l'application ne seront pas utilisées. JIT ne compilera que les parties qui intéressent l'utilisateur, laissant ainsi 80% du code intact, économisant du temps et de la mémoire.
Enfin, la compilation JIT peut appliquer des optimisations que les compilateurs normaux ne peuvent pas. Comme insérer des méthodes virtuelles ou des parties de méthodes avec arbres de trace . Ce qui, en théorie, peut les rendre plus rapides.
Meilleur support de réflexion. Cela pourrait être fait en principe dans un programme élaboré en avance sur le temps, mais cela ne semble presque jamais se produire dans la pratique.
Des optimisations qui ne peuvent souvent être calculées qu'en observant le programme de manière dynamique. Par exemple, l'inclusion de fonctions virtuelles, l'analyse d'échappement pour transformer les allocations de pile en allocations de tas et le verrouillage du grossissement.
Il semble que cette idée ait été implémentée en langage Dart:
https://hackernoon.com/why-flutter-uses-Dart-dd635a054ebf
La compilation JIT est utilisée pendant le développement, en utilisant un compilateur particulièrement rapide. Ensuite, lorsqu'une application est prête à être publiée, elle est compilée AOT. Par conséquent, avec l'aide d'outils et de compilateurs avancés, Dart peut offrir le meilleur des deux mondes: cycles de développement extrêmement rapides, temps d'exécution et de démarrage rapides.
J'essayais aussi de comprendre cela car j'avais constaté que Google allait remplacer leur machine virtuelle Dalvik (essentiellement une autre machine virtuelle Java telle que HotSpot) par Android Run Time (ART), un compilateur AOT, mais Java utilise généralement HotSpot. , qui est un compilateur JIT. Apparemment, ARM est environ 2 fois plus rapide que Dalvik ... alors je me suis dit: "Pourquoi Java n'utilise-t-il pas aussi AOT?" . Quoi qu'il en soit, d'après ce que je peux comprendre, la principale différence est que JIT utilise l'optimisation adaptative pendant l'exécution, ce qui (par exemple) permet de compiler SEULEMENT les parties du bytecode fréquemment exécutées en code natif; Tandis que AOT compile l'intégralité du code source en code natif, et qu'un code d'une quantité moindre s'exécute plus rapidement qu'un code d'une quantité supérieure.
Je dois imaginer que la plupart des applications Android sont composées d'une petite quantité de code, il est donc plus logique en moyenne de compiler l'intégralité du code source en code AOT natif et d'éviter les frais généraux liés à l'interprétation/optimisation.
Un des avantages de JIT, que je ne vois pas énumérés ici, est la possibilité d’aligner/d’optimiser des ensembles/dlls/binaires distincts (pour simplifier, je vais simplement utiliser des "assemblages" à partir de maintenant).
Si votre application fait référence à des assemblages susceptibles de changer après l'installation (par exemple, bibliothèques préinstallées, bibliothèques d'infrastructure, plugins), un modèle "compiler sur l'installation" doit s'abstenir d'inliner les méthodes au-delà des limites de Assembly. Sinon, lorsque l'assembly référencé est mis à jour, nous devons rechercher tous ces bits de code en ligne dans les assemblys référencés sur le système et les remplacer par le code mis à jour.
Dans un modèle JIT, nous pouvons librement nous aligner dans les assemblys, car nous ne nous préoccupons que de générer un code machine valide pour une seule exécution au cours de laquelle le code sous-jacent ne change pas.
Peut-être que cela a à voir avec l'approche moderne de la programmation. Vous savez, il y a de nombreuses années, vous écriviez votre programme sur une feuille de papier, certaines personnes le transformaient en une pile de cartes perforées et l'introduisaient dans l'ordinateur, et demain matin, vous obteniez un vidage sur un rouleau de papier pesant. une demi-livre. Tout cela vous a obligé à réfléchir longuement avant d'écrire la première ligne de code.
Ces jours sont révolus depuis longtemps. Lorsque vous utilisez un langage de script tel que PHP ou JavaScript, vous pouvez tester immédiatement toute modification. Ce n'est pas le cas avec Java, bien que les serveurs d'applications vous permettent un déploiement à chaud. Il est donc très pratique que les programmes Java puissent être compilés rapidement} _, car les compilateurs de code intermédiaire sont assez simples.
Mais, il n’existe pas de langues exclusivement JIT. Des compilateurs rapides sont disponibles en Java depuis un certain temps et, plus récemment, Mono l'a introduit dans CLR. En fait, MonoTouch est possible du fait de la compilation AOT, car les applications non natives sont interdites dans l'App Store d'Apple.
La différence entre plate-forme-navigateur-dynamique et plate-forme-navigateur réside dans la manière dont votre application angular sera compilée . L'utilisation de la plate-forme dynamique rend angulaire l'envoi du compilateur Just-in-Time au serveur frontal ainsi qu'à votre application. Ce qui signifie que votre application est en cours de compilation côté client… .. D'autre part, l'utilisation du navigateur de plate-forme conduit à l'envoi d'une version précompilée Ahead-of-Time de votre application au navigateur. Ce qui signifie généralement qu'un paquet considérablement plus petit est envoyé au navigateur . La documentation angular2 pour l'amorçage à https://angular.io/docs/ts/latest/guide/ngmodule.html#!#bootstrap l'explique plus en détail.