Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Débit et latence

À quelle vitesse va-t-il, sur quelle mesure, et face Ă  qui ? C’est votre deuxiĂšme question, et c’est celle oĂč la hype fait le plus de dĂ©gĂąts. Sur le plus grand benchmark public qui compare des frameworks web de tous langages, les entrĂ©es PHP s’étalent sur deux ordres de grandeur selon le runtime qui les fait tourner, des quarante premiĂšres places au dernier dixiĂšme. Le langage seul ne prĂ©dit rien ; le dĂ©ploiement prĂ©dit presque tout. Un Ă©cart pareil ne vous sert que si chaque chiffre vient avec son round, son matĂ©riel et son test, et ceux de ce chapitre les ont tous les trois.

L’histoire du moteur

Sur du code qui occupe le processeur, l’interprĂ©teur a fait un grand pas une fois, puis des petits pas depuis. Le grand pas, c’est PHP 7.0, en dĂ©cembre 2015, qui a remplacĂ© les structures de donnĂ©es internes du moteur. Son Ă©diteur affirmait alors que le temps d’exĂ©cution Ă©tait « souvent divisĂ© par deux » par rapport Ă  PHP 5.6, dans un livre blanc sans mĂ©thodologie publiĂ©e. Une mesure indĂ©pendante, faite sur une seule machine avec toutes les versions de l’interprĂ©teur de 5.6 Ă  une build de dĂ©veloppement de PHP 8.0, retrouve la mĂȘme forme. Le score PHPBench, une suite synthĂ©tique de micro-benchmarks de l’interprĂ©teur, passe de 288 000 sur PHP 5.6 Ă  620 000 sur PHP 7.0, puis monte par petites marches, entre rien et treize pour cent par version, jusqu’à 876 000 sur la build 8.0.

Score PHPBench par version de PHP, mĂȘme machine (plus haut est mieux) Barres horizontales, milliers de points : PHP 5.6.40 288, 7.0.33 620, 7.1.33 700, 7.2.28 772, 7.3.15 816, 7.4.3 814, 8.0-dev 876, 8.0-dev avec JIT 875 Score PHPBench par version de PHP, mĂȘme machine (plus haut est mieux) PHP 5.6.40 288 PHP 7.0.33 620 PHP 7.1.33 700 PHP 7.2.28 772 PHP 7.3.15 816 PHP 7.4.3 814 PHP 8.0-dev (fĂ©v. 2020) 876 OpenBenchmarking.org, rĂ©sultat 2002269-VE-PHPBENCHM24 par Michael Larabel, fĂ©vrier 2020, Core i9-9900KS, Ubuntu 20.04, PHPBench 0.8.1, moyenne de trois exĂ©cutions. PHPBench est un micro-benchmark CPU synthĂ©tique de l'interprĂ©teur, pas une application web. PHP 8.0 est un instantanĂ© de dĂ©veloppement de fĂ©vrier 2020, avant l'arrivĂ©e du JIT traçant.

Depuis PHP 8.0, la vitesse de l’interprĂ©teur sur une application web ne bouge plus, et ceux qui la mesurent le disent eux-mĂȘmes. Un hĂ©bergeur qui publie des benchmarks chaque annĂ©e a mesurĂ© WordPress Ă  146 requĂȘtes par seconde sur PHP 8.2 et Ă  148 sur PHP 8.5, sur la mĂȘme machine, et il Ă©crit que « les versions incrĂ©mentales produisent rarement de grands sauts de vitesse Ă  elles seules ». C’est sa propre mesure, lancĂ©e Ă  quinze requĂȘtes simultanĂ©es sur une machine de trente cƓurs ; elle dit donc le temps de rĂ©ponse, pas la capacitĂ©. Retenez ceci : une version actuelle de PHP est Ă  peu prĂšs deux fois plus rapide que PHP 5 sur le travail propre de l’interprĂ©teur, et une montĂ©e de version mineure ne vous apportera rien d’autre que des corrections et des fonctionnalitĂ©s.

Le benchmark multi-langages

