Le contenu de cet article est une introduction (images et textes) sur les mises à jour de PHP7 et l'optimisation des performances. J'espère qu'il sera utile aux amis dans le besoin. vous avez aidé.
Innovation PHP7 et optimisation des performances
J'ai eu la chance de participer au PHP Technology Summit 2015 (PHPCON) et d'écouter frère Niao (Hui Xinchen) parler des nouvelles fonctionnalités de PHP7 Et le partage des optimisations de performances est passionnant. Frère Niao est l'expert PHP le plus réputé en Chine. Son partage contient de nombreuses choses très précieuses. J'ai compilé le PPT partagé et collecté des informations pertinentes dans cet article technique interprétatif. J'espère qu'il pourra être utile aux étudiants qui développent PHP. .
PHP a traversé 20 ans d'histoire. Jusqu'à aujourd'hui, PHP7 a publié une version RC On dit que la version officielle de PHP7 devrait sortir vers novembre 2015. PHP7 peut être considéré comme une innovation à grande échelle par rapport à la série précédente de PHP5.*, notamment en termes de performances, qui a réalisé une amélioration significative à pas de géant.
PHP est un langage de développement web largement utilisé dans le monde. L'innovation de PHP7 apportera certainement des changements plus profonds à ces services web. Voici un graphique du PPT de Niao Ge (82 % des sites Web utilisent PHP comme langage de développement) :
(Remarque : un site Web peut utiliser plusieurs langues lors de son développement langue)
(Remarque : cet article contient de nombreuses captures d'écran du PPT de frère Niao. Les droits d'auteur des images appartiennent à frère Niao)
Jetons d'abord un coup d'œil à deux performances passionnantes Tableau des résultats des tests :
Comparaison de référence (image de PPT) :
Résultats des tests de performances PHP7, résultats des tests de résistance aux performances, consommation de temps passée de 2,991 à 1,186, une baisse significative de 60%.
Test de résistance WordPress QPS (photo de PPT) :
Dans le projet WordPress, PHP7 par rapport à PHP5.6, le QPS a augmenté de 2,77 fois.
Après avoir lu la comparaison passionnante des résultats des tests de performance, entrons dans le vif du sujet. Il existe de nombreuses nouveautés dans PHP7, mais nous nous concentrerons davantage sur les changements majeurs.
1. Nouvelles fonctionnalités et modifications
1. Déclarations de type scalaire et déclarations de type scalaire
Langage PHP Une fonctionnalité très importante est la "typage faible" , ce qui rend les programmes PHP très faciles à écrire, et les novices peuvent se lancer rapidement lorsqu'ils entrent en contact avec PHP. Cependant, cela s'accompagne également d'une certaine controverse. La prise en charge de la définition de types de variables peut être considérée comme un changement innovant. PHP commence à prendre en charge les définitions de types de manière facultative. De plus, une instruction switch declare(strict_type=1); Une fois cette instruction activée, elle forcera le programme sous le fichier actuel à suivre les types de transfert de paramètres de fonction stricts et les types de retour.
Par exemple, une fonction d'ajout plus une définition de type peuvent être écrites comme ceci :
Si elles sont combinées avec l'instruction de commutation de type obligatoire, elles peuvent être écrites comme ceci :
Si strict_type n'est pas activé, PHP essaiera de vous aider à le convertir au type requis. Après l'avoir activé, il changera PHP et non plus. effectuer une conversion de type. Si le type ne correspond pas, une erreur de correspondance de type sera générée. C’est une excellente nouvelle pour les étudiants qui aiment les langues « fortement typées ».
Introduction plus détaillée :
Déclaration de type scalaire PHP7 RFC [Traduction]
2. Plus d'erreurs deviennent des exceptions capturables
PHP7 implémente une interface globale jetable. L'exception d'origine et certaines erreurs implémentent cette interface (interface) et définissent la structure d'héritage des exceptions sous forme d'interfaces. En conséquence, davantage d'erreurs dans PHP7 deviennent des exceptions capturables et sont renvoyées aux développeurs. Si elles ne sont pas détectées, ce sont des erreurs. Si elles sont détectées, elles deviennent des exceptions qui peuvent être gérées dans le programme. Ces erreurs captables sont généralement des erreurs qui ne causeront pas de dommages mortels au programme, comme une fonction qui n'existe pas. PHP7 facilite davantage le traitement des développeurs et leur donne un plus grand contrôle sur le programme. Parce que par défaut, l'erreur provoquera directement l'interruption du programme, et PHP7 offre la possibilité de la capturer et de la traiter, permettant au programme de continuer à s'exécuter, offrant ainsi aux programmeurs des choix plus flexibles.
Par exemple, pour exécuter une fonction dont nous ne sommes pas sûrs qu'elle existe ou non, la méthode compatible PHP5 consiste à ajouter le jugement function_exist avant que la fonction ne soit appelée, tandis que PHP7 prend en charge la méthode de capture d'exception.
L'exemple dans l'image ci-dessous (la capture d'écran provient du PPT) :
3.AST (Arbre de syntaxe abstraite, Arbre de syntaxe abstraite)
AST En tant que middleware dans le processus de compilation PHP, il remplace la méthode originale consistant à cracher l'opcode directement à partir de l'interpréteur et dissocie l'interpréteur (analyseur) et le compilateur (compilateur), ce qui peut réduire certains codes de piratage et rendre l'implémentation plus facile à comprendre. .et maintenable. PHP5 :PHP7 : Plus d'informations sur l'AST : https://wiki.php.net/rfc/abstract_syntax_tree4. TLS natif (stockage local de thread natif, stockage local de thread natif)PHP en mode multi-thread (par exemple, Les modes woker et événementiel du serveur Web Apache sont multithread) et doivent résoudre le problème de la « sécurité des threads » (TS, Thread Safe). Parce que les threads partagent l'espace mémoire du processus, chaque thread lui-même doit en transmettre. Créez un espace privé pour sauvegarder vos propres données privées afin d'éviter toute contamination mutuelle avec d'autres threads. La méthode adoptée par PHP5 consiste à maintenir un grand tableau global et à allouer un espace de stockage indépendant à chaque thread. Les threads accèdent à ce groupe de données global via leurs propres valeurs de clé. En PHP5, cette valeur de clé unique doit être transmise à chaque fonction qui doit utiliser des variables globales. PHP7 estime que cette méthode de transmission n'est pas conviviale et présente quelques problèmes. Par conséquent, essayez d’utiliser une variable globale spécifique au thread pour enregistrer cette valeur clé. Problèmes liés à TLS natif : https://wiki.php.net/rfc/native-tlsAutres nouvelles fonctionnalités Là. Il existe de nombreuses nouvelles fonctionnalités et modifications dans PHP7, nous n'entrerons donc pas dans les détails ici. (1) Prise en charge d'Int64, unifiant les longueurs entières sous différentes plates-formes, prenant en charge les chaînes et les téléchargements de fichiers supérieurs à 2 Go. (2) Syntaxe de variable uniforme. (3) Comportements foreach cohérents (4) Nouveaux opérateurs <=>, ?? (5) Prise en charge du format de caractères Unicode (u{xxxxx}) (6) Prise en charge de la classe anonyme (Classe anonyme) … … 2. Un bond en avant en matière de performances : à toute vitesse 1. et performancesJust In Time (compilation juste à temps) est une technologie d'optimisation logicielle qui compile le bytecode en code machine uniquement lorsqu'il est en cours d'exécution. D'un point de vue intuitif, il nous est facile de penser que le code machine peut être directement reconnu et exécuté par les ordinateurs, et qu'il est plus efficace que Zend de lire les opcodes et de les exécuter un par un. Parmi eux, HHVM (HipHop Virtual Machine, HHVM est une machine virtuelle PHP open source de Facebook) utilise JIT, ce qui améliore leur test de performances PHP d'un ordre de grandeur et publie un résultat de test choquant, ce qui nous fait également penser intuitivement que JIT est un technologie puissante qui transforme la pierre en or. En fait, en 2013, frère Niao et Dmitry (l'un des développeurs du noyau du langage PHP) ont fait une fois une tentative JIT sur la version PHP5.5 (elle n'a pas été publiée). Le processus d'exécution original de PHP5.5 consiste à compiler le code PHP en bytecode d'opcode via une analyse lexicale et syntaxique (le format est quelque peu similaire à l'assemblage). Ensuite, le moteur Zend lit ces instructions d'opcode, les analyse et les exécute une par une. Et ils ont introduit l'inférence de type (TypeInf) après le lien opcode, puis ont généré des ByteCodes via JIT avant l'exécution. En conséquence, des résultats passionnants ont été obtenus dans le benchmark (programme de test). Après la mise en œuvre de JIT, les performances ont augmenté de 8 fois par rapport à PHP5.5. Cependant, lorsqu’ils ont intégré cette optimisation dans le projet WordPress lui-même (un projet de blog open source), ils n’ont constaté presque aucune amélioration des performances et ont obtenu un résultat de test déroutant. Ainsi, ils ont utilisé l'outil de type de profil sous Linux pour analyser la consommation de temps CPU de l'exécution du programme. Répartition de la consommation CPU lors de l'exécution de WordPress 100 fois (capture d'écran de PPT) : Remarque : 21 % du temps CPU est consacré à la gestion de la mémoire. 12 % du temps CPU est consacré aux opérations de table de hachage, principalement l'ajout, la suppression, la modification et la vérification des tableaux PHP.
30 % du temps CPU est consacré aux fonctions intégrées, telles que strlen.
25% du temps CPU est passé dans la VM (Zend Engine).
Après analyse, deux conclusions ont été tirées :
(1) Si les ByteCodes générés par JIT sont trop volumineux, cela entraînera une diminution du taux de réussite du cache CPU (CPU Cache Miss)
Dans le code PHP5.5, comme il n'y a pas de définition de type évidente, nous ne pouvons nous fier qu'à l'inférence de type. Définissez autant que possible les types de variables qui peuvent être déduits, puis, combinés à l'inférence de type, supprimez les codes de branche qui ne sont pas de ce type pour générer du code machine directement exécutable. Cependant, l’inférence de type ne peut pas déduire tous les types. Dans WordPress, moins de 30 % des informations de type pouvant être déduites sont limitées, et le code de branche pouvant être réduit est limité. En conséquence, après JIT, le code machine est directement généré et les ByteCodes générés sont trop volumineux, ce qui entraîne finalement une diminution significative des accès au cache CPU (CPU Cache Miss).
Un accès au cache du processeur signifie que pendant le processus de lecture et d'exécution des instructions du processeur, si les données requises ne peuvent pas être lues dans le cache de premier niveau (L1) du processeur, celui-ci doit continuer la recherche vers le bas. cache de deuxième niveau (L2) et le cache de troisième niveau (L3), il tentera éventuellement de trouver les données d'instruction requises dans la zone mémoire, et la différence de temps de lecture entre la mémoire et le cache CPU peut atteindre 100 fois. Par conséquent, si les ByteCodes sont trop volumineux et que le nombre d'instructions exécutées est trop important, le cache multi-niveaux ne peut pas accueillir autant de données et certaines instructions devront être stockées dans la zone mémoire.
Les tailles des caches à tous les niveaux du processeur sont également limitées. L'image suivante représente les informations de configuration de l'Intel i7 920 :
.
Par conséquent, la diminution du taux de réussite du cache CPU entraînera une augmentation importante de la consommation de temps. D'un autre côté, l'amélioration des performances apportée par JIT en sera également compensée.
Grâce au JIT, la surcharge de la VM peut être réduite dans le même temps, grâce à l'optimisation des instructions, le développement de la gestion de la mémoire peut être indirectement réduit car le nombre d'allocations de mémoire peut être réduit. Cependant, pour les vrais projets WordPress, seulement 25 % du temps CPU est consacré à la VM, et le principal problème et goulot d’étranglement ne réside pas réellement dans la VM. Par conséquent, le plan d’optimisation JIT n’a pas été inclus dans les fonctionnalités PHP7 de cette version. Cependant, il est probable qu’il soit implémenté dans une version ultérieure, ce qui mérite d’être attendu.
(2) L'effet d'amélioration des performances JIT dépend du goulot d'étranglement réel du projet
JIT a été considérablement amélioré dans le benchmark car la quantité de code est relativement faible et les ByteCodes finaux générés sont également relativement petits et la principale surcharge se situe dans la VM. Cependant, il n’y a pas d’amélioration évidente des performances dans le projet WordPress actuel, car le volume de code de WordPress est beaucoup plus important que celui du benchmark. Bien que JIT réduise la surcharge de la VM, il entraîne une diminution des accès au cache du processeur et de la mémoire supplémentaire. Les ByteCodes sont trop volumineux, au final, il n'y a aucune amélioration.
Différents types de projets auront des ratios de surcharge CPU différents et obtiendront également des résultats différents. Les tests de performances sans projets réels ne sont pas très représentatifs.
2. Changements dans Zval
En fait, le véritable support de stockage de divers types de variables en PHP est Zval, qui se caractérise par sa tolérance et sa tolérance. Essentiellement, il s'agit d'une structure (struct) implémentée en langage C. Pour les étudiants qui écrivent du PHP, vous pouvez le comprendre comme quelque chose de similaire à un tableau.
Zval de PHP5, la mémoire occupe 24 octets (capture d'écran de PPT) :
Zval de PHP7, la mémoire occupe 16 octets (capture d'écran de PPT) :
Zval est passé de 24 octets à 16 octets. Pourquoi ai-je besoin d'ajouter un peu de base du langage C ici pour aider ceux qui ne sont pas familiers avec C. les élèves comprennent. Il existe une légère différence entre struct et union (union). Chaque variable membre de Struct occupe un espace mémoire indépendant, tandis que les variables membres de union partagent un espace mémoire (c'est-à-dire que si l'une des variables membres est modifiée, la l'espace public sera Après modification, il n'y aura aucun enregistrement d'autres variables membres). Par conséquent, bien qu’il semble y avoir beaucoup plus de variables membres, l’espace mémoire réellement occupé a diminué.
De plus, il y a évidemment des fonctionnalités modifiées, certains types simples n'utilisent plus de références.
Schéma de structure Zval (à partir de PPT) :
Zval dans l'image est composé de deux 64 bits (1 octet = 8 bits, le bit est "bit") Si le type de variable est long ou bealoon, qui ne dépasse pas 64 bits de longueur, il sera stocké directement dans la valeur. , et aucune suite ne sera citée. Lorsque le type de variable est un tableau, un objet, une chaîne, etc. qui dépasse 64 bits, la valeur stockée est un pointeur pointant vers l'adresse réelle de la structure de stockage.
Pour les types de variables simples, le stockage Zval devient très simple et efficace.
Types qui ne nécessitent pas de références : NULL, Boolean, Long, Double
Types qui nécessitent des références : String, Array, Object, Resource, Reference
Type interne. zend_string
Zend_string est la structure qui stocke réellement les chaînes. Le contenu réel sera stocké dans val (char, type de caractère), et val est un tableau de caractères d'une longueur de 1 (pratique pour l'occupation des variables membres).
La dernière variable membre de la structure utilise un tableau char au lieu de char* Voici une petite astuce d'optimisation qui peut réduire les échecs de cache CPU.
Si vous utilisez un tableau de caractères, lorsque malloc s'applique à la mémoire de la structure ci-dessus, il est appliqué dans la même zone. Habituellement, la longueur est sizeof(_zend_string) + l'espace de stockage de caractères réel. Cependant, si vous utilisez char*, ce qui est stocké à cet emplacement n'est qu'un pointeur et le stockage réel se trouve dans une autre zone mémoire indépendante.
Comparaison de l'allocation de mémoire en utilisant char[1] et char* :
D'un point de vue d'implémentation logique, il n'y a en fait pas beaucoup de différence entre le deux. L'effet est très similaire. En fait, lorsque ces blocs mémoire sont chargés dans le CPU, ils apparaissent très différents. Étant donné que le premier est le même morceau de mémoire alloué en continu ensemble, il peut généralement être obtenu ensemble lorsque le processeur le lit (car il se trouvera dans le même niveau de cache). Ce dernier, parce qu'il contient des données de deux mémoires, lorsque le CPU lit la première mémoire, il est très probable que les données de la deuxième mémoire ne soient pas dans le même niveau de cache, le CPU doit donc chercher en dessous de L2 (cache secondaire), ou même jusqu'à La deuxième donnée mémoire souhaitée se trouve dans la zone mémoire. Cela entraînera un échec du cache du processeur et la différence de temps entre les deux peut atteindre 100 fois.
De plus, lors de la copie de chaînes, en utilisant l'affectation de référence, zend_string peut éviter les copies de mémoire.
6. Modifications des tableaux PHP (HashTable et Zend Array)
Dans le processus d'écriture de programmes PHP, le type le plus fréquemment utilisé est celui des tableaux, et les tableaux PHP5 sont implémentés à l'aide de HashTable. Pour résumer, il s'agit d'une table de hachage qui prend en charge les listes doublement liées. Elle prend non seulement en charge le mappage de hachage pour accéder aux éléments via des clés de tableau, mais peut également parcourir les éléments du tableau en accédant aux listes doublement liées via foreach.
PHP5 HashTable (capture d'écran de PPT) :
Cette image a l'air très compliquée, avec divers pointeurs qui sautent lorsque nous passons la clé lors de l'accès au contenu. d'un élément par valeur, il faut parfois trois sauts de pointeur pour trouver le contenu requis. Le point le plus important est que le stockage de ces éléments du tableau est dispersé dans différentes zones mémoire. De la même manière, lorsque le CPU lit, parce qu'ils ne sont probablement pas dans le cache de même niveau, le CPU devra rechercher dans le cache de niveau inférieur ou même dans la zone mémoire, ce qui entraînera une diminution de l'accès au cache du CPU, ce qui augmentant plus la consommation d'heure.
Zend Array de PHP7 (capture d'écran de PPT) :
La nouvelle version de la structure du tableau est très simple et accrocheuse. La plus grande caractéristique est que l'ensemble des éléments du tableau et la table de mappage de hachage sont tous connectés ensemble et alloués dans la même mémoire. Si vous parcourez un tableau d'entiers de type simple, l'efficacité sera très rapide, car les éléments du tableau (Bucket) eux-mêmes sont continuellement alloués dans la même mémoire, et le zval des éléments du tableau stockera les éléments entiers en interne et non Il existe également un lien externe de pointeur et toutes les données sont stockées dans la zone de mémoire actuelle. Bien sûr, la chose la plus importante est que cela peut éviter les échecs de cache du processeur (diminution du taux de réussite du cache du processeur).
Modifications du tableau Zend :
(1) La valeur par défaut du tableau est zval.
(2) La taille de HashTable est passée de 72 à 56 octets, soit une réduction de 22%.
(3) La taille des Buckets est passée de 72 à 32 octets, soit une réduction de 50 %.
(4) L'espace mémoire des buckets d'éléments du tableau est alloué ensemble.
(5) La clé de l'élément du tableau (Bucket.key) pointe vers zend_string.
(6) La valeur de l'élément du tableau est intégrée dans le Bucket.
(7) Réduisez les échecs de cache du processeur.
7. Convention d'appel de fonction
PHP7 a amélioré le mécanisme d'appel de fonction en optimisant le processus de transfert de paramètres, il a réduit certaines instructions et amélioré l'efficacité d'exécution.
Mécanisme d'appel de fonction PHP5 (capture d'écran de PPT) :
Dans l'image, les instructions send_val et les paramètres recv dans la pile vm sont les mêmes, PHP7 réalise l'optimisation sous-jacente du mécanisme d'appel de fonction en réduisant ces deux duplications.
Mécanisme d'appel de fonction de PHP7 (capture d'écran de PPT) :
8. Grâce à la définition de macro et à la fonction en ligne (inline), permettant le compilateur pour terminer une partie du travail à l'avance
Les définitions de macros du langage C seront exécutées dans la phase de prétraitement (phase de compilation), complétant une partie du travail à l'avance, sans allouer de mémoire lorsque le programme est en cours d'exécution, et peut implémenter des fonctions de fonctions similaires, mais sans la surcharge de poussée de pile et d'appels de fonction, l'efficacité sera relativement élevée. La même chose est vraie pour les fonctions en ligne. Lors de la phase de prétraitement, les fonctions du programme sont remplacées par des corps de fonction. Lorsque le programme en cours d'exécution est exécuté ici, il n'y aura pas de surcharge d'appels de fonction.
PHP7 a apporté de nombreuses optimisations dans ce domaine et a mis beaucoup de travail qui doit être effectué lors de la phase d'exécution dans la phase de compilation. Par exemple, le jugement du type de paramètre (Parameters Parsing), étant donné que tous les éléments impliqués ici sont des constantes de caractères fixes, peut être effectué lors de l'étape de compilation, améliorant ainsi l'efficacité de l'exécution ultérieure.
Par exemple, dans la figure ci-dessous, la manière de gérer le type de paramètres passés est optimisée de la méthode d'écriture à gauche à la méthode d'écriture de macro à droite.
3 Résumé
Le PPT de Niao Ge a publié un ensemble de données comparatives, selon lesquelles WordPress s'exécute 100 fois en PHP5. .6 Cela générera 7 milliards d’exécutions d’instructions CPU, alors qu’en PHP7 cela ne nécessite que 2,5 milliards de fois, soit une réduction de 64,2 %. C’est une donnée choquante.
Dans tout le partage de Brother Bird, le point le plus profond qui m'a été donné est : faire attention aux détails, à de nombreuses petites optimisations, accumuler petit à petit, additionner et finalement converger vers des résultats étonnants. Je pense que c'est probablement la même raison de construire une montagne avec neuf personnes.
Il ne fait aucun doute que PHP7 a réalisé des progrès considérables en termes de performances. Si ces résultats peuvent être appliqués au système Web de PHP, nous n'aurons peut-être besoin que de moins de machines pour prendre en charge un volume de requêtes plus élevé. La sortie de la version officielle de PHP7 est pleine d'attentes infinies.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!