PHP Bezauberndes Punktieren über Anführungszeichen


Bei PHP-Mikrooptimierungen werden durch Ersetzen von doppelten Anführungszeichen durch einfache Anführungszeichen so viele Kopien beschädigt, dass es ziemlich problematisch ist, einen neuen Stream zu erstellen. Aber ich werde es versuchen.

In diesem Artikel wird es nur einen Benchmark geben, wo er ohne wäre, und der Schwerpunkt liegt auf der Analyse, wie er im Inneren angeordnet ist.

Haftungsausschluss


  1. Alles, was unten beschrieben wird, spart größtenteils Nanosekunden, und in der Praxis wird es nichts anderes tun, als die Zeit zu verlieren, die bei einer solchen Mikrooptimierung verloren geht. Dies gilt insbesondere für "Optimierungen" der Kompilierungszeit.
  2. Ich werde den Code schneiden und maximal ausgeben, wobei nur die Essenz übrig bleibt.
  3. Beim Schreiben eines Artikels habe ich PHP 7.2 verwendet

Notwendige Einführung


Eine Zeichenfolge in doppelten Anführungszeichen wird in der Kompilierungsphase geringfügig anders verarbeitet als eine Zeichenfolge in einfachen Anführungszeichen.

Einfache Anführungszeichen werden wie folgt analysiert:

statement -> expr -> scalar -> dereferencable_scalar -> T_CONSTANT_ENCAPSED_STRING 

Verdoppeln Sie also:

 statement -> expr -> scalar -> '"' encaps_list '"' ->        ,  ,     

In Artikeln über PHP-Mikrooptimierungen wird sehr oft empfohlen, den Druck nicht zu verwenden, da er langsamer als das Echo ist . Mal sehen, wie sie sortiert sind.

Parsing- Echo :

 statement -> T_ECHO echo_expr_list -> echo_expr_list ->  echo_expr -> expr 

Analysedruck:

 statement -> expr -> T_PRINT expr -> expr ( ) 

Das heißt, Im Allgemeinen wird das Echo einen Schritt früher erkannt, und dieser Schritt ist, wie zu beachten ist, ziemlich schwierig.

Um die Aufmerksamkeit während des Artikels nicht noch einmal zu betonen, werden wir bedenken, dass in der Kompilierungsphase doppelte Anführungszeichen einfache Anführungszeichen verlieren und der Druck das Echo verliert. Vergessen wir auch nicht, dass es im schlimmsten Fall um Nanosekunden geht.

Nun, um nicht zweimal aufzustehen. Hier sind die Diff-Funktionen, die Print und Echo kompilieren:

 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 } 

Nun, Sie verstehen - sie sind in der Funktionalität identisch, aber print gibt zusätzlich eine Konstante gleich 1 zurück. Ich denke, bei diesem Thema mit print können Sie es für immer schließen und vergessen.

Einfache Linie, ohne Schnickschnack


Strings echo 'Some string'; und echo "Some string"; wird fast identisch in 2 (Haftungsausschluss P2) Token aufgeteilt.

 T_ECHO: echo T_ENCAPSED_AND_WHITESPACE/T_CONSTANT_ENCAPSED_STRING: "Some string" 

Darüber hinaus gibt es für einfache Anführungszeichen immer T_CONSTANT_ENCAPSED_STRING und für doppelte Anführungszeichen, wenn es so ist. Wenn in der Zeile ein Leerzeichen vorhanden ist, wird T_ENCAPSED_AND_WHITESPACE verwendet.

Die Opcodes sind einfach zu blamieren und absolut identisch:

 line #* EIO op fetch ext return operands ----------------------------------------------------------- 4 0 E > ECHO 'Some string' 


Schlussfolgerungen


Wenn Sie in der Kompilierungsphase einige Prozessorzyklen speichern möchten, verwenden Sie für konstante Zeichenfolgen einfache Anführungszeichen.

Dynamische Linie


Es gibt 4 Optionen.

 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); 

Für die erste Option:

 T_ECHO: echo T_ENCAPSED_AND_WHITESPACE: Hello T_VARIABLE: $name T_ENCAPSED_AND_WHITESPACE: ! Have a nice day! 

Für die zweite (auch für die dritte gibt es nur anstelle von Punkten Kommas):

 T_ECHO: echo T_CONSTANT_ENCAPSED_STRING: 'Hello ' string: . T_VARIABLE: $name string: . T_CONSTANT_ENCAPSED_STRING: '! Have a nice day!' 

Zum vierten:

 T_STRING: printf T_CONSTANT_ENCAPSED_STRING: 'Hello %s! Have a nice day!' string: , T_VARIABLE: $name 

Aber mit Opcodes wird alles viel unterhaltsamer.

Erster:

 echo "Hello $name! Have a nice day!"; line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 ROPE_INIT 3 ~3 'Hello+' 2 ROPE_ADD 1 ~3 ~3, !0 3 ROPE_END 2 ~2 ~3, '%21+Have+a+nice+day%21' 4 ECHO ~2 

Zweitens:

 echo 'Hello '.$name.'! Have a nice day!'; line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 CONCAT ~2 'Hello+', !0 2 CONCAT ~3 ~2, '%21+Have+a+nice+day%21' 3 ECHO ~3 

Drittens:

 echo 'Hello ', $name, '! Have a nice day!'; line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 ECHO 'Hello+' 2 ECHO !0 3 ECHO '%21+Have+a+nice+day%21' 

Viertens:

 printf ('Hello %s! Have a nice day!', $name); line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 INIT_FCALL 'printf' 2 SEND_VAL 'Hello+%25s%21+Have+a+nice+day%21' 3 SEND_VAR !0 4 DO_ICALL 

