Quelle amélioration de performances pouvez-vous attendre d'une application Symfony 5 avec le préchargement PHP OPcache ?

Cela fait maintenant plus de six mois que le lancement de PHP 7.4 et de son préchargement OPcache a eu lieu. Cette fonctionnalité promet de rendre plus efficace l'exécution d'applications complexes, en gardant certaines parties d'entre elles dans la mémoire long terme. Au départ, il était pourtant difficile de faire fonctionner des applications complexes.

 

Cela était du au fait que le framework Symfony et les dépendances comme Doctrine ou le PHP lui-même avaient tous besoin d'un peu de travail pour fonctionner de pair avec la fonctionnalité. Depuis les premières étapes d'essais d'exécution eZ Platform avec l'OPcache Preloading, l'écosystème PHP / Symfony a désormais suffisamment mûri pour que vous puissiez vous attendre à améliorer les performances avec la fonctionnalité.

Au cours des premiers mois de 2020, le PHP a reçu un certain nombre de versions de correctifs portant la dernière version à 7.4.6. Ceux-ci ont inclus des correctifs de bugs et de sécurité ainsi que des améliorations de compatibilité pour le préchargement. La prise en charge du framework Symfony pour la fonctionnalité est également passée de la compatibilité initiale dans 4.4 à une prise en charge plus configurable et robuste dans Symfony 5.1. Enfin, d'autres dépendances majeures comme Doctrine ont été mises à jour et sont compatibles avec l'OPcache Preloading.

Pendant ce temps, eZ Platform a également reçu des mises à jour importantes, notamment une mise à niveau vers Symfony 5 dans la dernière version majeure : eZ Platform v3.0. Quelques semaines plus tard, eZ Platform a reçu une compatibilité complète avec PHP 7.4. Cela fait d'eZ Platform un bon candidat pour l'analyse comparative :  comment les utilisateurs de Symfony 5 peuvent améliorer leurs performances en activant le préchargement OPcache.

Mise en route et fonctionnement de l'OPcache Preloading 

Symfony 5.1 est désormais la dernière version stable de Symfony. eZ Platform 3.0 a été déployé alors que Symfony était encore à sa version 5.0. De fait avec la dernière version officielle, eZ Platform n'a pas accès à l'ensemble des dernières améliorations du préchargement de l'OPcache effectuées sur les composants Symfony. Cependant, la prochaine version, eZ Platform 3.1, dont la sortie est prévue fin juin 2020, inclura une mise à niveau vers Symfony 5.1.

En tant que plate-forme d'analyse comparative, j'ai décidé d'utiliser une copie de Windows fonctionnant sur un LattePanda Alpha SBC avec 8 Go de RAM et 512 Mo de SSD. Concernant la pile logicielle requise pour exécuter les applications Symfony comme eZ Platform, j'ai utilisé Ubuntu 20.20 (avec Nginx installé) comme décrit dans notre blog précédent (en anglais) : Configuration d'un environnement de développement PHP pour Symfony avec Windows et WSL2

Vous pouvez considérer WSL2 comme une plate-forme d'analyse comparative non conventionnelle, mais rappelez-vous que vous devez garder un œil sur les améliorations relatives (avec et sans préchargement OPcache) au lieu du débit absolu. Lors des tests, le LattePanda exécutait uniquement notre application. Pour la génération de charge, j'ai utilisé un ordinateur portable sur Gigabit Ethernet connecté au même commutateur que le SBC. La charge a été générée en utilisant hey. TLS / SSL (par la suite HTTP / 2/3) n'était pas activé sur Nginx.

Au moment de la rédaction, il n'y avait pas de version officielle d'eZ Platform avec Symfony 5.1, mais j'ai utilisé un instantané de la branche principale et mis à niveau les dernières dépendances (y compris Symfony 5.1) :