Le Framework Benchmarks de TechEmpower est une suite publique qui a fait passer des centaines de frameworks web, dans des dizaines de langages, par les six mĂȘmes tests sur le mĂȘme matĂ©riel, chaque implĂ©mentation Ă©tant contribuĂ©e et entretenue par des volontaires dans un dĂ©pĂŽt public. Son dernier round achevĂ© est le Round 23, datĂ© du 24 fĂ©vrier 2025, exĂ©cutĂ© sur un serveur Ă  processeur Xeon Gold 6330, 28 cƓurs et 56 threads, avec un rĂ©seau Ă  40 gigabits. Le dĂ©pĂŽt du projet a Ă©tĂ© archivĂ© le 24 mars 2026 : le Round 23 est donc le dernier, et ses chiffres sont les derniers de leur espĂšce. Rien d’autre ne compare autant de frameworks sous un mĂȘme protocole, c’est pourquoi je les garde. Et comme les tests ne mesurent pas la mĂȘme chose, chaque chiffre nomme le sien.

Le test Fortunes est celui qui ressemble le plus Ă  une page web : une requĂȘte en base qui renvoie une douzaine de lignes, un gabarit HTML rendu avec Ă©chappement. Sur ce test, 510 entrĂ©es ont terminĂ© le Round 23. Le graphique en retient seize, les leaders toutes catĂ©gories, le framework grand public de chaque langage majeur et les entrĂ©es PHP de chaque type de runtime. Le tableau brut avec toutes les entrĂ©es est rangĂ© Ă  cĂŽtĂ© des donnĂ©es de graphiques du livre, pour que vous puissiez faire votre propre sĂ©lection.

TechEmpower Round 23, test Fortunes, milliers de requĂȘtes par seconde Barres horizontales, sĂ©lection d'entrĂ©es : may-minihttp (Rust) 1327, vertx-postgres (Java) 1041, workerman (PHP, Postgres) 743, asp.net core (C#) 446, ubiquity sur workerman (PHP, ORM complet) 428, fiber (Go) 411, spring (Java) 244, php sur PHP-FPM et nginx 146, php sur FrankenPHP 129, gin (Go) 111, fastapi (Python) 109, express (Node.js) 78, rails (Ruby) 43, django (Python) 32, symfony sur PHP-FPM 26, laravel sur PHP-FPM 16 TechEmpower Round 23, test Fortunes, milliers de requĂȘtes par seconde may-minihttp (Rust) 1 327 vertx-postgres (Java) 1 041 workerman (PHP) 743 asp.net core (C#) 446 ubiquity, workerman (PHP) 428 fiber (Go) 411 spring (Java) 244 php, PHP-FPM + nginx 146 php, FrankenPHP 129 gin (Go) 111 fastapi (Python) 109 express (Node.js) 78 rails (Ruby) 43 django (Python) 32 symfony, PHP-FPM 26 laravel, PHP-FPM 16 TechEmpower Framework Benchmarks, Round 23 (24 fĂ©vrier 2025), test Fortunes, environnement Citrine : Xeon Gold 6330 (28 cƓurs, 56 threads), 40 GbE. SĂ©lection de 16 entrĂ©es sur 510. Fortunes : une requĂȘte en base, rendu HTML avec Ă©chappement. En bleu : entrĂ©es PHP. Chaque barre nomme le framework et son runtime ; le langage seul ne dĂ©cide rien ici.

Lisez les barres PHP de haut en bas. Les entrĂ©es PHP les plus rapides, sur les runtimes workerman et Swoole, en SQL brut et sans framework, occupent les rangs 31 Ă  38 sur 510, au-dessus de 730 000 requĂȘtes par seconde, devant l’entrĂ©e ASP.NET Core de base, les entrĂ©es Go et Spring. Un framework PHP complet avec un ORM complet sur un runtime worker, l’entrĂ©e Ubiquity, atteint 428 000 au rang 92. PHP nu sous PHP-FPM et nginx, le dĂ©ploiement standard, atteint 146 000 au rang 248, environ un tiers devant Gin et FastAPI. Les deux frameworks PHP les plus utilisĂ©s, sous PHP-FPM, sont prĂšs du bas : Symfony Ă  26 000 et Laravel Ă  16 000, derriĂšre Rails Ă  43 000 et Django Ă  32 000. Un mĂȘme benchmark soutient donc Ă  la fois que PHP peut compter parmi les runtimes web les plus rapides et qu’une application PHP typique compte parmi les plus lentes. Quand on ne vous dit que l’un des deux, on vous vend quelque chose.

Une partie de l’écart tient au runtime. Une entrĂ©e qui garde l’application chargĂ©e entre deux requĂȘtes supprime le coĂ»t de dĂ©marrage, et c’est lui qui domine le temps de requĂȘte d’un framework.

