
Quant aux micro-optimisations PHP, en remplaçant les guillemets doubles par des guillemets simples, tant de copies sont cassées qu'il est assez problématique de faire un nouveau flux. Mais je vais essayer.
Dans cet article, il n'y aura qu'un seul point de référence, où serait-il sans lui, et l'accent principal est d'analyser comment il est organisé à l'intérieur.
Clause de non-responsabilité
- Tout ce qui est décrit ci-dessous permet, pour la plupart, d'économiser sur les nanosecondes, et en pratique, il ne fera que perdre le temps perdu sur une telle microoptimisation. Cela est particulièrement vrai pour les "optimisations" du temps de compilation.
- Je vais couper le code et sortir au maximum, ne laissant que l'essentiel.
- Lors de la rédaction d'un article, j'ai utilisé PHP 7.2
Introduction nécessaire
Une chaîne entre guillemets doubles
au stade de la compilation est traitée légèrement différemment d'une chaîne entre guillemets simples.
Les guillemets simples seront analysés comme suit:
statement -> expr -> scalar -> dereferencable_scalar -> T_CONSTANT_ENCAPSED_STRING
Double donc:
statement -> expr -> scalar -> '"' encaps_list '"' -> , ,
Dans les articles sur les microoptimisations PHP, il est très souvent conseillé de ne pas utiliser l'
impression , car elle est plus lente que l'
écho . Voyons comment ils sont triés.
Analyse de l'
écho :
statement -> T_ECHO echo_expr_list -> echo_expr_list -> echo_expr -> expr
Analyse de l'analyse:
statement -> expr -> T_PRINT expr -> expr ( )
C'est-à-dire en général, oui, l'
écho est détecté une étape plus tôt et cette étape, il faut le noter, est assez difficile.
Afin de ne pas accentuer à nouveau l'attention lors de l'article, nous garderons à l'esprit qu'au stade de la compilation, les guillemets doubles perdent le single et l'
impression perd l'
écho . N'oublions pas non plus que dans le pire des cas, il s'agit de nanosecondes.
Eh bien, pour ne pas se lever deux fois. Voici les fonctions diff compilant
impression et
écho :
1 - void zend_compile_print(znode *result, zend_ast *ast) 1 + void zend_compile_echo(zend_ast *ast) 2 2 { 3 3 zend_op *opline; 4 4 zend_ast *expr_ast = ast->child[0]; 5 5 6 6 znode expr_node; 7 7 zend_compile_expr(&expr_node, expr_ast); 8 8 9 9 opline = zend_emit_op(NULL, ZEND_ECHO, &expr_node, NULL); 10 - opline->extended_value = 1; 11 - 12 - result->op_type = IS_CONST; 13 - ZVAL_LONG(&result->u.constant, 1); 10 + opline->extended_value = 0; 14 11 }
Eh bien, vous comprenez - ils sont identiques dans leur fonctionnalité, mais
print retourne en outre une constante égale à 1. Je pense que sur ce sujet avec
print, vous pouvez fermer et l'oublier pour toujours.
Ligne simple, sans fioritures
Les chaînes
echo 'Some string';
et en
echo "Some string";
sera divisé presque à l'identique en 2 jetons (clause de non-responsabilité P2).
T_ECHO: echo T_ENCAPSED_AND_WHITESPACE/T_CONSTANT_ENCAPSED_STRING: "Some string"
De plus, pour les guillemets simples, il y aura toujours T_CONSTANT_ENCAPSED_STRING, et pour les guillemets doubles, quand c'est comme. S'il y a un espace dans la ligne, alors T_ENCAPSED_AND_WHITESPACE.
Les opcodes seront simples à déshonorer et absolument identiques:
line
Conclusions
Si vous souhaitez enregistrer quelques cycles de processeur au stade de la compilation, utilisez des guillemets simples pour les chaînes constantes.
Ligne dynamique
Il y a 4 options.
echo "Hello $name! Have a nice day!"; echo 'Hello '.$name.'! Have a nice day!'; echo 'Hello ', $name, '! Have a nice day!'; printf ('Hello %s! Have a nice day!', $name);
Pour la première option:
T_ECHO: echo T_ENCAPSED_AND_WHITESPACE: Hello T_VARIABLE: $name T_ENCAPSED_AND_WHITESPACE: ! Have a nice day!
Pour le second (pour le troisième aussi, seulement au lieu de points il y aura des virgules):
T_ECHO: echo T_CONSTANT_ENCAPSED_STRING: 'Hello ' string: . T_VARIABLE: $name string: . T_CONSTANT_ENCAPSED_STRING: '! Have a nice day!'
Pour le quatrième:
T_STRING: printf T_CONSTANT_ENCAPSED_STRING: 'Hello %s! Have a nice day!' string: , T_VARIABLE: $name
Mais avec les opcodes, tout sera beaucoup plus amusant.
Premier:
echo "Hello $name! Have a nice day!"; line
Deuxièmement:
echo 'Hello '.$name.'! Have a nice day!'; line
Troisièmement:
echo 'Hello ', $name, '! Have a nice day!'; line
Quatrièmement:
printf ('Hello %s! Have a nice day!', $name); line
Le bon sens nous dit que l'option avec `printf` perdra de la vitesse par les trois premiers (d'autant plus qu'à la fin il y a toujours le même ECHO), donc nous le laisserons pour les tâches où le formatage est nécessaire et nous ne nous en souviendrons plus dans cet article.
Il semblerait que la troisième option soit la plus rapide - pour imprimer trois lignes successivement sans enchaînements, étranges CORDES et la création de variables supplémentaires. Mais pas si simple. La fonction d'impression en PHP n'est certainement pas Rocket Science, mais ce n'est en aucun cas un fputs C-shny
banal .
Peu importe - la balle se défait en commençant par
php_output_write dans le
fichier main / output.c .
CONCAT. Tout est simple ici - nous convertissons, si nécessaire, les arguments en chaînes et créons un nouveau
zend_string en utilisant la
memcpy rapide. Le seul point négatif est qu'avec une longue chaîne de concaténations pour chaque opération, de nouvelles lignes seront créées en déplaçant les mêmes octets d'un endroit à l'autre.
Mais avec ROPE_INIT, ROPE_ADD et ROPE_END, tout est beaucoup plus intéressant. Suivez les mains:
- ROPE_INIT (ext = 3, return = ~ 3, operands = 'Hello +')
Nous allouons la «corde» à partir de trois emplacements (ext), mettons la chaîne «Bonjour +» (opérandes) dans l'emplacement 0 et renvoyons la variable temporaire ~ 3 (retour) contenant la «corde». - ROPE_ADD (ext = 1, return = ~ 3, operands = ~ 3,! 0)
Nous mettons dans la fente 1 (ext) de la «corde» ~ 3 (opérandes) la chaîne «Vasya» obtenue à partir de la variable! 0 (opérandes) et retournons la «corde» ~ 3 (retour). - ROPE_END (ext = 2, return = ~ 2, operands = ~ 3, '% 21 + Have + a + nice + day% 21')
Nous mettons la ligne '% 21 + Have + a + nice + day% 21' (opérandes) dans le slot 2 (ext), après quoi nous créons un zend_string de la taille requise et copions à tour de rôle tous les slots "corde" avec le même memcpy .
Il convient de noter séparément que dans le cas des constantes et des variables temporaires, les liens vers les données seront placés dans les emplacements et il n'y aura pas de copie inutile.
À mon avis, assez élégant. :)
Faisons référence. En tant que données source, nous prenons le fichier
zend_vm_execute.h (à
mon humble avis, cela sera vrai) pour 71 mille lignes et l'imprimons de 100 façons pour 100 passes, en supprimant le minimum et le maximum (chaque mesure a commencé 10 fois, en choisissant l'option la plus courante):
<?php $file = explode("\n", file_get_contents("C:\projects\C\php-src\Zend\zend_vm_execute.h")); $out = []; for ($c = 0; $c < 100; $c++) { $start = microtime(true); ob_start(); $i = 0; foreach ($file as $line) { $i++;
Que mesurons-nous | Temps moyen en secondes |
---|
"Corde" | 0,0129 |
Plusieurs ECHO | 0,0135 |
Concaténation | 0,0158 |
printf , pour l'exhaustivité | 0,0245 |
Conclusions
- Pour les chaînes avec substitution simple, tout d'un coup, les guillemets doubles sont plus optimaux que les guillemets simples avec concaténation. Et plus les lignes sont utilisées, plus le gain est important.
- Arguments séparés par des virgules ... Il existe de nombreuses nuances. Par mesure, elle est plus rapide que la concaténation et plus lente que la «corde», mais il y a trop de «variables» associées à l'entrée / sortie.
Conclusion
Il m'est difficile de trouver une situation où le besoin de telles micro-optimisations peut survenir. Lors du choix de telle ou telle approche, il est plus raisonnable de se laisser guider par d'autres principes - par exemple, la lisibilité du code ou le style de codage adopté par votre entreprise.
Quant à moi personnellement, je n'aime pas l'approche de concaténation à cause du look vyrviglazny, bien que dans certains cas cela puisse être justifié.
PS Si ce type d'analyse est intéressant - faites-le moi savoir - il y en a bien plus qui est loin d'être toujours univoque et évident: un tableau d'objets VS, foreach VS tandis que VS for, votre option ... :)
Une petite explication de la lecture des commentaires
La syntaxe HEREDOC et les «chaînes complexes» (où les variables sont entre accolades à l'intérieur) sont les mêmes chaînes entre guillemets doubles et compilent exactement de la même manière.
Un mélange de PHP avec HTML comme ceci:
<?php $name = 'Vasya';?>Hello <?=$name?>! Have a nice day!
Ce n'est que 3
échos d'affilée.