Mes notes du ForumPHP 2015, jour 1
13 janvier 2016 —Le ForumPHP organisé par l’AFUP a cette année eu lieu les 23 et 24 novembre, au Beffroi de Montrouge à Paris.
Ces deux jours ont encore une fois été l’occasion de revoir plein de monde, de découvrir de nouveaux visages, d’associer des têtes à des pseudos twitter, … Et, bien sûr, d’assister à de nombreuses conférences !
J’ai eu la chance d’être retenu cette année pour présenter une conférence sur les flux et pour animer une table ronde qui a permis de répondre à de nombreuses questions en toute fin d’événement.
J’ai tenté de prendre quelques notes lors de chaque conférence à laquelle j’ai assisté. Voici donc ci-dessous, dans l’ordre chronologique, celles du premier jour. Notez qu’il s’agit de mes notes, soumises à mon interprétation de chaque conférence.
Les slides d’une partie des conférences sont disponibles en ligne (sur joind.in) et les vidéos captées lors de l’événement sont diffusées par l’AFUP sur youtube.
Keynote d’ouverture
Pour ouvrir ce Forum PHP, Maxime, président de l’AFUP, a rappelé que PHP 7 devait sortir bientôt et que cette année était marqué à la fois par les 20 ans de PHP et par les 15 ans de l’AFUP.
C’est donc l’occasion de faire un Forum PHP plus grand que d’habitude, avec plus de lieux, plus de place, plus de visiteurs, plus de conférences et plus de conférenciers !
Symfony 3.0 est sorti !
Nicolas GREKAS – Résumé, Slides, Vidéo
Symfony 3 devrait sortir d’ici une semaine, pour la SymfonyCon ; avec une approche orientée composants, le framework étant une couche au-dessus. Symfony, c’est une méthodologie et une approche partagées par les 46 composants synchronisés, sur un seul repository monolithe.
Le but est de publier une nouvelle version tous les six mois, avec promesse de compatibilité antérieure. Mise en place de guidelines pour arriver à quelque chose de plus strict que semver.
En fait, sf3, ça n’apporte rien de nouveau : les fonctionnalités, ça arrive sur les versions mineures et pas sur les versions majeures (qui sont celles qui peuvent casser la compatibilité, par contre). Finalement, sf3, c’est globalement sf2.8 moins ce qui était déprécié en 2.x (ça mène à 10% de lignes de code en moins) – tout en prenant soin de garder la compatibilité autant que possible.
Les nouveautés de sf3 portent beaucoup sur les processus et les méthodes :
-
Chemin de migration continue : objectif de facilitation de migrations 2.x → 2.x+1 → 3.0
-
Avant 2.7 : il fallait lire la documentation, il n’y avait pas d’outil pour migrer, à part les
@deprecated
-
En 2.7 : trigger d’erreurs
E_DEPRECATED
à l’exécution (ça a demandé de convaincre les développeurs / utilisateurs ; pas toujours facile !)- Il faut donc corriger !
- Outil pour collecter les deprecated et les présenter à la fin des tests
- sf utilisait des trucs deprecated de sf… Il a donc fallu mettre à jour symfony pour qu’il soit compatible avec lui-même
- Autre outil pour déprécier ce qui n’est pas de l’appel ou de l’exécution de code (interfaces, constantes)
- Services dépréciés : modification de l’injecteur de dépendances pour qu’il sache avertir
- Analyse statique de code pour détecter les deprecated.
Dans la durée :
- Intégration continue : contraintes sur la matrice de tests de sf (tester un composant en 2.x avec d’autres en 3.0 et inversement)
--prefer-lowest-stable
et--prefer-stable
de composer pour vérifier la validité des lowest et que tout marche ensemble- Tests sur les nouvelles interfaces (il faut 0 erreur deprecated) et sur les anciennes (là, il faut des deprecated)
- Tests sur HHVM, PHP 5.3/4/5/6 et PHP 7.0.
- Tests également sous Windows.
Travail aussi au niveau de l’écosystème :
- PHPUnit bridge
- Bundle par bundle, pousser les auteurs à updater (et/ou les aider)
- Twig : ajout de trigger d’erreurs.
Mon avis : globalement pas de bc-break en passant de 2.x à 3.0, voila une bonne nouvelle. Suppression de quelques trucs obsolètes depuis un moment, bon, c’est la vie dans nos métiers.
Les flux : méconnus et sous-utilisés
Pascal MARTIN – Résumé, Slides, Vidéo
Je suis souvent surpris de voir que les flux, qui sont un composant central de PHP depuis sa version 4.3, sont mal connus et en conséquence moins souvent utilisés qu’ils ne le mériteraient.
Mon objectif pour cette conférence courte était de donner quelques exemples de trucs faisables à travers la couche de flux de PHP, afin d’attirer l’oeil du public et donner envie d’en apprendre plus par la suite.
J’espère avoir atteint cet objectif ! Si vous êtes curieux, je vous encourage à parcourir mes slides en activant les notes du présentateur, où j’ai essayé de résumer ce que j’ai dit à l’oral.
Mon avis : je visais, par cette présentation courte, à montrer que les flux permettent de faire des choses et à donner envie au public de fouiller plus en profondeur par la suite. Plusieurs personnes sont venues me voir par la suite avec de bons retours ; j’espère de tout coeur avoir atteint mon objectif !
The PHP7 Story
Zeev, qui est rentré sur le projet PHP au moment de PHP 3 en compagnie d’Andi Gutmans, a commencé par rappeler que c’est un projet qui a démarré petit – malgré une anecdote où le réceptionniste de son hôtel pas du tout informatisé à Buenos Aires, lors de sa lune de miel de 2008, a reconnu son nom comme étant le “PHP guy”, qui fait plus bizarre que toute statistique !
Une partie importante de cette présentation a permis de passer en revue les évolutions majeures de chaque version de PHP depuis presque 20 ans ; ci-dessous, quelques notes en résumé.
PHP 3.0, juin 1998
- Syntaxe qu’on retrouve aujourd’hui
- Mécanisme d’extensions : la grande force de PHP, qui est extensible sans qu’on n’ait à se pencher sur son coeur ni savoir comment le parser fonctionne !
- Performances : jusqu’à présent, PHP lisait le code-source lors de l’exécution, y compris à chaque parcours des boucles ! Introduction du Zend Memory Manager.
PHP 4.0, mai 2000
- Modularité : principe de SAPI + moteur plus modulaire qui permet de brancher des fonctionnalités comme du cache d’opcode ou des extensions de debugging
- Compatibilité : la migration 3 → 4 se fait sans trop de mal (contrairement à 2 → 3)
- Performances : arrivée du Zend Engine, avec notamment un compilateur qui converti le code-source en opcodes (en mémoire) qui sont ensuite exécutées.
- Orientation web : module de sessions
PHP 5.0, juillet 2004
- Fonctionnalités : “vrai” modèle objet proche de Java (casse la compatibilité avec PHP 4 ; mais permet de mettre en place des vraies applications), avec destructeurs, exceptions, …
- Performances : soyons content que ça ne soit pas plus lent ! Graines plantées pour le futur : grosse amélioration entre 5.0 et 5.1, puis pas mal entre 5.2 et 5.3 puis entre 5.3 et 5.4. Mais plus tellement pour 5.5 ni 5.6 : un plateau a été atteint.
PHP 6
-
Idée : PHP 5.x + Unicode (à tous les niveaux)
-
Andrei Lead sur PHP 6 ; gros boulot !
-
Projet de plusieurs années, mais :
- De moins en moins de contributions, y compris de la communauté
- Plus lent que PHP 5 (50%) ; et mémoire consommée doublée
-
11 mars 2010 : mort de PHP 6
PHP 7
-
Perfs de PHP 5 en plateau depuis un moment ⇒ il faut des changements plus profonds pour arriver à un bon résultat
-
JIT ?
- Première chose recherchée, à partir de 2012
- Moteur beaucou plus rapide sur des benchs simplistes (1.15 secondes → 0.21 secondes)
- Mais les utilisateurs ne font pas ça en prod…
- 2014 : en fait, pas de différence pour des applications réelles ;-(. Des mois de travail pour pas de progrès. Il ne faut donc pas continuer sur le chemin de ce POC.
- Raison possible : trop d’accès à la RAM, plus lente que le CPU.
-
Nouvel objectif (janvier 2014) : réduire la consommation mémoire en modifiant les structures de données ; et sans rien casser.
-
Mars 2014 : fait tourner wordpress et
bench.php
. Pas parfait sur un bench synthétique, mais 30% de gain sur Wordpress (= une application réelle) -
Mai 2014 : projet public ⇒ merge vers
master
en août.
Ce qu’on peut attendre de PHP 7
-
Rapidité
- jusqu’à x2 sur des applications réelles (oui, avec accès DB et tout)
- Un peu + ou un peu - rapide que HHVM selon les cas ; globalement égal ⇒ les perfs ne sont plus une raison pour passer à HHVM et arrêter d’utiliser php.net
- Perfs continuent à s’améliorer, en temps et en nombre d’instructions CPU.
-
Nouveautés : gestion d’erreurs, déclarations de types scalaraires, types de retours, assertions à coût nul, …
Mon avis : je connaissais déjà plutôt bien l’histoire de PHP et n’ai donc pas appris grand chose lors de cette présentation. Je pense toutefois que ces quelques rappels auront permis au public de comprendre l’évolution de PHP et pourquoi / comment il a évolué pendant ces dernières 20 années.
One extension, three engines
Derick Rethans – Résumé, Vidéo
Conférence un peu plus bas niveau ensuite, avec Derick, qui bosse chez MongoDB et a notamment développé Xdebug.
Pour commencer, l’extension qui permet de communiquer avec MongoDB depuis PHP 5 a pas mal de dette, résultat de vieux choix (du code écrit au début de PHP 5, qui a évolué de manière un peu décousue), et des incohérences d’APIs. Objectifs donc pour une nouvelle version : supporter plusieurs moteurs (PHP 5.4+, HHVM 3.9+ avec du C++ et du OCAML, PHP 7 avec beaucoup de changements internes par rapport à PHP 5).
Nouvelle architecture :
- libbson : utilisée par libmongoc et par les drivers
- libmongoc : client en C, utilisé par les drivers PHP et HHVM
- Phongo : extension pour PHP, minimaliste (uniquement query et command), qui sera utilisée par une bibliothèque écrite en PHP.
- Hippo : même chose que Phongo, mais pour HHVM (écrire une extension pour HHVM est relativement simple : une partie écrite en PHP/Hack – avec compilation en code quasi-natif – et une partie en C++)
- phplib : couche en espace utilisateur, au-dessus de Hippo/Phongo. Installation avec composer. C’est cette brique qu’on utilisera, nous, depuis notre code PHP.
Quelques notes en vrac :
- Passer des paramères : HHVM = 1 ligne. PHP = 30 lignes avec
zend_parse_parameters()
- PHP 7 : beaucoup de changements par rapport à PHP 5 (+3000 -1000 lignes) : ordre des éléments dans les structures,
char *
vszend_string
, changement sur les hash. Choix d’attendre la sortie de PHP 7.0 stable avant de merger et tester. - phplib : pas toutà fait compatible avec l’existant, mais API plus propre et plus claire.
- HHVM n’est plus aussi important qu’à une époque. En particulier parce que la vitesse est globalement identique sous PHP 7.
Au rang des problèmes rencontrés :
-
API non figées, specs écrites trop tard
-
Ecrire des extensions :
- HHVM : pas de doc sur plein d’API
- HHVM a des “problèmes” : constantes, exceptions, difficile à débugger (PHP est plus facile, là-dessus.)
Le travail avance et la nouvelle version devrait sortir dans pas trop longtemps. Il reste principalement à releaser et à rédiger de la documentation et des tutoriels.
Mon avis : ayant moi-même écrit un livre sur le développement d’extensions PHP, le sujet m’intéressait beaucoup. J’en suis sorti avec l’idée que développer en plusieurs couches comme cela a été fait ici (une bibliothèque bas niveau commune à tous les moteurs, une extension minimaliste spécifique à chaque moteur, et le gros du code en espace utilisateur) était fort intéressante et pouvait être adaptée à pas mal de cas !
Performance Testing for Modern Apps
Dustin Whittle – Résumé, Vidéo
En introduction, Dustin a commencé par rappeler quelques chiffres bien connus : 100ms de réduction du temps de chargement des pages chez Amazon = 1% de CA en plus. Et 2.2 secondes en moins pour Mozilla = 50 million de téléchargements de Firefox en plus.
Grande question : “How fast is ‘fast enough’ ?”
- 0.1 seconde = semble instantané
- 1 seconde = à peine acceptable, limite pour l’utilisateur
- 10 secondes = utilisateur perdu !
- 3 secondes = 40% des utilisateurs abandonnent le site !
Et rappel : ce qui compte, c’est la perception que les utilisateurs ont du site.
Une fois qu’on connait les perfs de base de nos serveurs (page statique, page PHP simple, application, … pour déterminer où est ce qui prend du temps), on peut bencher. Plusieurs outils :
- Apache bench : 1 seule machine, 1 seule URL (load test d’un seul endpoint), mais permet de voir quand ça augmente en temps vs augmentation de la concurrence
- Siege : un peu plus flexible (liste d’URLs, notamment)
- Bees with machine guns : boot d’instances EC2 pour attaquer depuis beaucoup de machines
- locust.io : interface en CLI et interface Web : scénarios complexes, écrits sous forme de code python.
- beaucoup d’autres : gatling.io, wrk, tsung, … Ce qui compte, c’est de tester les perfs ; pas l’outil.
Arrivé là, on sait combien de temps on passe côté serveur (mettons 100-500 ms), mais combien côté client ? Encore une fois, ce qui compte, c’est la perception utilisateur. Voir Google Page Speed, avec API JSON pour intégrer à la CI et recommandations + module côté serveur pour modifier les pages à la volée.
Penser à automatiser : tests de perfs en intégration continue ! Trop souvent, on réalise qu’il y a un problème parce qu’un utilisateur a appelé le support… Alors qu’on a déjà perdu de l’argent ! Donc : instrumenter, suivre les performances, en développement et en production.
Quelques outils, encore :
- webpagetest.org : idée rapide. Permet de choisir le navigateur, plusieurs réseaux, du réseau dégradé, … Rapport détaillé, y compris côté client.
- sitespeed.io : run à chaque deploiement, test de toutes les pages publiques, détail sur les perfs côté serveur et côté client.
- blitz.io : load testing d’un endpoint facilement
- blazemeter
- il existe plein d’autres outils d’APM, commerciaux ou open-source ; en utilisant un existant ou hébergé en externe peut être moins cher que d’en développer / héberger un nous-même ;-)
Au-delà des performances, tester les échecs et problèmes : “Test for failures”. Cf netflix et simian army + chaos monkey (qui tue des instances au hasard). Typiquement : que se passe-t-il si le cache tombe ? Ou si des dépendances ralentissent ?
En résumé, best practices :
- Perf as feature !
- Capacity plan + load testing
- Optimiser + tester, y compris côté client
- Savoir d’où on part, instrumenter, monitorer les perfs (dev + prod), mesurer les différences de chaque modification
- Automatiser les tests de performances, comprendre comment les échecs impactent les perfs.
Mon avis : je connaissais déjà l’idée de “Perf as a feature” et elle résonne très fort en moi. Je suis sorti de cette conférence avec une liste d’outils à l’air fort sympa ; dont certains que je vois déjà comment utiliser sur certains projets ;-). L’approche automatisez les tests de perfs m’est plus nouvelle, mais je suis séduis !
Libérez vous de votre client HTTP avec PSR-7 et httplug
Joël a eu la difficile tâche de remplacer un conférencier dont la conférence a été annulée presque de dernière minute et est venu nous parler de PSR-7.
Pour commencer, il a rappelé que PHP permet plusieurs approches, lorsqu’il s’agit de requêter une API (comme twitter, facebook, docker, nos propres API, …) : stream socket, curl, guzzle, … Autrement dit, lorsque l’on souhaite créer un client d’API, il faut commencer par choisir de quoi dépendre ! En effet, chaque bibliothèque a ses contraintes (guzzle ne fait pas de socket unix, par exemple) et si on utilise deux bibliothèques, elles peuvent avoir des conflits de dépendances. Et dupliquer, avoir plusieurs bibliothèques qui font du HTTP dans le même projet, ça ajoute du boulot (pour ajouter des logs, par exemple) et de la charge de maintenance.
PSR-7 apporte des nouvelles interfaces Psr\Http\Message\{ RequestInterface, ResponseInterface, StreamInterface }
. A terme, espérons que ce soit supporté par de plus en plus de bibliothèques clientes. Le but est de permettre de créer des requêtes et manipuler des réponses, sans avoir à se soucier de comment elles sont envoyées : c’est à la bibliothèque de gérer ça !
httplug (successeur de ivory-http-adapter) définit un contrat pour l’envoi de requêtes HTTP, avec une interface pour envoyer une requête / recevoir une réponse et une seconde interface asynchrone (qui retourne une Promise/A+ avec callbacks onFulfilled
et onRejected
), même si l’asynchrone en PHP ce n’est pas encore tout à fait ça (encore que, avec ReactPHP…). Au besoin, il y a une méthode wait()
pour forcer l’attente de résolution de la promesse.
Utilisation avec composer, via un virtual package et plein d’implémentations (Guzzle 5/6, React, Zend, …) + client (socket, curl, …). Pour créer des requêtes, deux implémentations aujourd’hui (Guzzle/psr-7 et Zend/Diactoros). Bibliothèque pour créer des requêtes, indépendamment de l’adapter, avec php-adapter/discovery pour l’instancier automatiquement en fonction des dépendances disponibles.
Et aussi : middleware pour ajouter des fonctionnalités (en async-first) comme authentification (couche OAuth, headers HTTP, …), content-length, cookies, decoder (chunked, gzip), error, log, retry, …
Mon avis : les premières minutes de cette présentation étaient fort intéressantes, avec l’idée de faire abstraction du client HTTP utilisé et d’écrire du code fonctionnant aussi bien avec l’un que l’autre. Par contre, sur la seconde moitié, malgré quelques notions sympa, j’ai le sentiment (déjà évoqué ici ou là plusieurs fois ces derniers mois) qu’on a vraiment du mal à trouver un juste milieu, en PHP, entre pas assez architecturé et complètement over-engineeré par rapport aux besoins réels de nos applications…
Portable PHP
Une conférence plutôt orientée implémentation interne de PHP, ensuite, par Anatol, Release Manager de PHP 7.0, autour de points en rapport avec l’interopérabilité. Interopérabilité, ça signifie différents : Hardware / architecture / système d’exploitation / programme ; avec cross-platform correspondant aux trois premiers niveaux.
Aux rangs des différences entre Unix et Windows, notamment :
- Compilateur : gcc / MS Visual C++
- tout un ensemble de bibliothèques / code API et bibliothèques OSS
- approche processus / approche thread
- C99 / C99 pour VC12 – mais PHP est en C89
- ext, reiserfs, btrfs, … / ntfs, fat
- permissions Unix / ACLs
Exemple des API en rapport avec le temps : fragiles, compliquées. Présence de bugs mais pas fixables par PHP. Sources matérielles variées (CPU, gestion d’énergie, Timers HPET, …) et l’OS en choisit une parmi d’autres (alors qu’il peut y avoir des bugs dans un peu toutes !). Pour PHP : utilisation de gettimeofday()
, native sous Linux et plus ou moins custom sous Windows.
Pour ce qui est des performances :
- Plein de pistes : 36 caches différents, recki-ct, QB, …
- Ecrire du code correct et bon ;-)
- Pour le compilateur, on peut utiliser PGO (sous VC++) ou LTO/FDO (sous gcc) pour compiler PHP spécialement pour une application (1er build instrumenté, puis exécuter PHP sur les cas d’usage, puis recompiler PHP en mode optimisé à partir des cas d’usage) ⇒ un binaire PHP spécifique à une application ; mais avec un brin de perfs en plus ;-)
64-bits (Anatol a pas mal bossé dessus) : cohérence entre toutes les plate-formes. Plus de mémoire peut être adressée (plus que ce qui existe aujourd’hui, en fait ^^). Chaînes peuvent être de la longueur qu’on veut. Même si un peu plus lent en général.
Mon avis : ce type de problématiques bas-niveau est tout particulièrement intéressant quand on veut savoir comment ça marche en-dessous du PHP qu’on utilise tous les jours. Deux regrets : la présentation a été écourtée (problèmes d’installation de PC/projection) et public vraiment trop réduit (je regrette que nous ne soyons, dans la communauté, pas plus nombreux à nous intéresser à comment ça marche, en somme).
Zoom sur les objets PHP
Au tour de Julien, RM sur PHP 5.5 et co-RM pour PHP 5.6, d’enchainer avec une présentation, également orientée assez bas-niveau sur les objets. En se concentrant, pour répondre à des questions ou à des articles qu’on trouve ici ou là sur Internet, sur les objets en PHP 5 (pas mal de choses ont changé pour PHP 7).
Pour commencer, la zval
:
- Représentation interne d’une variable, supporte le type-juggling, puisqu’une variable PHP peut contenir différents types. Cf
zval_struct
etzval_value
- Pas mal de macros pour les manipuler.
- Contient un compteur de références. Libération automatique quand
refcount=0
. Quand une zval est libérée, son contenu est libéré s’il n’est pas utilisé ailleurs.
Objets PHP :
zval
de typeZ_OBJECT
; utilisation de l’élément.obj
, de typezend_object_value
handle
= entier qui référence l’objet dans la table interne ; l’object store. Affiché parvar_dump()
;-)- Les objets ne sont pas des références : ce qui est dans la documentation est faux (mais c’est plus simple que d’expliquer…). Si on passe un objet à une fonction, elle ne peut pas le transformer en entier (ou en un autre type). Par contre, elle peut changer son contenu (comme modifier la valeur d’une propriété). PHP passe par le handle et c’est le même objet qui est référencé en interne. En somme, il faut différencier la variable de l’objet : on peut voir 36
zval
qui pointent en fait sur le même objet ! - En schématisant : table de symboles → zval store → object store
- Les seules opérations qui peuvent créer un objet dans le store sont :
new
,clone
etunserialize()
Garbage collector :
- Détection des références cycliques. Et rien que de ça !
- Travaille sur les
zval
⇒ fonctionne sur les tableaux, par exemple ; et pas que sur les objets. - Se déclenche automatiquement. Ou à la demande.
- Beaucoup de rérérences circulaires dans les frameworks (sf2, doctrine, …). Impact si objets lourds et/ou pour les processus qui vivent longtemps (comme des commandes/démons en CLI)
Plongée dans les classes et objets :
- Une classe consomme beaucoup de mémoire (1KB pour une classe vide !) ⇒ bien utiliser un autoloader !
- Une classe est lourde (mais partagée entre tous ses objets enfants), alors qu’un objet peut être léger (ça dépend du poids de ses attributs).
Les object handlers :
- Chaque handler permet une opération sur un objet. Il y en a tout un paquet !
- Permettent d’implémenter des objets à comportements spéciaux (les classes correspondantes redéfinissent les gestionnaires correspondant) :
SimpleXML
,PDOStatement
,DateTime
, … - Il y a en interne des interfaces qui reviennent à jouer sur des handlers :
ArrayAccess
,Serializable
,Countable
, …
Quelques gotchas pour terminer :
-
Si une classe n’a pas de constructeur, la VM n’exécute pas les paramètres (y compris s’ils comprennent des expressions !) passés lors de l’instanciation avec
new
-
Le destructeur est appelé quand l’objet est détruit, mais, par défaut, PHP essaye de les supprimer dans un ordre qui marche à peu près :
- PHP itère sur sur les objets en commençant par celui créé le plus récemment et remontant vers le plus ancien, pour supprimer ceux qui ont un un
refcount=1
- Puis parcours en avançant dans le store d’objets, en supprimant tout, dans l’ordre de création.
- PHP itère sur sur les objets en commençant par celui créé le plus récemment et remontant vers le plus ancien, pour supprimer ceux qui ont un un
-
Si un destructeur utilise
exit
oudie
ou part en Fatal Error ⇒ les destructeurs suivants ne sont pas appelés -
La destruction est distincte de la libération mémoire, que PHP gère en interne
-
En somme, si on veut maitriser les appels de destructeurs, il faut détruire nous-même les objets. Et ne pas mettre de code critique dans un destructeur. Sinon, tant pis.
-
Attention à la sérialisation d’une exception : cela essaye de sérialiser sa stack-trace, qui peut ne pas être sérialisable, en fonction de son contenu !
-
Veiller à déclarer les classes dans leur ordre d’héritage, pour éviter des surprises étranges.
Mon avis : j’ai pas mal fouillé sur le fonctionnement des objets lorsque j’ai écrit le chapitre correspondant de mon bouquin, du coup ça faisait un résumé intéressant (et, là, j’étais content de voir autant de monde assister à une présentation orientée interne !). Dommage que la présentation ait été axée PHP 5 et pas PHP 7, cela dit (là, j’aurais appris plus de choses ;-) ).
PHP 7 – What changed internally?
Nikita Popov – Résumé, Slides, Vidéo
PHP 7, on l’a déjà vu plusieurs fois, va apporter (très bientôt) un ensemble conséquent de nouvelles fonctionnalités et ou nouveau souffle niveau performances. Mais d’où est-ce que ces améliorations de performances sortent ?
Optimisation mémoire :
- Les I/O, c’est lent ; même si la RAM est plus rapide que le réseau ou la DB, la RAM ça reste plus lent que le CPU.
- Les allocations mémoire, c’est coûteux ⇒ objectif de réduction des allocations (le Memory Manager pouvait consommer, en PHP 5, jusqu’à 20% du temps CPU correspondant à l’exécution d’un script ! Ca tourne plus aux environs de 5% avec PHP 7)
- Réduire l’utilisation mémoire (en termes de volume). Des fois, améliorer les perfs se fait en consommant plus de mémoire (pour du cache, par exemple) ; mais limiter la consommation mémoire et les échanges de données RAM ↔ CPU aider aussi ! Rappels : cache L1 = 32 KB = 1ns ; cache L2 = 254 KB = 4ns ; cache L3 = quelques MB = 12 ns ; RAM = plein de GB, mais 100ns.
- Réduire les indirections (variable qui contient l’adresse d’un bloc mémoire ; il faut aller le lire ; il contient l’adresse d’un autre bloc mémoire ; qu’il faut aller lire ; pour avoir l’adresse de la vraie donnée à charger depuis la RAM)
La structure zval
, utilisée pour toutes les variables de PHP
-
En PHP 5, contient type + valeur et
refcount
. -
Mais copier une valeur simple (un entier par exemple) est plus rapide / plus simple que d’incrémenter / décrémenter un compteur de références
-
Structure plus simple pour les types simples (entier, flottants, booléens) ⇒ refcount uniquement pour les types compliqués, désormais.
-
Mais structure également simplifiée pour les types complexes.
-
Exemple des chaines de caractères :
- PHP 5 : pointeur + longueur.
- PHP 7 :
zend_string
avec ajout d’un hash qui évite d’avoir à le recalculer à la volée.
-
Hashtable (utilisée pour tous les tableaux associatifs ; et à vraiment plein d’endroits en interne)
- Au lieu d’avoir des buckets un par un en mémoire, on a maintenant un tableau qui contient tous les buckets ; et on utilise des indexes et plus des pointeurs (1 entier = 32 bits, alors qu'1 pointeur = 64 bits ; donc économie)
- Bilan : grosse réduction de la consommation mémoire pour les tableaux ; et moins d’indirection.
-
Tableaux immuables, notamment quand
opcache
est activé ⇒ déplacement vers mémoire partagée entre tous les scripts et donc diminution mémoire consommée au total. -
Objets : même chose que pour les tableaux.
La compilation de scripts PHP passe désormais par un AST – Abstract Syntax Tree
- Pas fait pour les perfs (même si ça peut aider), mais permet de ne plus se restreindre à cause d’un vieux compilateur
- Processus : lexing → parsing (là, y’a un AST maintenant) + compilation
- Voir sur github :
nikic/php-ast
Au niveau de la VM, également, des améliorations
- Gestion de la pile lors des appels de fonctions : en PHP 5, les paramètres étaient présents deux fois sur la stack. En PHP 7, ils ne sont plus dupliqués (et donc, optimisation mémoire ; et quelques indirections de mois). Petit impact sur l’exécution du code PHP, des fonctions comme
func_get_args()
peuvent retourner quelque chose de différent. - Inlined internal function : quelques fonctions (
strlen()
par exemple) sont désormais des opcodes, qui peuvent calculer directement. Attention : ne marche que pour des appels pleinement qualifiés ! - Utilisation de registres globaux : deux données (
execute_data
etopline
) sont désormais stockées dans des registres ⇒ plus besoin de les recharger / sauver / restaurer. 2% de gain sur architectures Intel. Jusqu’à 20% de gain sur certaines autres architectures !
Mon avis : Nikita a fait du super boulot ces dernières années et sa présentation allait dans le même sens : très claire, fort bien illustrée, très intéressante et fort instructive ! Je vous conseille vivement de visionner la vidéo, si vous n’étiez pas présent au Forum !
Lightning-talks
Pour finir cette première journée de forum, une succession de sept lightning-talks de 5 minutes chacun, sur vim, les classes anonymes, …
Mon avis : Frédéric m’a épaté avec les trucs absoluments dégueulasses qu’il a montré autour des classes anonymes ; j’adore !
Bref, en résumé : une première journée de conférences fort intéressantes dans l’ensemble ; qui m’a aussi été l’occasion de revoir et de discuter avec plein de monde que je ne croise que trop rarement1 !
Mes notes du second jour de ce Forum PHP 2015 seront publiées dans quelques jours. N’hésitez pas à vous abonner au flux RSS et/ou à me suivre, @pascal_martin pour être tenu au courant !
-
C’est simple, sur quelque chose comme une bonne heure et demie de pause déjeuner, je n’ai réussi à naviguer que sur la moitié d’un des deux espaces de rencontre ! ↩︎