Der gesunde Menschenverstand sagt uns, dass die Option mit "printf" durch die ersten drei an Geschwindigkeit verlieren wird (zumal es am Ende immer noch das gleiche ECHO gibt), so dass wir es für Aufgaben belassen, bei denen eine Formatierung erforderlich ist, und wir werden uns in diesem Artikel nicht mehr daran erinnern.

Es scheint, dass die dritte Option die schnellste ist - drei Zeilen hintereinander ohne Verkettungen, seltsame ROPEs und die Erstellung zusätzlicher Variablen zu drucken. Aber nicht so einfach. Die Druckfunktion in PHP ist sicherlich nicht Rocket Science, aber es ist keineswegs eine banale C-shny- Eingabe . Wen kümmert es - der Ball löst sich beginnend mit php_output_write in der Datei main / output.c .

CONCAT. Hier ist alles einfach - wir konvertieren die Argumente bei Bedarf in Strings und erstellen mit fast memcpy einen neuen zend_string . Der einzige Nachteil ist, dass mit einer langen Kette von Verkettungen für jede Operation neue Zeilen erstellt werden, indem dieselben Bytes von Ort zu Ort verschoben werden.

Aber mit ROPE_INIT, ROPE_ADD und ROPE_END ist alles viel interessanter. Folgen Sie den Händen:

  1. ROPE_INIT (ext = 3, return = ~ 3, operands = 'Hello +')
    Wir ordnen das "Seil" aus drei Slots (ext) zu, setzen den String 'Hello +' (Operanden) in Slot 0 und geben die temporäre Variable ~ 3 (return) zurück, die das "Seil" enthält.
  2. ROPE_ADD (ext = 1, return = ~ 3, Operanden = ~ 3 ,! 0)
    Wir setzen in den Schlitz 1 (ext) des "Seils" ~ 3 (Operanden) die Zeichenfolge 'Vasya', die aus der Variablen! 0 (Operanden) erhalten wird, und geben das "Seil" ~ 3 (return) zurück.
  3. ROPE_END (ext = 2, return = ~ 2, operands = ~ 3, '% 21 + Have + a + nice + day% 21')
    Wir setzen die Zeile '% 21 + Have + a + nice + day% 21' (Operanden) in Slot 2 (ext), danach erstellen wir einen zend_string der erforderlichen Größe und kopieren alle "Seil" -Slots nacheinander mit demselben Memcpy hinein .

Unabhängig davon ist zu beachten, dass bei Konstanten und temporären Variablen Links zu den Daten in den Slots platziert werden und kein unnötiges Kopieren erfolgt.

Meiner Meinung nach ziemlich elegant. :) :)

Lassen Sie uns Benchmarking. Als Quelldaten nehmen wir die Datei zend_vm_execute.h (IMHO wird dies zutreffen) für 71.000 Zeilen und drucken sie auf 100 Arten für 100 Durchgänge, wobei das Minimum und das Maximum gelöscht werden (jede Messung wurde 10 Mal gestartet, wobei die häufigste Option ausgewählt wurde):

 <?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++; // echo 'line: ', $i, 'text: ', $line; // echo 'line: ' . $i . 'text: ' . $line; // echo "line: $i text: $line"; // printf('line: %d text: %s', $i, $line); } ob_end_clean(); $out[] = (microtime(true) - $start); } $min = min($out); $max = max($out); echo (array_sum($out) - $min - $max) / 98; 

Was messen wir?Durchschnittliche Zeit in Sekunden
"Seil"0,0129
Mehrere ECHO0,0135
Verkettung0,0158
printf der Vollständigkeit halber0,0245

Schlussfolgerungen


  1. Bei Zeichenfolgen mit einfacher Ersetzung sind doppelte Anführungszeichen plötzlich optimaler als einfache Anführungszeichen mit Verkettung. Und je länger die Leitungen verwendet werden, desto größer ist die Verstärkung.
  2. Durch Kommas getrennte Argumente ... Es gibt viele Nuancen. Durch Messung ist es schneller als die Verkettung und langsamer als das „Seil“, aber es gibt zu viele „Variablen“, die mit der Eingabe / Ausgabe verbunden sind.

Fazit


Es fällt mir schwer, eine Situation zu finden, in der solche Mikrooptimierungen erforderlich sein können. Wenn Sie sich für diesen oder jenen Ansatz entscheiden, ist es sinnvoller, sich von anderen Prinzipien leiten zu lassen - beispielsweise von der Lesbarkeit des Codes oder dem von Ihrem Unternehmen verwendeten Codierungsstil.

Ich persönlich mag den Verkettungsansatz wegen des vyrviglazny-Aussehens nicht, obwohl er in einigen Fällen gerechtfertigt sein kann.

PS Wenn diese Art der Analyse interessant ist - lassen Sie es mich wissen -, gibt es noch viel mehr, das alles andere als immer eindeutig und offensichtlich ist: eine Reihe von VS-Objekten, für jedes VS, während VS für, Ihre Option ... :)

Eine kleine Erklärung aus dem Lesen von Kommentaren


Die HEREDOC-Syntax und die „komplexen Zeichenfolgen“ (wobei sich die Variablen in geschweiften Klammern befinden) sind dieselben Zeichenfolgen in doppelten Anführungszeichen und werden auf genau dieselbe Weise kompiliert.

Eine Mischung aus PHP mit HTML wie folgt:
 <?php $name = 'Vasya';?>Hello <?=$name?>! Have a nice day! 

Dies sind nur 3 Echos hintereinander.

Source: https://habr.com/ru/post/de447416/


All Articles