web-dev-qa-db-fra.com

Pourquoi la JVM nécessite-t-elle un préchauffage?

Je comprends que dans la Java machine virtuelle (JVM), le préchauffage est potentiellement requis car Java charge les classes en utilisant un processus de chargement paresseux et en tant que tel, vous voulez vous assurer que les objets sont initialisés avant de commencer les transactions principales. Je suis développeur C++ et je n'ai pas eu à faire face à des exigences similaires.

Cependant, les parties que je ne peux pas comprendre sont les suivantes:

  1. Quelles parties du code devez-vous réchauffer?
  2. Même si je réchauffe certaines parties du code, combien de temps reste-t-il chaud (en supposant que ce terme signifie seulement combien de temps vos objets de classe restent en mémoire)?
  3. En quoi cela peut-il être utile d'avoir des objets à créer à chaque fois que je reçois un événement?

Prenons l'exemple d'une application qui devrait recevoir des messages sur un socket et les transactions pourraient être Nouvelle commande, Modifier la commande et Annuler la commande ou transaction confirmée.

Notez que l'application concerne le trading haute fréquence (HFT), les performances sont donc extrêmement importantes.

38
Suparna

Quelles parties du code devez-vous réchauffer?

Habituellement, vous n'avez rien à faire. Cependant, pour une application à faible latence, vous devez préchauffer le chemin critique de votre système. Vous devriez avoir des tests unitaires, donc je vous suggère de les exécuter au démarrage pour réchauffer le code.

Même une fois que votre code est réchauffé, vous devez également vous assurer que vos caches CPU restent au chaud. Vous pouvez constater un ralentissement significatif des performances après une opération de blocage, par exemple réseau IO, jusqu'à 50 micro-secondes. Habituellement, ce n'est pas un problème, mais si vous essayez de rester sous 50 micro-secondes la plupart du temps, ce sera un problème la plupart du temps.

Remarque: Warmup peut permettre à Escape Analysis de démarrer et de placer des objets sur la pile. Cela signifie que ces objets n'ont pas besoin d'être optimisés. Il est préférable de profiler la mémoire de votre application avant d'optimiser votre code.

Même si je réchauffe certaines parties du code, combien de temps reste-t-il chaud (en supposant que ce terme signifie seulement combien de temps vos objets de classe restent en mémoire)?

Il n'y a pas de limite de temps. Cela dépend si le JIt détecte si l'hypothèse qu'il a faite lors de l'optimisation du code s'est révélée incorrecte.

En quoi cela aide-t-il si j'ai des objets qui doivent être créés à chaque fois que je reçois un événement?

Si vous souhaitez une faible latence ou des performances élevées, vous devez créer le moins d'objets possible. Je vise à produire moins de 300 Ko/sec. Avec ce taux d'allocation, vous pouvez disposer d'un espace Eden suffisamment grand pour être collecté une fois par jour.

Prenons l'exemple d'une application qui devrait recevoir des messages sur un socket et les transactions pourraient être Nouvelle commande, Modifier la commande et Annuler la commande ou transaction confirmée.

Je vous suggère de réutiliser les objets autant que possible, bien que si cela est en dessous de votre budget d'allocation, cela ne vaut peut-être pas la peine de vous inquiéter.

Notez que l'application concerne le trading haute fréquence (HFT), les performances sont donc extrêmement importantes.

Vous pourriez être intéressé par notre logiciel open source qui est utilisé pour les systèmes HFT dans différentes banques d'investissement et fonds spéculatifs.

http://chronicle.software/

Mon application de production est utilisée pour le trading haute fréquence et chaque bit de latence peut être un problème. Il est clair qu'au démarrage, si vous ne préchauffez pas votre application, cela entraînera une latence élevée de quelques millis.

En particulier, vous pourriez être intéressé par https://github.com/OpenHFT/Java-Thread-Affinity car cette bibliothèque peut aider à réduire la gigue de planification dans vos threads critiques.

Et il est également dit que les sections critiques du code qui nécessitent un échauffement doivent être exécutées (avec de faux messages) au moins 12K fois pour que cela fonctionne de manière optimisée. Pourquoi et comment ça marche?