MĂȘme framework, runtime diffĂ©rent (Fortunes, milliers de requĂȘtes par seconde) Barres horizontales. Symfony : PHP-FPM 26, RoadRunner 36, FrankenPHP 74, workerman 108, Swoole 111. Laravel : RoadRunner 8, PHP-FPM 16, Octane sur FrankenPHP 34, Swoole 34, workerman 50. MĂȘme framework, runtime diffĂ©rent (Fortunes, milliers de requĂȘtes par seconde) Symfony, Swoole 111 Symfony, workerman 108 Symfony, FrankenPHP 74 Symfony, RoadRunner 36 Symfony, PHP-FPM 26 Laravel, workerman 50 Laravel, Swoole 34 Laravel Octane, FrankenPHP 34 Laravel, PHP-FPM 16 Laravel, RoadRunner 8 TechEmpower Framework Benchmarks, Round 23 (24 fĂ©vrier 2025), test Fortunes. En bleu : la rĂ©fĂ©rence PHP-FPM. Les entrĂ©es sont maintenues par des volontaires ; une entrĂ©e lente peut reflĂ©ter sa configuration plutĂŽt que le runtime.

Le mĂȘme code Symfony passe de 26 000 requĂȘtes par seconde sous PHP-FPM Ă  74 000 sous FrankenPHP et 111 000 sous Swoole, et le mĂȘme code Laravel de 16 000 Ă  50 000 sous workerman. Le reste de l’écart tient Ă  l’entrĂ©e elle-mĂȘme. Chez TechEmpower, une entrĂ©e est entretenue par qui veut bien s’en occuper, et une entrĂ©e lente peut reflĂ©ter une configuration datĂ©e autant que le framework. L’entrĂ©e Laravel sous RoadRunner, Ă  8 000, est plus lente que sous PHP-FPM ; je n’ai pas d’explication et je la rapporte telle qu’elle a Ă©tĂ© mesurĂ©e.

Les autres tests dĂ©placent les rangs, pas l’histoire. Sur le test de sĂ©rialisation JSON, qui n’a pas de base de donnĂ©es, l’entrĂ©e Swoole atteint 2,5 millions de requĂȘtes par seconde au rang 73, PHP-FPM nu 428 000 et Laravel sous PHP-FPM 27 000, tandis qu’ASP.NET Core est Ă  1,4 million, Node.js Ă  1,1 million, Spring Ă  328 000 et Django Ă  167 000. Sur le test Ă  vingt requĂȘtes SQL, que le pilote de base de donnĂ©es bride, les leaders de tous les langages se rejoignent sous 90 000, et les entrĂ©es PHP les plus rapides sont Ă  56 000, devant FastAPI Ă  37 000 et Spring Ă  32 000.

La latence

Le dĂ©bit dit combien de requĂȘtes une machine peut absorber. La latence dit combien de temps un utilisateur attend, et ce qui la domine, c’est ce que la requĂȘte attend, pas l’interprĂ©teur. Tideways, un Ă©diteur de profileur et membre fondateur de la PHP Foundation, a mesurĂ© un hello-world PHP-FPM derriĂšre nginx sur une machine virtuelle Ă  huit cƓurs, et relevĂ© un 99e centile de 0,9 milliseconde Ă  18 000 requĂȘtes par seconde. C’est le plancher : l’interprĂ©teur et le gestionnaire de processus coĂ»tent ensemble moins d’une milliseconde quand il n’y a rien d’autre Ă  faire, et tout ce qui vient au-dessus, c’est l’application et ses dĂ©pendances.

Les chiffres de latence publics les plus utiles viennent d’une organisation qui publie ses propres objectifs et ses propres tableaux de bord. Les rĂšgles d’ingĂ©nierie de Wikimedia exigent qu’une requĂȘte GET s’achĂšve en 50 millisecondes Ă  la mĂ©diane et en 200 millisecondes au 99e centile du temps passĂ© dans PHP, et en 500 millisecondes au 99e centile pour un POST. Son instance Grafana est publique et montre la distribution mesurĂ©e pour les requĂȘtes qui atteignent les serveurs applicatifs, c’est-Ă -dire les dĂ©fauts de cache et les utilisateurs connectĂ©s, autrement dit les requĂȘtes chĂšres. Le jour oĂč j’ai vĂ©rifiĂ©, la mĂ©diane Ă©tait d’environ 170 millisecondes et la queue de distribution bien au-dessus de l’objectif, pour des pages qui incluent le rendu du wikitexte depuis un cache d’analyse froid. Je vous donne le tableau de bord plutĂŽt que l’instantanĂ©, parce qu’une semaine vous en dira plus qu’une heure.