janit@W1ND0Z1337:/var/www$ git clone https://github.com/ezsystems/ezplatform.git
Cloning into 'ezplatform'...
remote: Enumerating objects: 86, done.
remote: Counting objects: 100% (86/86), done.
remote: Compressing objects: 100% (48/48), done.
remote: Total 16214 (delta 49), reused 45 (delta 38), pack-reused 16128
Receiving objects: 100% (16214/16214), 10.72 MiB | 438.00 KiB/s, done.
Resolving deltas: 100% (8300/8300), done.
janit@W1ND0Z1337:/var/www$ cd ezplatform
janit@W1ND0Z1337:/var/www/ezplatform$ git checkout da04a3e7fe14e5ee1c76d1924dbf77b53755f62c
HEAD is now at da04a3e Merge remote-tracking branch 'origin/3.0'
janit@W1ND0Z1337:/var/www/ezplatform$ composer update

Une fois terminé, j'ai configuré l'OPcache avec les options suivantes, ajoutées à l'installation standard :

opcache.preload_user=www-data
opcache.preload=/var/www/ezplatform/var/cache/prod/App_KernelProdContainer.preload.php
opcache.memory_consumption=1024
opcache.interned_strings_buffer=256
opcache.max_accelerated_files=30000
opcache.validate_timestamps=0

Ensuite, j'ai configuré l'environnement Symfony pour produire à partir de la configuration Nginx et j'ai continué à le redémarrer ainsi que le PHP-FPM. Pour Nginx, le redémarrage s'est bien passé, mais le PHP-FPM n'a pas pu redémarrer :

janit@W1ND0Z1337:/var/www/ezplatform$ sudo /etc/init.d/nginx restart
 * Restarting nginx nginx                                                        [ OK ]
janit@W1ND0Z1337:/var/www/ezplatform$ sudo /etc/init.d/php7.4-fpm restart
 * Restarting PHP 7.4 FastCGI Process Manager php-fpm7.4                         [fail]

Nginx n'a pas démarré. Je pensais que cela était du au fait que le fichier de préchargement généré automatiquement (App_KernelProdContainer.preload.php) n'existait pas, j'ai donc effacé les caches pour le générer :

janit@W1ND0Z1337:/var/www/ezplatform$ ./bin/console cache:clear --env=prod

 // Clearing the cache for the prod environment with debug false

 [OK] Cache for the "prod" environment (debug=false) was successfully cleared.

janit@W1ND0Z1337:/var/www/ezplatform$ head var/cache/prod/App_KernelProdContainer.preload.php
<?php

// This file has been auto-generated by the Symfony Dependency Injection Component
// You can reference it in the "opcache.preload" php.ini setting on PHP >= 7.4 when preloading is desired

use Symfony\Component\DependencyInjection\Dumper\Preloader;

require dirname(__DIR__, 3).'/vendor/autoload.php';
require __DIR__.'/ContainerTmRMTu6/App_KernelProdContainer.php';
require __DIR__.'/ContainerTmRMTu6/EzcSystemInfoWrapper_de0c8e5.php';
janit@W1ND0Z1337:/var/www/ezplatform$

Mais même après cette étape le PHP-FPM n'a toujours pas démarré. J'ai donc cherché dans les données du serveur et j'ai trouvé cette erreur :

Jun  9 13:52:58 |ERROR| PHP    PHP message: PHP Fatal error:  Uncaught Error: Class
    'Symfony\Cmf\Component\Routing\ChainRouterBaseBcLayer' not found in
    /var/www/ezplatform/vendor/ezsystems/routing/src/ChainRouterBcLayer.php:41

J'ai pensé qu'il y avait un problème de compatibilité avec l'OPcache Preloading ici, alors je l'ai reporté :

// $classes[] = 'eZ\Publish\Core\MVC\Symfony\Routing\ChainRouter';

Une fois cette ligne sortie, le PHP-FPM a démarré. J'ai confirmé, via phpinfo (), que le préchargement était actif :

Analyse comparative du débit d'eZ Platform

Pour l'analyse comparative, je voulais comprendre comment l'OPcache fonctionne à travers différents scénarios. J'ai commencé par charger la page d'accueil avec du contenu via le pipeline de rendu de modèles :