Le code est compilé à l'aide de threads d'arrière-plan. Cela signifie que même si une méthode peut être éligible pour la compilation en code natif, cela ne signifie pas qu'elle l'a fait en particulier au démarrage lorsque le compilateur est déjà assez occupé. 12K n'est pas déraisonnable, mais il pourrait être plus élevé.

25
Peter Lawrey

Le réchauffement fait référence à l'exécution d'un morceau de code suffisamment de fois pour que la machine virtuelle Java cesse d'interpréter et se compile en natif (au moins pour la première fois). En général, c'est quelque chose que vous ne voulez pas faire. La raison en est que la JVM recueille des statistiques sur le code en question qu'elle utilise lors de la génération du code (semblable aux optimisations guidées par profil). Donc, si un morceau de code en question est "réchauffé" avec de fausses données qui ont des propriétés différentes des vraies données, vous risquez de nuire aux performances.

EDIT: Étant donné que la JVM ne peut pas effectuer une analyse statique de l'ensemble du programme (elle ne peut pas savoir quel code va être chargé par l'application), elle peut à la place faire des suppositions sur les types à partir des statistiques qu'elle a recueillies. À titre d'exemple lors de l'appel d'une fonction virtuelle (en langage C++) à un emplacement d'appel exact et il détermine que tous les types ont la même implémentation, puis l'appel est promu en appel direct (ou même en ligne). Si plus tard cette hypothèse s'avère fausse, alors l'ancien code doit être "non compilé" pour se comporter correctement. AFAIK HotSpot classe les sites d'appel comme monomorphes (mise en œuvre unique), bi-morphiques (exactement deux..transformés en if (type imp1) {imp1} else {imp2}) et polymorphes..envoi complet polymorphe.

Et il y a un autre cas où la recompilation se produit ... lorsque vous avez une compilation à plusieurs niveaux. Le premier niveau passera moins de temps à essayer de produire un bon code et si la méthode est "assez chaude", alors le générateur de code au moment de la compilation le plus cher entre en jeu.

18
MB Reynolds

L'échauffement est rarement obligatoire. C'est pertinent lorsque vous effectuez par exemple des tests de performances, pour vous assurer que le temps de préchauffage JIT ne biaise pas les résultats.

Dans le code de production normal, vous voyez rarement du code destiné à l'échauffement. Le JIT se réchauffe pendant le traitement normal, il n'y a donc que très peu d'avantages à introduire du code supplémentaire juste pour cela. Dans le pire des cas, vous pourriez introduire des bogues, passer du temps de développement supplémentaire et même nuire aux performances.

Sauf si vous savez avec certitude que vous avez besoin d'une sorte d'échauffement, ne vous en faites pas. L'exemple d'application que vous avez décrit n'en a certainement pas besoin.

10
Kayaman

Pourquoi JVM nécessite un préchauffage?

Les machines virtuelles modernes (J) collectent des statistiques au moment de l'exécution sur le code utilisé le plus souvent et comment il est utilisé. Un exemple (parmi des centaines sinon des milliers) est l'optimisation des appels à des fonctions virtuelles (en langage C++) qui n'ont qu'une implémentation. Ces statistiques ne peuvent, par définition, être collectées qu'au moment de l'exécution.

Le chargement de classe lui-même fait également partie de l'échauffement, mais cela se produit évidemment automatiquement avant l'exécution du code à l'intérieur de ces classes, donc il n'y a pas grand-chose à craindre

Quelles parties du code devez-vous échauffer?

La partie cruciale pour les performances de votre application. L'important est de le "réchauffer" de la même manière qu'il est utilisé lors d'une utilisation normale, sinon les mauvaises optimisations seront effectuées (et annulées plus tard).

Même si je chauffe certaines parties du code, combien de temps reste-t-il chaud (en supposant que ce terme signifie seulement combien de temps vos objets de classe restent en mémoire)?

