Présentation technique détaillée de JIT (Just-in Time Compilation) pour PHP OPCache
Le JIT OPCache ou Just-in-Time Compiler fait son entrée avec PHP 8, pour plus de performances !
En effet, la version 8 de PHP est la seule donnant accès la la nouvelle technologie de « Compiler » de Zend Technologies : « JIT ». Il s’agit d’un complément au système de Cache PHP : OPCache (plus d’infos). À l’heure où nous écrivons ces lignes, nous n’avons encore jamais rencontré d’hébergeur web français communiquant sur cette technologie PHP récente. D’ailleurs, nous n’avons vu aucun hébergeur (même US), proposant de l’assistance à ses clients pour la mise en œuvre de ce nouveau Compiler PHP Performant. Donc, si vous êtes utilisateur EasyHoster, profitez-en pour découvrir cette nouvelle technologie PHP !
Donc, comme nous l’avons vu, OPcache permet d’éliminer le besoin de charger et d’analyser des scripts à chaque requête en stockant le bytecode de script précompilé dans la mémoire partagée. Cet avantage majeur est que le code source n’a toujours pas besoin d’être précompilé, mais avec un cache partagé de code machine compilé, le langage peut déclencher l’exécution du code avec du code machine compilé, compilé pour plus tard, ou exécuté sans JIT. Cependant, la mise en œuvre de JIT a ajouté un obstacle considérable au débogage, étant donné que certaines parties de l’application pourraient être mises en cache sous forme de « code machine CPU », avec lequel les débogueurs PHP standard ne peuvent pas fonctionner.
Contrairement à OPCache, qui met en mémoire le bytecode entièrement compilé, JIT compile les instructions à la demande lorsqu’elles sont rencontrées dans un script. Cela signifie que les instructions n’ont pas besoin d’être précompilées et stockées en mémoire, ce qui permet d’économiser une quantité considérable de mémoire et d’améliorer la vitesse d’exécution.
Ce nouveau compilateur JIT propose également des techniques d’optimisation avancées, telles que le réarrangement du code pour améliorer les performances, l’analyse des valeurs de données pour optimiser leur utilisation et la spécialisation des instructions pour un meilleur débit. Tous ces éléments se combinent pour améliorer considérablement les performances par rapport à la seule mise en cache OPCache, qui était déjà l’un de nos indispensables chez EasyHoster !
Outre l’amélioration des performances, le compilateur Just-in-Time offre également un environnement plus sûr pour l’exécution des scripts en éliminant les vulnérabilités potentielles d’un code malveillant. En compilant le code au fur et à mesure de son exécution plutôt qu’en s’appuyant sur un bytecode précompilé, le JIT réduit considérablement les chances de réussite d’une attaque contre une application ou un script. Dans l’ensemble, la compilation Just-in-Time est sur le point de révolutionner la manière dont PHP est utilisé dans les applications web et apportera des améliorations de performance bienvenues qui rendront l’écriture de scripts plus facile et plus rapide que jamais. Avec ses mesures de sécurité accrues et son utilisation optimisée des ressources, l’implémentation de la compilation Just-in-Time de PHP 8 promet une plus grande efficacité dans tous les domaines.
Comprendre les bases de PHP JIT sous le capot (Buffer, Tracer…)
OPcache permet d’éliminer le besoin de charger et d’analyser des scripts à chaque requête en stockant le bytecode de script précompilé dans la mémoire partagée.
Cet avantage majeur est que le code source n’a toujours pas besoin d’être précompilé, mais avec un cache partagé de code machine compilé, le langage peut déclencher l’exécution du code avec du code machine compilé, compilé pour plus tard, ou exécuté sans JIT.
Cependant, la mise en œuvre de JIT a ajouté un obstacle considérable au débogage, étant donné que certaines parties de l’application pourraient être mises en cache sous forme de « code machine CPU », avec lequel les débogueurs PHP standard ne peuvent pas fonctionner.
Le JIT PHP est intégré en tant que partie d’Opcache. Cela permet de garder JIT séparé du moteur PHP. Le JIT se compose de trois composants : stocker, inspecter et exécuter le code avec la machine virtuelle ou directement à l’aide du code machine stocké dans le tampon.
Le tampon JIT (Buffer) est l’endroit où le code machine CPU compilé est stocké. PHP fourni des options de configuration pour contrôler la quantité de mémoire à allouer au tampon JIT. Les déclencheurs dans Opcache sont responsables de l’exécution du code machine compilé lorsqu’il rencontre une structure de code. Ces déclencheurs peuvent être une entrée d’appel de fonction, une boucle, etc.
La fonctionnalité de traçage JIT (Tracer) inspecte le code avant, après ou pendant son exécution, et détermine quel code est « chaud », c’est-à-dire quelles structures peuvent être compilées avec JIT. Le traçage peut compiler le code pendant qu’il est en cours d’exécution, lorsqu’une certaine structure de code atteint le seuil, qui est également configurable.
Configuration avancée de PHP JIT ?
La configuration de base de JIT est assez simple à mettre en place. Par défaut, le JIT est activé mais peut être désactivé en limitant la taille du tampon. Pour une configuration minimale, la taille du tampon pour JIT est définie, et JIT utilisera les valeurs par défaut sensibles.
Pour cela, vous pouvez utiliser les commandes suivantes :
opcache.enable=1
opcache.jit_buffer_size=256M
Ceci alloue 256 Mo pour le tampon JIT, et active également JIT sur les applications CLI. Il est recommandé de définir cette valeur au maximum à 10% de votre RAM actuelle. Par exemple, si vous avez 1G de mémoire RAM, une valeur de tampon JIT de 128M est une bonne valeur.
La directive opcache.jit permet de régler finement la fonctionnalité JIT.
opcache.jit=tracing
La valeur de configuration opcode.jit est quelque peu complexe. Elle accepte disable, on, off, tracing, function, et une valeur à 4 chiffres (pas un masque binaire) de 4 drapeaux différents dans l’ordre. Les options ‘disable’, ‘off’, ‘on’, ‘tracing’, et ‘function’ ont chacune leurs propres implications et configurations associées, détaillées dans la section précédente.
- disable: Désactive complètement la fonction JIT au moment du démarrage, et ne peut pas être activée en cours d’exécution.
- off: Désactivé, mais il est possible d’activer JIT en cours d’exécution.
- on: Active le mode de traçage.
- tracing: Un alias pour la configuration granulaire 1254.
- function: Un alias pour la configuration granulaire 1205.
La configuration de PHP JIT accepte les options de traçage ou de fonction, ainsi qu’une valeur de configuration à 4 chiffres pour le paramètre opcache.jit. Ces chiffres, sous la forme CRTO, permettent d’ajuster le comportement du JIT. Par défaut, la configuration est opcache.jit=tracing. Les configurations JIT de fonction et de traçage utilisent les instructions et les allocations de registres de CPU pour maximiser les capacités du CPU.
La fonction opcache_get_configuration peut être utilisée pour déterminer le mode du JIT (traçage, fonction, etc.). La configuration de fonction compile le script entier dès que possible. C’est une approche plus audacieuse, semblable à la précharge des fichiers PHP dans Opcache avec la fonction de préchargement en PHP 7.4.
Avec le traçage activé, JIT peut être plus granulaire et choisir les segments de code à compiler dans une fonction. Les structures de bouclage et les fonctions qui sont fréquemment appelées sont des candidats idéaux.
Il existe également des directives pour déterminer après combien d’itérations une boucle, une fonction, un retour ou une sortie latérale sont considérés comme « chauds », puis compilés en JIT. Les valeurs par défaut sont probablement les plus adaptées pour la plupart des applications.
Configuration de JIT Idéale
Avoir plus de code compilé par JIT ne signifie pas nécessairement une application plus rapide (comme on peut le voir dans les benchmarks d’applications web ci-dessous). Le coût de la compilation, associé à un tampon plus petit, peut ralentir les applications en raison du temps passé sur les étapes de compilation JIT.
La valeur opcache.jit est préférable de rester inchangée (par défaut c’est le traçage) car elle offre déjà un bon équilibre entre l’utilisation du CPU, la mémoire et le suivi des structures de code qui sont compilées. Le JIT n’apportera pas d’avantages de performance significatifs pour les applications fortement liées à l’IO. La majorité des applications web d’aujourd’hui sont en fait lourdes en IO, où le JIT ne fera pas de différence, encore moins une positive. Il est important de calculer la taille du tamponPour la taille du tampon, faites attention à ne pas avoir une mémoire trop petite, ce qui peut gaspiller le code compilé par JIT et entraîner des recompilations fréquentes. Une mémoire trop grande peut aussi être exagérée. Une valeur de 50 à 100% de la mémoire partagée actuelle de l’Opcache pour Opcode pourrait être la valeur idéale pour opcache.jit_buffer_size.
Comparatif de performances avant/après JIT
Benchmark JIT et OPCache PHP 8 vs PHP 7
Comme vous pouvez le voir dans le Benchmark ci-dessous, l’apport en termes de performances, pour certaines applications PHP, est encore plus stupéfiant, lorsque PHP 8 est allié à OPCache, ainsi qu’à JIT… jusqu’à -55% de temps de chargement sur certaines applications après l’activation de JIT !

2e benchmark de PHP JIT

Le script PHP comprend deux tests de performance, qui évaluent diverses fonctionnalités de PHP. Les fichiers micro_bench.php et bench.php ont été testés sur la branche PHP 8.0. Pour les tests, une fonction calculant le nième nombre dans la séquence de Fibonacci a été utilisée. Quatre cas ont été testés avec différentes configurations. Les résultats montrent que les meilleurs résultats sont obtenus avec le JIT de traçage.
Le JIT peut apporter un gain de performance significatif pour les applications effectuant des calculs intensifs en CPU. Cependant, son impact peut être minimal ou même négatif dans les applications où la base de données, le réseau ou l’I/O sont des goulets d’étranglement. Les deux modes JIT apportent des gains de performance substantiels, avec le mode de traçage légèrement en tête. Il est important de noter que ce test de performance ne représente guère une application PHP en temps réel.