hey -c 10 -n 10000 http://192.168.42.10/

Pour signaler les performances de l'API, j'ai fait un test similaire à l'API REST eZ Platform :

hey -c 10 -n 10000 http://192.168.42.10/api/ezp/v2/content/locations/1/2

J'ai exécuté les deux tests trois fois avec différentes devises, avec et sans le préchargement de l'OPcache activé. Les valeurs moyennes des trois essais sont présentées dans les graphiques ci-dessous.

Débit et temps de réponse pour le contenu rendu

L'activation du préchargement entraîne systématiquement une augmentation du débit. Les améliorations varient de 9 à 13% selon la fréquence. Curieusement, le pic est à 10 demandes simultanées, il peut donc y avoir des inefficacités internes dans le système d'exploitation ou le(s) runtime(s) en jeu.

Pour les temps de réponse, les résultats sont concordants avec les résultats de débit. L'ensemble de résultats de référence activés pour le préchargement indique systématiquement une latence plus faible pour les réponses. A noter : les temps de réponse sont nettement inférieurs lorsqu'il y a une fréquence moindre. Investir dans de vastes ressources offre une meilleure expérience utilisateur, même si un débit suffisant peut être atteint avec des ressources limitées.

Débit et temps de réponse de l'API REST

La comparaison du débit de test de l'API REST pour récupérer un objet de localisation entraîne également un débit plus élevé de façon constante. Lorsque le préchargement est activé, le résultat est une amélioration de 3%, alors que sans, il est d'environ 1,5%. Les tests ne sont pas 1: 1, mais la différence dans l'affichage des données via le pipeline de rendu n'a pas d'impact significatif sur le débit de notre architecture d'application.

Pour les temps de réponse, le préchargement présente également des améliorations constantes. À des latences faibles, les temps de réponse sont pratiquement égaux dans la pratique, mais à des devises plus élevées, la configuration non préchargée donne environ 15% de temps de réponse plus lents, soit une différence d'environ 20 millisecondes.

Conclusion

En conclusion, les améliorations de performances offertes par l'OPcache Preloading avec le PHP 7.4 sont tout à fait remarquables. Sans aucune modification de notre application, le simple fait d'activer l'OPcache Preloading sur le PHP 7.4 a entraîné une augmentation de 14% du débit et une diminution de 12,5% des temps de réponse moyens.

Les modèles de trafic réels donnent des résultats plus variés, mais même une amélioration moyenne réaliste de 10% des mesures mesurées est significative. Ils se traduiront directement par un coût inférieur, une expérience utilisateur améliorée et un impact environnemental réduit de l'exécution d'un service en ligne à grande échelle.

L'usage d'OPcache Preloading a amélioré les limites de la PHP depuis la sortie de la version 7.4.0 le 1er décembre 2019. Bien qu'il y ait probablement encore un certain nombre d'aspects à améliorer, la stack principale des applications basées sur Symfony 5 est suffisamment mature pour être considérée pour le déploiement de la production en 2020.

Pour les applications s'appuyant sur Symfony, ce n'est pas nécessairement le cas. Pour eZ Platform, nous aurons encore besoin de plus de tests et de validation (comme la correction de EZP-31679) jusqu'à ce que nous y soyons. Et cela sans même mentionner les implémentations du projet. Les ajustements manuels au préchargement qui sont nécessaires aujourd'hui ne sont pas pratiques pour le déploiement en réalité, mais dans l'ensemble, il semble que nous partions sur un début prometteur.

Notre équipe d'ingénieurs travaille dur pour délivrer eZ Platform 3.1 plus tard dans le mois. Il y aura de nouvelles fonctionnalités pour l'utilisateur final ainsi que des améliorations pour les développeurs. Pour en savoir plus, consultez la feuille de route du produit et restez à l'écoute dans les prochaines semaines pour de nouveaux articles de blog détaillant la prochaine version.

Insights and news