Il est vraiment difficile de dire que le compilateur JIT surveille en permanence l'exécution et les performances. Si un certain seuil est atteint, il tentera d'optimiser les choses. Il continuera ensuite à surveiller les performances pour vérifier que l'optimisation est réellement utile. Sinon, cela pourrait ne pas optimiser le code. Des choses peuvent également se produire, qui invalident les optimisations, comme le chargement de nouvelles classes. Je considérerais ces choses comme non prévisibles, du moins pas basées sur une réponse stackoverflow, mais il existe des outils pour vous dire ce que fait le JIT: https://github.com/AdoptOpenJDK/jitwatch

En quoi cela aide-t-il si j'ai des objets qui doivent être créés à chaque fois que je reçois un événement.

Un exemple simple pourrait être: vous créez des objets à l'intérieur d'une méthode, car une référence quitte le champ d'application de la méthode, ces objets seront stockés sur le tas, et éventuellement collectés par le garbage collector. Si le code utilisant ces objets est fortement utilisé, il pourrait finir par être intégré dans une seule grande méthode, éventuellement réorganisée au-delà de la reconnaissance, jusqu'à ce que ces objets ne vivent qu'à l'intérieur de cette méthode. À ce stade, ils peuvent être placés sur la pile et supprimés à la fin de la méthode. Cela peut économiser d'énormes quantités de déchets et ne se produira qu'après un certain échauffement.

Avec tout cela dit: je suis sceptique sur l'idée qu'il faut faire quelque chose de spécial pour s'échauffer. Lancez simplement votre application, et utilisez-la et le compilateur JIT fera très bien l'affaire. Si vous rencontrez des problèmes, découvrez ce que le JIT fait avec votre application et comment affiner ce comportement ou comment écrire votre application afin qu'elle en profite le plus.

Le seul cas où je connais réellement la nécessité d'un échauffement sont les repères. Parce que si vous le négligez, vous obtiendrez de faux résultats presque garantis.

3
Jens Schauder

Il s'agit du compilateur JIT, qui est utilisé sur le JVM pour optimiser le bytecode dans le runtime (parce que javac ne peut pas utiliser des techniques d'optimisation avancées ou agressives en raison de la plate-forme- caractère indépendant du bytecode)

  1. vous pouvez réchauffer le code qui traitera vos messages. En fait, dans la plupart des cas, vous n'avez pas besoin de le faire par des cycles d'échauffement spéciaux: laissez simplement l'application démarrer et traiter certains des premiers messages - JVM essaiera de faire de son mieux pour analyser l'exécution du code et faire des optimisations :) Un échauffement manuel avec de faux échantillons peut donner des résultats encore pires

  2. le code sera optimisé après un certain temps et sera optimisé jusqu'à ce qu'un événement du flux de programme dégrade l'état du code (après cela, le compilateur JIT essaiera à nouveau d'optimiser le code - ce processus ne se termine jamais)

  3. les objets à courte durée de vie sont également des sujets à optimiser, mais en règle générale, cela devrait aider votre traitement de message à coder le code foncier pour être plus efficace.

2
Cootri

Quelles parties du code devez-vous échauffer?

Il n'y a pas de réponse à cette question en général. Cela dépend entièrement de votre application.

Même si je chauffe certaines parties du code, combien de temps reste-t-il chaud (en supposant que ce terme signifie seulement combien de temps vos objets de classe restent en mémoire)?

Les objets restent en mémoire aussi longtemps que votre programme y fait référence, en l'absence de toute utilisation de référence faible spéciale ou quelque chose de similaire. Savoir quand votre programme "a une référence" à quelque chose peut être un peu plus obscur que vous ne le pensez à première vue, mais c'est la base de la gestion de la mémoire dans Java et en vaut la chandelle).

En quoi cela aide-t-il si j'ai des objets qui doivent être créés à chaque fois que je reçois un événement.

Cela dépend entièrement de l'application. Il n'y a pas de réponse en général.

