
Compilateur JIT dans PHP 8
PHP est un langage de programmation interprété, qui a longtemps eu du mal à améliorer ses performances, qui étaient particulièrement faibles par rapport à d'autres langages de programmation concurrents utilisés dans le développement web. Parmi ce qu'il y a de nouveau dans PHP 8, le compilateur JIT mérite une attention particulière, car il a été officiellement inclus dans la dernière version. Sera-t-il une véritable percée, ou simplement une addition intéressante au langage ? Voyons quels avantages réels il peut apporter et s'il apportera une amélioration concrète des performances de votre application.
Certains lecteurs pourraient ne pas se souvenir que, à partir de PHP 5.5, OPcache exécuté par Zend VM est devenu une partie intégrante du langage. Ce fut une avancée en termes d'amélioration des performances des applications, et donc dans le contexte des services de développement web PHP . La version ultérieure de PHP 7 a offert encore de meilleures performances. Même à ce stade, il y avait des propositions pour implémenter et ajouter un compilateur JIT au langage ; cependant, la communauté a fait tous les efforts possibles pour maximiser l'efficacité en utilisant les mécanismes disponibles.
Qu'est-ce que JIT ?
En termes simples, JIT (compilation en temps réel) est une technique qui permet de compiler un programme en code machine juste avant qu'il ne soit exécuté. En réalité, cette approche se traduit par une exécution du code significativement plus rapide par rapport à un interpréteur traditionnel. Parfois, JIT est qualifié de juste milieu entre la flexibilité dans le développement du code de l'application et ses performances à l'exécution.
Principe de fonctionnement
Comme je l'ai mentionné plus tôt, jusqu'à présent, de meilleures performances PHP étaient basées sur OPCache utilisant le soi-disant OPCode, qui est un extrait de code précompilé sous forme de commandes données au processeur pour exécution, créé après que le script ait été exécuté pour la première fois. Le code mis en cache peut être exécuté presque immédiatement par une machine virtuelle ; cependant, ce n'est toujours pas du code machine natif.
JIT offre un véritable code machine qui a été implémenté de manière à fonctionner avec OPCache. Lorsque le script est lancé pour la première fois, s'il a déjà été mis en cache dans OPCache, il est immédiatement renvoyé et compilé (si ce n'était pas déjà fait). Cependant, dans le cas où le script n'a pas encore été mis en cache, il passe d'abord par le processus de génération d'OPCode complet, comme précédemment. L'ensemble du processus est parfaitement illustré dans le schéma ci-dessous.
Configuration de JIT
Si vous pensez que JIT fait simplement partie du langage/interpréteur et fonctionne automatiquement, vous vous trompez. Malheureusement, il nécessite une configuration supplémentaire, qui, à première vue, ne semble pas particulièrement conviviale et évidente. L'ensemble de l'opération est effectuée dans le fichier de configuration php.ini, bien connu des programmeurs PHP.
La première chose importante est que JIT ne fonctionne que lorsque OPCache est activé. Chaque installation par défaut de PHP a cette valeur (opcache.enable) définie à 1 immédiatement. Ensuite, pour déverrouiller JIT, nous devons définir deux paramètres :
- opcache.jit_buffer_size
- opcache.jit
Le premier (opcache.jit_buffer_size) est responsable de la définition de la quantité de mémoire que nous voulons allouer pour la compilation de code. Définir une valeur d'exemple est donc assez simple :
opcache.jit_buffer_size=256M
Quant au second paramètre (opcache.jit), il vous indique en gros comment JIT est censé fonctionner. Pour changer, permettez-moi de commencer par un exemple :
opcache.jit=1255
Oui, j'étais également surpris par la valeur numérique mystérieuse. Au début, je pensais que c'était une sorte de masque de bits ou quelque chose du genre. Cependant, lorsque j'ai analysé le RFC, dans lequel nous trouvons plus de détails sur les options individuelles, il s'est avéré que chacun de ces chiffres séparément est une valeur de configuration spécifique. J'aimerais donc présenter trois valeurs de configuration qui pourraient être les plus utilisées. Ils sont un raccourci pour aider les développeurs à définir le mode souhaité.
- opcache.jit = 1205 - tout le code est compilé avec JIT
- opcache.jit = 1235 - seules certaines parties du code (basées sur leur utilisation relative) sont passées à la compilation JIT
- opcache.jit = 1255 - le code de l'application est suivi pour compilation par JIT et certaines parties du code sont transférées au compilateur
Bien sûr, je n'étais pas le seul à avoir souligné l'accessibilité discutable de cette configuration. Par conséquent, des corrections ont été ajoutées au RFC susmentionné, en fait deux alias tracing et function qui peuvent être utilisés à la place de valeurs numériques, par exemple
opcache.jit=tracing
La différence entre ces modes est que JIT optimise le code uniquement dans le cadre d'une fonction lorsqu'il utilise la valeur function. Cependant, en utilisant une valeur de tracing, il examine tout le traçage de pile et recherche le code optimisable. Parmi ces deux options, la plus recommandée est le JIT tracing, qui donne les résultats les plus prometteurs dans les benchmarks, augmentant considérablement la performance de l'application.
Impact sur les performances des applications web
Tout bon développeur doit savoir que le facteur le plus important qui affecte la performance de l'application est la qualité de leur code. Un autre facteur tout aussi important est le choix des technologies qui composent son stack complet et la manière dont elles sont développées. Dans le cas de PHP, OPCache a été une révolution ; cependant, JIT - malgré le battage médiatique énorme - ne semble pas être le même jalon, surtout pour les applications web. Pourquoi ?
JIT a été introduit dans le but de compiler des lots de code qui ne sont pas soumis à des fluctuations significatives. Il détecte les extraits de code qui sont exécutés plus d'une fois et les compile en conséquence. Comme vous pouvez probablement déjà le deviner, l'exécution de code lors de la gestion d'une requête/demande unique d'une application web dépend de trop de variables et en réalité, il s'avère que ces extraits de code identiques sont peu nombreux et espacés. De plus, dans certains cas, il peut y en avoir si peu que, au lieu de vraiment accélérer l'application, JIT la ralentira en raison du fardeau supplémentaire de la compilation du code.
Dans l'un des tests disponibles publiquement du projet basé sur le framework Laravel et décrit dans l'article medium.com, nous pouvons constater que son exécution en PHP 8 avec JIT offre seulement une légère amélioration des performances :
PHP 7.3 : 131,37 req/s
PHP 8.0 + JIT : 133,57 req/s
On peut clairement voir que, dans les applications web, la performance ajoutée sera à peine perceptible.
Cette thèse est confirmée dans le benchmark public présenté par le Groupe PHP dans le cadre de la publication de PHP 8.
Comme vous pouvez le voir, les applications conçues pour des applications web telles que WordPress, MediaWiki ou la démo Symfony obtiennent des résultats similaires, légèrement supérieurs ou même inférieurs (lors de l'utilisation du Function JIT), que lors de l'exécution en PHP 8 sans utiliser JIT. Dans d'autres cas, cependant, la situation est assez différente. Pour les benchmarks synthétiques, ou pour des tâches telles que la génération de fractales, l'efficacité peut être jusqu'à 3 fois supérieure.
Dans d'autres cas, comme des applications de longue durée, c'est un gain de 1,5 à 2 fois en matière de performance. Comme vous pouvez le voir, c'est en fait une percée qui offre de nouvelles possibilités d'utilisation du langage en dehors des applications web classiques.
À quoi cela sert-il donc ?
L'introduction d'un compilateur JIT dans PHP est en fait un pas vers l'ouverture du langage à de nouvelles possibilités. JIT améliore considérablement les performances d'exécution du code dans les applications nécessitant un usage intensif du processeur. Il s'agit d'utiliser PHP à des fins complètement nouvelles, telles que l'apprentissage automatique, les calculs mathématiques complexes ou le traitement/modélisation d'images 2D/3D.
Une vidéo de preuve de concept créée par l'un des principaux développeurs PHP (Zeev Suraski) circule sur le web depuis un certain temps, montrant des améliorations de performances grâce à JIT - dans son cas, il s'agissait de générer des fractales en temps réel.
Selon divers benchmarks disponibles sur le web (par exemple https://www.phoronix.com/scan.php?page=article&item=php8-jit-june&num=2), selon l'application, JIT offre une réelle amélioration des performances, allant de simples chiffres à une amélioration presque à cent pour cent.
Avantages et inconvénients
La mise en œuvre du compilateur JIT est un pas vers l'ouverture du langage à de nouvelles possibilités et rendre, par exemple, PHP parfait pour les startups. La capacité de compiler directement le code PHP réduira la dépendance à l'égard de C, ce qui facilitera et rendra possible le développement sans implications particulières en matière de performance. Indéniablement, JIT améliore la performance des applications dans une plus ou moins grande mesure.
D'autre part, dans sa forme actuelle, JIT est incapable d'améliorer significativement les performances des applications web, et dans certains scénarios spécifiques, cela pourrait même entraîner des performances pires qu'auparavant. La façon dont il fonctionne peut également affecter le processus de développement, rendant le débogage du code plus difficile, car il y a quelques problèmes connus avec l'outil xDebug. JIT nécessitera également des connaissances de configuration supplémentaires de la part des développeurs.
Conclusions
Chez Droptica, où nous fournissons des services Drupal, du support Drupal et notre équipe comprend de nombreux programmeurs PHP, nous savons que bien que JIT dans le monde ne soit pas entièrement nouveau (Facebook et leur HHVM étaient les pionniers), il deviendra bientôt une partie native du langage avec la sortie de PHP 8. JIT va améliorer la performance d'exécution du code et étendre les possibilités d'utilisation du langage lui-même. Je pense que dans les années à venir, nous devrions suivre son développement, ce qui peut apporter des résultats encore meilleurs, surtout dans le contexte des applications web.