
Tester les performances de PHP (ou pourquoi HHVM c’est bien)

Un autre article dans la lignée de ceux m’amenant vers une infrastructure à même d’absorber le trafic toujours plus important du blog. En attendant la prochaine version de Debian qui s’accompagne de PHP 5.6, j’ai commencé à faire des tests à la maison sur les gains potentiels d’un passage à PHP 5.5, et, par curiosité, à HHVM. Test synthétique, test pratique, comparaison… Et un point de plus d’amélioré dans mon infrastructure.
Prérequis, préparation
Avant toute chose, j’ai fait une sauvegarde de ma machine virtuelle. Si quelque chose pète, je peux toujours revenir à l’état précédent comme ça. Vu que les tests se sont étalés sur plusieurs jours, j’ai évité les snapshots. Mais c’est l’idée, même si je ne le rappelle pas toujours : toujours sauvegarder avant de fusiller un truc, toujours.
Ensuite, ma VM sous Debian Wheezy a été « installée » avec le script de nicorlargo, qui compile nginx et installe php-fpm depuis les dépots Dotdeb. Vous avez compris l’idée : je pars du principe que vous utilisez PHP-FPM, qui est utilisable même avec Apache (d’ailleurs j’avais zappé l’utilisation de mpm-worker, corrigé depuis–une autre piste d’optimisation pour ceux qui ne veulent pas d’nginx ?). C’est d’une part plus performant, et surtout ça sera pratique quand on introduira HHVM dans l’équation.
Le test théorique
Pour faire mes mesures, j’ai utilisé un petit script, un peu ancien (2012), mais qui fait le taf à priori. Il s’appelle PHP Benchmark Script, il est gratuit, et on le récupère simplement à cette adresse. C’est une archive contenant le script bench.php qu’il suffit de mettre à la racine de votre serveur web/vhost et de lancer, au bout de quelques secondes il affiche les résultats du test au format texte. C’est un test un peu théorique, mais c’est reproductible, ce qu’on cherche justement.
Le test « monde réel »
Depuis mon ordinateur portable, je contacte la copie du blog à l’aide de l’utilitaire ab. Un nom qui signifie Apache Benchmark, qui fait partie du paquet apache2-utils sous Debian; un paquet apache-tools est dispo dans AUR pour Manjaro (attention, compilation un poil longue, et surtout verbeuse).
Le test est donc le suivant :
1 |
ab -n 100 -c 1 http://test.seboss666.info/blog/ |
Ça fait quoi ? 100 requêtes, une seule à la fois, sur la page d’accueil du blog. Vous pouvez adapter, ou faire le même test avec de plus gros paramètres, pour vérifier la tenue de charge. Je serais donc de toute façon « limité » à l’upload de ma connexion.
Ajout du dépot « kivabien » et installation
En effet, même si vous utilisez déjà les dépôts dotdeb (qui fournissent en plus la dernière version stable d’nginx si vous êtes frileux de la compilation), les paquets relatifs à PHP 5.5 sont séparés, les dépôts standards se cantonnant à la « branche » 5.4 :
1 2 3 4 5 6 7 8 9 10 11 12 |
seboss666@heberg-new:~$ dpkg -l |grep php ii php-apc 3.1.13-1 amd64 APC (Alternative PHP Cache) module for PHP 5 ii php-pear 5.4.36-1~dotdeb.1 all PEAR - PHP Extension and Application Repository ii php5-cli 5.4.36-1~dotdeb.1 amd64 command-line interpreter for the php5 scripting language ii php5-common 5.4.36-1~dotdeb.1 amd64 Common files for packages built from the php5 source ii php5-curl 5.4.36-1~dotdeb.1 amd64 CURL module for php5 ii php5-dev 5.4.36-1~dotdeb.1 amd64 Files for PHP5 module development ii php5-fpm 5.4.36-1~dotdeb.1 amd64 server-side, HTML-embedded scripting language (FPM-CGI binary) ii php5-gd 5.4.36-1~dotdeb.1 amd64 GD module for php5 ii php5-mcrypt 5.4.36-1~dotdeb.1 amd64 MCrypt module for php5 ii php5-memcache 5.4.36-1~dotdeb.1 amd64 memcache module for php5 ii php5-mysql 5.4.36-1~dotdeb.1 amd64 MySQL module for php5 |
Donc, on crée un nouveau fichier dans le dossier /etc/apt/sources.list.d/, qu’on appellera php55-dotdeb.list (attention à l’extension, sinon le fichier sera ignoré–oui je me suis fait avoir), et dans lequel on mettra ceci :
1 2 |
deb http://packages.dotdeb.org wheezy-php55 all deb-src http://packages.dotdeb.org wheezy-php55 all |
Si vous n’avez pas déjà les dépôts standards d’installés, je conseille d’ajouter les deux lignes les concernant :
1 2 |
deb http://packages.dotdeb.org wheezy all deb-src http://packages.dotdeb.org wheezy all |
Une fois fait, on sauvegarde, et on récupère les clés de signatures histoire de s’éviter quelques messages d’erreur, et surtout valider la provenance des paquets. Sans ces signatures, un « malfrat » pourrait détourner le trafic du dépôt, pour vous diriger vers une copie contenant des paquets infectés.
Bref, récupération des clés :
1 2 |
wget http://www.dotdeb.org/dotdeb.gpg sudo apt-key add dotdeb.gpg |
Tout est prêt, on peut maintenant mettre à jour la liste de paquets, et on installe :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
seboss666@heberg-new:~/web/www$ sudo apt-get update Atteint http://packages.dotdeb.org wheezy Release.gpg Atteint http://ftp.fr.debian.org wheezy Release.gpg Réception de : 1 http://packages.dotdeb.org wheezy-php55 Release.gpg [836 B] Atteint http://ftp.fr.debian.org wheezy-updates Release.gpg Atteint http://packages.dotdeb.org wheezy Release Atteint http://security.debian.org wheezy/updates Release.gpg Atteint http://ftp.igh.cnrs.fr wheezy Release.gpg Atteint http://ftp.fr.debian.org wheezy Release Réception de : 2 http://packages.dotdeb.org wheezy-php55 Release [2 268 B] Atteint http://ftp.igh.cnrs.fr wheezy Release Atteint http://ftp.fr.debian.org wheezy-updates Release Atteint http://security.debian.org wheezy/updates Release Atteint http://packages.dotdeb.org wheezy/all Sources Atteint http://packages.dotdeb.org wheezy/all amd64 Packages Atteint http://ftp.fr.debian.org wheezy/main Sources Atteint http://ftp.fr.debian.org wheezy/main amd64 Packages Atteint http://ftp.igh.cnrs.fr wheezy/main Sources Réception de : 3 http://packages.dotdeb.org wheezy-php55/all Sources [1 570 B] Atteint http://ftp.fr.debian.org wheezy/main Translation-fr Atteint http://ftp.igh.cnrs.fr wheezy/main amd64 Packages Réception de : 4 http://packages.dotdeb.org wheezy-php55/all amd64 Packages [9 415 B] Atteint http://security.debian.org wheezy/updates/main Sources Atteint http://ftp.fr.debian.org wheezy/main Translation-en Atteint http://security.debian.org wheezy/updates/main amd64 Packages Atteint http://ftp.fr.debian.org wheezy-updates/main Sources Atteint http://security.debian.org wheezy/updates/main Translation-en Atteint http://ftp.fr.debian.org wheezy-updates/main amd64 Packages/DiffIndex Atteint http://ftp.fr.debian.org wheezy-updates/main Translation-en/DiffIndex Ign http://packages.dotdeb.org wheezy/all Translation-fr_FR Ign http://packages.dotdeb.org wheezy/all Translation-fr Ign http://packages.dotdeb.org wheezy/all Translation-en Ign http://packages.dotdeb.org wheezy-php55/all Translation-fr_FR Ign http://packages.dotdeb.org wheezy-php55/all Translation-fr Ign http://packages.dotdeb.org wheezy-php55/all Translation-en Ign http://ftp.igh.cnrs.fr wheezy/main Translation-fr_FR Ign http://ftp.igh.cnrs.fr wheezy/main Translation-fr Ign http://ftp.igh.cnrs.fr wheezy/main Translation-en 14,1 ko réceptionnés en 2s (6 059 o/s) Lecture des listes de paquets... Fait seboss666@heberg-new:~/web/www$ sudo apt-get upgrade Lecture des listes de paquets... Fait Construction de l'arbre des dépendances Lecture des informations d'état... Fait Les paquets suivants ont été conservés : php-pear php5-cli php5-common php5-curl php5-dev php5-fpm php5-gd php5-mcrypt php5-memcache php5-mysql 0 mis à jour, 0 nouvellement installés, 0 à enlever et 10 non mis à jour. |
Hein ? Pourquoi ? En fait, il faut savoir que la politique de Debian en matière de mises à jour de paquets est assez stricte : l’instruction update ne s’occupera de mettre à jour que les paquets déjà présents sur la machine, si leur état reste le même. Si une mise à jour demande à supprimer et/ou installer un ou plusieurs autres paquets (dépendances), alors la mise à jour ne sera pas appliquée.
Fort heureusement, il est possible de valider la mise à jour avec l’instruction dist-upgrade :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
seboss666@heberg-new:~/web/www$ sudo apt-get dist-upgrade Lecture des listes de paquets... Fait Construction de l'arbre des dépendances Lecture des informations d'état... Fait Calcul de la mise à jour... Fait Les paquets suivants seront ENLEVÉS : php-apc Les NOUVEAUX paquets suivants seront installés : autopoint debhelper gettext html2text intltool-debian libcroco3 libgettextpo0 libglib2.0-0 libglib2.0-data libmail-sendmail-perl libsys-hostname-long-perl libunistring0 libvpx1 php5-readline pkg-php-tools po-debconf shared-mime-info Les paquets suivants seront mis à jour : php-pear php5-cli php5-common php5-curl php5-dev php5-fpm php5-gd php5-mcrypt php5-memcache php5-mysql 10 mis à jour, 17 nouvellement installés, 1 à enlever et 0 non mis à jour. Il est nécessaire de prendre 16,1 Mo dans les archives. Après cette opération, 28,3 Mo d'espace disque supplémentaires seront utilisés. Souhaitez-vous continuer [O/n] ? |
Et voilà : apc sera retiré (car PHP 5.5 utilise Zend OpCache, intégré), et plusieurs nouveaux paquets sont nécessaires. J’ai validé, et l’installation s’est à priori passée sans encombres.
Alors, ça donne quoi ?
Alors, j’ai lancé dix fois le test théorique, d’abord en PHP 5.4, ensuite dix fois en PHP 5.5, et compilé les résultats dans un petit graphe :
Le gain n’est pas flagrant, mais tout de même : on constate bien une légère baisse, si on était aux alentours/proche des 5.7 secondes, on est plus près des 5.6 secondes, sur une machine sans charge.
Dans le « monde » réel, pareil, j’ai lancé dix fois le test pour chaque version de PHP, je n’ai gardé que le temps total de test, et compilé de la même façon dans un zoli graphe :
Déception. Je m’attendais à du mieux, mais mieux que ça tout de même. Maintenant, vu le peu de différences dans le test théorique, les résultats pratiques sont finalement assez logiques, sans pouvoir les expliquer.
J’aurais pu penser à une limitation de la machine virtuelle elle-même (Core 2 Duo E6600, 512Mo de RAM), ou de l’upload de ma connexion ADSL, minable (988kbps ATM d’après la page d’info de la Freebox). Jusqu’à ce que je me penche sur un autre projet…
HHVM, ou l’arme nucléaire selon Facebook
HipHop Virtual Machine de son petit nom, est une machine virtuelle d’exécution de PHP créée et rendue open-source par Facebook. De l’extérieur, on l’utilise de la même manière que PHP-FPM, Je n’aurais donc que très peu de modifications à faire pour m’en servir.
Son installation est des plus simples sous Debian, ils proposent un dépôt qui permet de disposer de la dernière version stable (ainsi que des éventuelles mises à jour de sécurité). J’ai suivi la procédure décrite sur le dépôt Github pour l’installation (en root) :
1 2 3 4 |
apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0x5a16e7281be7a449 echo deb http://dl.hhvm.com/debian wheezy main | tee /etc/apt/sources.list.d/hhvm.list apt-get update apt-get install hhvm |
Quelques instants après, il est installé. Avant de le lancer, j’édite le fichier de configuration d’hhvm, /etc/hhvm/server.ini :
1 2 3 4 5 6 7 8 9 10 11 12 |
; php options pid = /var/run/hhvm/pid ; hhvm specific hhvm.server.port = 8000 hhvm.server.type = fastcgi hhvm.server.default_document = index.php hhvm.log.use_log_file = true hhvm.log.file = /var/log/hhvm/error.log hhvm.repo.central.path = /var/run/hhvm/hhvm.hhbc |
Comme le port 9000, renseigné par défaut, est déjà utilisé par FPM, que je ne vais pas supprimer trop vite, j’utilise plutôt le port 8000. Ensuite, je modifie mon vhost pour faire pointer les appels fastcgi vers le bon port (extrait) :
1 2 3 4 5 6 7 |
location ~ \.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:8000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } |
Une fois que c’est fait, je démarre HHVM et je redémarre Nginx :
1 |
service hhvm start && service nginx restart |
J’ai ensuite relancé ma batterie de tests (bench.php, dix fois, et ab dix fois aussi). Les résultats sont édifiants, comparés à ceux de PHP 5.5 :
Une vraie claque. Alors que je pensais presque que ma VM était bridée de tous les côtés (avec Virtualbox et mon ADSL, tout est possible), HHVM m’a prouvé le contraire. J’ai beau cracher sur le réseau social lui-même et tous les problèmes sociétaux et sociaux qu’il implique, les développeurs ont vraiment fait un boulot remarquable. Pour les amateurs de pourcentages, passer de PHP 5.4 à PHP 5.5 fait gagner presque 17% de performances dans mon cas, mais basculer de PHP 5.4 à HHVM augmente de presque 100% !
Attention, HHVM n’est pas PHP
Enfin presque (titre de paragraphe putassier assumé). HHVM a d’abord été conçu pour « sur-optimiser » PHP, mais d’abord pour les besoins de Facebook. Il ne couvre donc pas l’intégralité du jeu d’instructions du langage, même si depuis l’ouverture de son code, les applications compatibles parfois sans modifications sont plus nombreuses. WordPress en fait partie depuis la version 3.9. Mais si vous pensez l’utiliser chez vous, il faudra faire des tests approfondis pour « valider » votre application dessus. J’ai pu utiliser Leed, mon lecteur de flux, ainsi que ma collection de films (dont je vous parlerais un jour, promis), sans modifications aucune. Et malgré ma connexion anémique, la copie du blog semble bien réactive.
Y’a encore plein de pistes pour gagner du temps
Je n’ai abordé que la mise à jour d’un des composants de ma « pile » d’hébergement. Sur la machine virtuelle de test j’ai déjà nginx, que je n’ai pas encore déployé sur le serveur « de prod ». J’ai aussi la possibilité d’avancer du côté de PHP 5.6, puisqu’après tout c’est la version qui sera incluse dans Jessie, pour couvrir les applications qui pourraient poser problème avec HHVM. Mais je ne compte pas non plus basculer la version de Debian « day one », histoire de laisser à d’autres les joies de la souffrance de tout casser (en particulier avec Systemd). Et PHP 7 se fait salement attendre, la version actuelle, si elle apportera son lot d’améliorations, est dans l’état toujours moins performante qu’HHVM. À moins, avec Nginx aux commandes, d’utiliser les deux : PHP 5.6 avec PHP-FPM pour les vhosts « génériques », et HHVM pour les applications « critiques », comme le blog, et peut-être l’instance Piwik.
Et tout ça sans même aborder le cas de la base de données, qui n’utilise pas encore MariaDB sur le serveur de prod. Et puis aussi, je n’ai aucune solution de cache global, comme Varnish. Encore un point que je dois creuser, avant que je devienne une star et donc qu’il ne soit trop tard ?
Hello,
Perso j’ajouterais juste une ‘tite modification à ta configuration de HHVM histoire d’utiliser les sockets Unix :
; hhvm specific
; hhvm.server.port = 8000
hhvm.server.file_socket = /var/run/hhvm/server.sock
Et dans le vHost Nginx :
# fastcgi_pass 127.0.0.1:8000;
fastcgi_pass unix:/var/run/hhvm/server.sock;
Un petit coup de restart :
service hhvm start && service nginx restart
Et maintenant ça fait une installation digne de HHVM qui ne sera pas bridée par le TCP 🙂
Ben justement, suivant la taille de la machine, tu seras mieux de passer par TCP. C’est un point que j’ai abordé dans le cadre de la bascule de mod-php à php-fpm. L’utilisation du (de la ?) socket demande un poil plus de travail pour les grosses, grosses charges, encore plus quand les ressources de la machine sont limitées.
Mais comme ça marche aussi comme ça, merci de la précision 😉
Je viens de voir ton article sur la bascule mais ma question est la suivante. Tu règle ton php-fpm sur quel mode ? Sur static tout est trop limité, sur dynamic c’est pas mal mais ça bouffe beaucoup de mémoire. Par contre le mode on-demand avec les sockets là ça déchire sur petites et grosses charges 🙂 Il faut par contre le coupler avec Nginx. Quand j’utilisait encore apache ça tournait beaucoup moins bien, je pense qu’Apache supporte moins bien la gestion des sockets unix.
À l’heure actuelle j’ai fait le plus mauvais choix d’implémentation d’FPM : je l’ai réglé en global dans Apache, avec un seul pool. Une grosse flemme en fait. Je vais devoir granulariser ça un peu avant d’aller plus loin. Pour l’instant FPM est réglé sur Dynamic, avec un max-children de 20, un start-servers à 2, et un max-spare à 3, un max-requests à 500 (j’en suis à 160 visites par jour sur le blog, moins sur le reste, donc ça se tient pour l’instant). Mais je ne suis pas encore super calé, et j’ai du mal à comprendre si c’est… Lire la suite »
C’est super cette chose qu’est HHVM !
Donc pour avoir un blog sous WordPress aux petits oignons, il faudrait ce genre de config :
> Debian 7.8 – nginx – HHVM – PHP 5.6 – MariaDB – Varnish – WP 4.1.1 (nooon sans dec) – Piwik (pour les stats, sauf si on est utilisateur de Google Analytics)
Clairement, je vais piquer l’idée et tester ça prochainement… : Reproduire l’architecture « type » d’un serveur web mutualisé contre le serveur web de roxxor que tu nous fais miroiter 🙂
Merci pour le retour ! =)
Salut 🙂
Pour moi Varnish est en trop. Nginx sais très bien faire le serveur de cache avec une intégration memcached 😉
Ah part ça c’est exactement les composants de mon infra et ça tourne niquel avec 20 wordpress dessus, HHVM pour le blog et des PHP-FPM 5.6 séparés pour les autres sites le tout avec sockets unix 🙂
Puis bon, pour contrebalancer un peu HHVM c’est pas à mettre dans toutes les mains. C’est puissant OK mais ça n’a pas 1/10ème des fonctionnalités de PHP-FPM et ça n’a donc pas la même flexibilité.
Ah mais on est tout à fait d’accord, d’ailleurs comme j’ai dit demain je montre comment corriger phpmyadmin pour le faire fonctionner avec 😀
Je serais curieux tout de même de voir les diff de perfs entre socket et TCP sur une charge moyenne.
Merci pour les infos complémentaires Nicolas 😉
Il faut réellement que je m’intéresse à ces outils… :-p
[…] https://www.howtoforge.com/tutorial/install-wordpress-nginx-mariadb-hhvm-debian-8/ https://www.abyssproject.net/2015/04/installation-de-nginx-hhvm-et-mariadb-sur-debian-8/ Et merci à Seboss666, qui m’a donnée envie de tester HHVM avec son article. […]
[…] avoir lu l’article de l’ami Seboss666, et plus récemment celui de Freddy de memo-linux, j’ai été tenté de tester HHVM pour […]
[…] avoir lu l’article de l’ami Seboss666, et plus récemment celui de Freddy de memo-linux, j’ai été tenté de tester HHVM pour […]