Je vous encourage à étudier et à travailler avec Java pour comprendre des choses comme le chargement de classe, la gestion de la mémoire et la surveillance des performances. Il faut un certain temps pour instancier un objet, en général cela prend plus de temps à charger une classe (ce qui, bien sûr, se fait généralement beaucoup moins souvent). Habituellement, une fois qu'une classe est chargée, elle reste en mémoire pendant toute la durée du programme - c'est le genre de chose qui vous devriez comprendre, pas seulement obtenir une réponse.

Il existe également des techniques pour apprendre si vous ne les connaissez pas déjà. Certains programmes utilisent des "pools" d'objets, instanciés avant qu'ils ne soient réellement nécessaires, puis confiés au traitement une fois que le besoin s'en fait sentir. Cela permet à une partie critique du programme d'éviter le temps passé à instancier pendant la période critique. Les pools conservent une collection d'objets (10? 100? 1000? 10000?), Et instancient plus si nécessaire, etc. Mais la gestion des pools est un effort de programmation important, et, bien sûr, vous occupez de la mémoire avec les objets dans les pools .

Il serait tout à fait possible d'utiliser suffisamment de mémoire pour déclencher la récupération de place beaucoup plus souvent et ralentir le système que vous aviez l'intention d'accélérer. C'est pourquoi vous devez comprendre comment cela fonctionne, pas seulement "obtenir une réponse".

Une autre considération - de loin la plupart des efforts consentis pour accélérer les programmes sont gaspillés, car ils ne sont pas nécessaires. Sans une vaste expérience de l'application envisagée et/ou de la mesure du système, vous ne savez tout simplement pas où (ou si) l'optimisation sera même perceptible. La conception de systèmes/programmes pour éviter les cas pathologiques de lenteur est utile et ne prend pas beaucoup de temps et d'efforts pour une "optimisation". La plupart du temps, tout le monde en a besoin.

- modifier - ajouter une compilation juste à temps à la liste des choses à étudier et à comprendre.

2
arcy

Je l'ai toujours imaginé comme suit:

En tant que (développeur C++), vous pourriez imaginer une approche itérative automatisée par la compilation de jvm/chargement à chaud/remplacement de divers bits par morceaux avec ( l'analogue imaginaire de) gcc -O0, -O1, -O2, -O3 variantes (et parfois les annuler s'il le juge nécessaire)

Je suis sûr que ce n'est pas strictement ce qui se passe, mais cela pourrait être une analogie utile pour un développeur C++.

Sur un jvm standard, le temps nécessaire pour qu'un extrait soit pris en compte pour jit est défini par -XX:CompileThreshold qui est 1500 par défaut. (Les sources et les versions jvm varient - mais je pense que c'est pour jvm8)

De plus, un livre que j'ai sous la main indique sous Host Performace JIT Chapter (p59) que les optimisations suivantes sont effectuées pendant JIT:

  • Inlining
  • Élimination des verrous
  • Élimination des appels virtuels
  • Élimination d'écriture en mémoire non volatile
  • Génération de code natif

ÉDITER:

concernant les commentaires

Je pense que 1500 peut être juste suffisant pour laisser entendre à JIT qu'il devrait compiler le code en natif et arrêter l'interprétation. accepteriez-vous?

Je ne sais pas si c'est juste un indice, mais comme openjdk est open-source, regardons les différentes limites et nombres dans globals.hpp#l3559@ver-a801bc33b08c (pour jdk8u)

(Je ne suis pas un développeur jvm, cela pourrait être le mauvais endroit pour regarder)

Compiler un code en natif ne signifie pas nécessairement qu'il est également optimisé.

À ma compréhension - vrai; surtout si vous voulez dire -Xcomp (forcer la compilation) - ceci blog déclare même que cela empêche le jvm de faire du profilage - donc d'optimiser - si vous n'exécutez pas -Xmixed (le défaut).

Ainsi, une minuterie entre en action pour échantillonner le code natif fréquemment utilisé et l'optimiser. Savez-vous comment nous pouvons contrôler cet intervalle de minuterie?

Je ne connais vraiment pas les détails, mais le gobals.hpp J'ai lié définit en effet quelques intervalles de fréquence.

1
birdspider