LĂ  oĂč PHP perd

Sur du calcul pur, sans entrĂ©es-sorties, PHP est un interprĂ©teur avec un JIT en option, et il est un ordre de grandeur plus lent qu’un runtime compilĂ© Ă  la volĂ©e comme V8. Le Computer Language Benchmarks Game, qui compare des programmes contribuĂ©s sur les mĂȘmes petites tĂąches, place le programme n-body mono-thread le plus rapide Ă  2,2 secondes en Rust, 3,1 en C#, 6,0 en Java, 6,4 en Go, 8,6 en Node.js, 167 en Ruby avec YJIT, 204 en PHP 8.4 et 360 en Python 3.13.

Calcul pur : n-body, secondes Ă©coulĂ©es (plus bas est mieux) Barres horizontales, programme mono-thread le plus rapide par langage : Rust 2,2 secondes, C# 3,1, Java 6,0, Go 6,4, Node.js 8,6, Ruby 166,7, PHP 204,1, Python 360,0 Calcul pur : n-body, secondes Ă©coulĂ©es (plus bas est mieux) Rust 2,2 s C# 3,1 s Java 6 s Go 6,4 s Node.js 8,6 s Ruby 166,7 s PHP 204,1 s Python 360 s The Computer Language Benchmarks Game, n-body, entrĂ©e la plus rapide par langage, mesurĂ©e sur un Intel i5-3330 ; PHP 8.4.1, Python 3.13, Ruby 3.4 avec YJIT, Node.js 23.8. RelevĂ© le 17 septembre 2026. Programmes contribuĂ©s, un cƓur chacun ; les entrĂ©es C# et Java sont compilĂ©es Ă  l'avance. La ligne de commande PHP fixe un tampon JIT mais pas opcache.jit, que PHP 8.4 dĂ©sactive par dĂ©faut.

Le graphique a ses rĂ©serves. Les programmes PHP tournent avec un tampon JIT configurĂ© mais opcache.jit laissĂ© Ă  sa valeur par dĂ©faut de PHP 8.4, c’est-Ă -dire dĂ©sactivé : ils mesurent donc l’interprĂ©teur seul. La RFC du JIT rapporte un gain de quatre fois sur Mandelbrot une fois le JIT activĂ©, ce qui resserrerait l’écart sans le combler. Les programmes sont aussi contribuĂ©s, donc ils mesurent le meilleur programme que quelqu’un a pris la peine d’écrire. Avec ces deux rĂ©serves, l’ordre tient. Pour du calcul numĂ©rique, de la simulation, l’analyse de gros fichiers en boucle ou tout ce qui occupe un cƓur, PHP est dans la classe de Python et de Ruby, pas dans celle de Node.js, et trente Ă  cent fois derriĂšre Go, Java, C# et Rust sur ce programme. LĂ  oĂč PHP est le mauvais choix en tire la consĂ©quence.

La limite : les bons chiffres de dĂ©bit de PHP viennent des runtimes worker et de l’accĂšs brut Ă  la base, ce qu’une Ă©quipe ordinaire ne fait pas tourner dĂšs le premier jour. Ses chiffres typiques, un framework complet sous PHP-FPM, sont ceux de Rails et de Django, quelques dizaines de milliers de requĂȘtes par seconde sur une grosse machine, ce qui reste plus que ce que la plupart des applications recevront jamais. Et sur du calcul pur, l’interprĂ©teur est un ordre de grandeur derriĂšre V8.

Ce que vous pouvez vĂ©rifier vous-mĂȘme

L’outillage TechEmpower tourne encore depuis son dĂ©pĂŽt archivĂ©, avec Docker, et les tableaux bruts du Round 23 pour six tests sont rangĂ©s en CSV dans le dossier charts/raw/ du livre : vous pouvez vĂ©rifier ma sĂ©lection et en faire une autre. Plus prĂšs de chez vous, prenez l’application que vous construiriez, ou un Ă©quivalent open source, faites-la tourner sous PHP-FPM puis sous un runtime worker sur la mĂȘme machine, et mesurez avec wrk Ă  la concurrence que vous attendez en production. Le rapport que vous obtiendrez est le seul qui compte. Pour la latence, ouvrez le tableau de bord public « Backend Pageview Timing » de Wikimedia et lisez-en une semaine.

Le dĂ©bit suppose des requĂȘtes indĂ©pendantes. Que se passe-t-il quand elles ne le sont pas ? C’est la question suivante.