Erst kürzlich habe ich wieder gehört, dass PHP-Leute immer noch über einfache Anführungszeichen vs. doppelte Anführungszeichen sprechen und dass die Verwendung einfacher Anführungszeichen nur eine Mikrooptimierung ist, aber wenn man sich daran gewöhnt, ständig einfache Anführungszeichen zu verwenden, würde man eine Menge CPU sparen Zyklen!
„Alles ist schon gesagt, aber noch nicht von allen“ – Karl Valentin
In diesem Sinne schreibe ich einen Artikel über dasselbe Thema, das Nikita Popov bereits vor 12 Jahren geschrieben hat (wenn Sie seinen Artikel lesen, können Sie hier aufhören zu lesen).
PHP führt eine String-Interpolation durch, bei der es nach der Verwendung von Variablen in einem String sucht und diese durch den Wert der verwendeten Variablen ersetzt:
$juice = "apple"; echo "They drank some $juice juice."; // will output: They drank some apple juice.
Diese Funktion ist auf Zeichenfolgen in doppelten Anführungszeichen und Heredoc beschränkt. Die Verwendung von einfachen Anführungszeichen (oder nowdoc) führt zu einem anderen Ergebnis:
$juice = "apple"; echo 'They drank some $juice juice.'; // will output: They drank some $juice juice.
Sehen Sie sich das an: PHP sucht nicht nach Variablen in dieser Zeichenfolge in einfachen Anführungszeichen. Wir könnten also einfach überall einfache Anführungszeichen verwenden. Also fingen die Leute an, solche Änderungen vorzuschlagen ..
- $juice = "apple"; $juice = 'apple';
.. weil es schneller ist und bei jeder Ausführung dieses Codes eine Menge CPU-Zyklen einspart, weil PHP nicht nach Variablen in Zeichenfolgen in einfachen Anführungszeichen sucht (die im Beispiel sowieso nicht vorhanden sind) und Alle sind zufrieden, Fall abgeschlossen.
Natürlich gibt es einen Unterschied zwischen einfachen und doppelten Anführungszeichen, aber um zu verstehen, was vor sich geht, müssen wir etwas tiefer graben.
Obwohl PHP eine interpretierte Sprache ist, verwendet es einen Kompilierungsschritt, in dem bestimmte Teile zusammenspielen, um etwas zu erhalten, das die virtuelle Maschine tatsächlich ausführen kann, nämlich Opcodes. Wie gelangen wir also vom PHP-Quellcode zu Opcodes?
Der Lexer scannt die Quellcodedatei und zerlegt sie in Token. Ein einfaches Beispiel dafür, was das bedeutet, finden Sie in der Dokumentation der Funktion token_get_all(). Ein PHP-Quellcode von nur
T_OPEN_TAG (In diesem 3v4l.org-Snippet können wir dies in Aktion sehen und damit spielen.
Der Parser
Der Parser nimmt diese Token und generiert daraus einen abstrakten Syntaxbaum. Eine AST-Darstellung des obigen Beispiels sieht folgendermaßen aus, wenn sie als JSON dargestellt wird:
{ "data": [ { "nodeType": "Stmt_Echo", "attributes": { "startLine": 1, "startTokenPos": 1, "startFilePos": 6, "endLine": 1, "endTokenPos": 4, "endFilePos": 13 }, "exprs": [ { "nodeType": "Scalar_String", "attributes": { "startLine": 1, "startTokenPos": 3, "startFilePos": 11, "endLine": 1, "endTokenPos": 3, "endFilePos": 12, "kind": 2, "rawValue": "\"\"" }, "value": "" } ] } ] }Falls Sie auch damit herumspielen und sehen möchten, wie der AST für anderen Code aussieht, habe ich https://phpast.com/ von Ryan Chandler und https://php-ast-viewer.com/ gefunden beide zeigen Ihnen den AST eines bestimmten PHP-Codestücks.
Der Compiler
Der Compiler nimmt den AST und erstellt Opcodes. Die Opcodes sind die Dinge, die die virtuelle Maschine ausführt. Sie werden auch im OPcache gespeichert, wenn Sie dies eingerichtet und aktiviert haben (was ich sehr empfehle).
Um die Opcodes anzuzeigen, haben wir mehrere Optionen (vielleicht mehr, aber ich kenne diese drei):
- Verwenden Sie die vulcan Logic Dumper-Erweiterung. Es ist auch in 3v4l.org integriert
- verwenden Sie phpdbg -p script.php, um die Opcodes auszugeben
- oder verwenden Sie die INI-Einstellung opcache.opt_debug_level für OPcache, um die Opcodes auszugeben
- Ein Wert von 0x10000 gibt Opcodes vor der Optimierung aus
- Ein Wert von 0x20000 gibt Opcodes nach der Optimierung aus
$ echo ' foo.php $ php -dopcache.opt_debug_level=0x10000 foo.php $_main: ... 0000 ECHO string("") 0001 RETURN int(1)Hypothese
Um auf die ursprüngliche Idee der Einsparung von CPU-Zyklen bei der Verwendung von einfachen Anführungszeichen gegenüber doppelten Anführungszeichen zurückzukommen: Ich denke, wir sind uns alle einig, dass dies nur dann zutrifft, wenn PHP diese Zeichenfolgen zur Laufzeit für jede einzelne Anfrage auswerten würde.
Was passiert zur Laufzeit?
Mal sehen, welche Opcodes PHP für die beiden verschiedenen Versionen erstellt.
Doppelte Anführungszeichen:
0000 ECHO string("apple") 0001 RETURN int(1)vs. einfache Anführungszeichen:
0000 ECHO string("apple") 0001 RETURN int(1)Hey, warte, etwas Seltsames ist passiert. Das sieht identisch aus! Wo ist meine Mikrooptimierung geblieben?
Nun, vielleicht, nur vielleicht analysiert die Implementierung des ECHO-Opcode-Handlers die angegebene Zeichenfolge, obwohl es keine Markierung oder etwas anderes gibt, das sie dazu auffordert ... hmm ?
Lassen Sie uns einen anderen Ansatz ausprobieren und sehen, was der Lexer in diesen beiden Fällen tut:
Doppelte Anführungszeichen:
T_OPEN_TAG (vs. einfache Anführungszeichen:
Line 1: T_OPEN_TAG (Die Token unterscheiden immer noch zwischen doppelten und einfachen Anführungszeichen, aber die Überprüfung des AST liefert uns in beiden Fällen ein identisches Ergebnis – der einzige Unterschied ist der rawValue in den Scalar_String-Knotenattributen, der immer noch die einfachen/doppelten Anführungszeichen enthält, aber Der Wert verwendet in beiden Fällen doppelte Anführungszeichen.
Neue Hypothese
Könnte es sein, dass die String-Interpolation tatsächlich zur Kompilierzeit erfolgt?
Überprüfen wir es anhand eines etwas „ausgefeilteren“ Beispiels:
Tokens für diese Datei sind:
T_OPEN_TAG (Sehen Sie sich die letzten beiden Token an! Die String-Interpolation wird im Lexer gehandhabt und ist daher eine Sache zur Kompilierungszeit und hat nichts mit der Laufzeit zu tun.
Der Vollständigkeit halber werfen wir einen Blick auf die dadurch generierten Opcodes (nach der Optimierung mit 0x20000):
0000 ASSIGN CV0($juice) string("apple") 0001 T2 = FAST_CONCAT string("juice: ") CV0($juice) 0002 ECHO T2 0003 RETURN int(1)Dies ist ein anderer Opcode als wir in unserem einfachen
Kommen Sie auf den Punkt: Soll ich verketten oder interpolieren?
Schauen wir uns diese drei verschiedenen Versionen an:
Der erste Opcode weist der Variablen $juice die Zeichenfolge „apple“ zu:
0000 ASSIGN CV0($juice) string("apple")
Die erste Version (String-Interpolation) verwendet ein Seil als zugrunde liegende Datenstruktur, die darauf optimiert ist, so wenig String-Kopien wie möglich zu erstellen.
0001 T2 = ROPE_INIT 4 string("juice: ") 0002 T2 = ROPE_ADD 1 T2 CV0($juice) 0003 T2 = ROPE_ADD 2 T2 string(" ") 0004 T1 = ROPE_END 3 T2 CV0($juice) 0005 ECHO T1
Die zweite Version ist am speichereffizientesten, da sie keine Zwischenzeichenfolgendarstellung erstellt. Stattdessen führt es mehrere Aufrufe an ECHO aus, was aus E/A-Sicht einen blockierenden Aufruf darstellt. Abhängig von Ihrem Anwendungsfall kann dies also ein Nachteil sein.
0006 ECHO string("juice: ") 0007 ECHO CV0($juice) 0008 ECHO string(" ") 0009 ECHO CV0($juice)
Die dritte Version verwendet CONCAT/FAST_CONCAT, um eine Zwischenzeichenfolgendarstellung zu erstellen und benötigt daher möglicherweise mehr Speicher als die Rope-Version.
0010 T1 = CONCAT string("juice: ") CV0($juice) 0011 T2 = FAST_CONCAT T1 string(" ") 0012 T1 = CONCAT T2 CV0($juice) 0013 ECHO T1
Also ... was ist hier das Richtige und warum ist es String-Interpolation?
String-Interpolation verwendet entweder ein FAST_CONCAT im Fall von echo „juice: $juice“; oder hochoptimierte ROPE_*-Opcodes im Fall von echo „juice: $juice $juice“;, aber am wichtigsten ist, dass es die Absicht klar kommuniziert und nichts davon war in irgendeiner der PHP-Anwendungen, mit denen ich bisher gearbeitet habe, ein Flaschenhals war. also spielt das alles keine Rolle.
String-Interpolation ist eine Sache zur Kompilierungszeit. Zugegeben, ohne OPcache muss der Lexer bei jeder Anfrage nach Variablen suchen, die in Zeichenfolgen in doppelten Anführungszeichen verwendet werden, auch wenn es keine gibt, was die CPU-Zyklen verschlingt, aber ehrlich gesagt: Das Problem sind nicht die Zeichenfolgen in doppelten Anführungszeichen, sondern nicht die Verwendung von OPcache!
Allerdings gibt es eine Einschränkung: PHP bis 4 (und ich glaube sogar einschließlich 5.0 und vielleicht sogar 5.1, ich weiß es nicht) hat zur Laufzeit String-Interpolation durchgeführt, also mit diesen Versionen ... hmm, ich denke, wenn Wenn jemand wirklich noch PHP 5 nutzt, gilt das Gleiche wie oben: Das Problem sind nicht die doppelten Anführungszeichen, sondern die Verwendung einer veralteten PHP-Version.
Aktualisieren Sie auf die neueste PHP-Version, aktivieren Sie OPcache und leben Sie glücklich bis ans Ende Ihrer Tage!
Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.
Copyright© 2022 湘ICP备2022001581号-3