PHP GR8: Verbessert JIT die Leistung von PHP 8



PHP ist eine der wichtigsten Entwicklungssprachen bei Badoo. In unseren Rechenzentren sind Tausende von Prozessorkernen damit beschäftigt, Millionen von Zeilen PHP-Code auszuführen. Wir verfolgen die Nachrichten genau und suchen aktiv nach Möglichkeiten zur Verbesserung der Produktivität, da bereits eine geringfügige Optimierung unserer Mengen zu erheblichen Ressourceneinsparungen führt. Eine der wichtigsten Neuigkeiten zur PHP-Leistung ist das Erscheinen von JIT in der achten Version der Sprache. Dies konnte natürlich nicht ohne unsere Aufmerksamkeit bleiben, und wir übersetzten einen Artikel darüber, was JIT ist, wie es in PHP implementiert wird, warum es beschlossen wurde und was von ihm zu erwarten ist.

Wenn Sie die Höhle nicht verlassen haben oder nicht aus der Vergangenheit stammen (in diesem Fall willkommen), wissen Sie bereits, dass PHP 8 JIT haben wird: Neulich war die Abstimmung ruhig und friedlich und die überwiegende Mehrheit der Teilnehmer stimmte für die Implementierung. Das ist also alles entschieden .

In einem Anfall von Freude können Sie sogar mehrere verrückte Bewegungen wie auf dem Foto darstellen (dies wird übrigens als "Detroit JIT" bezeichnet:



Setzen Sie sich jetzt und lesen Sie diesen Mythos entlarvenden Artikel. Ich möchte das Missverständnis klären, das damit verbunden ist, was JIT ist und wie es nützlich ist, und darüber sprechen, wie es funktioniert (aber nicht zu detailliert, damit Sie sich nicht langweilen).

Da ich nicht weiß, wer den Artikel lesen wird, gehe ich von einfachen zu komplexen Fragen über. Wenn Sie die Antwort auf die Frage im Titel bereits kennen, können Sie das entsprechende Kapitel sicher überspringen.

Was ist JIT?


PHP wird auf Basis einer virtuellen Maschine implementiert (wir nennen es Zend VM). Die Sprache kompiliert den PHP-Quellcode in Anweisungen, die die virtuelle Maschine versteht (dies wird als Kompilierungsphase bezeichnet). Die in der Kompilierungsphase erhaltenen Anweisungen für virtuelle Maschinen werden als Opcodes bezeichnet. Zur Laufzeit führt die Zend VM die Opcodes aus und führt damit die erforderliche Arbeit aus.

Diese Schaltung funktioniert super. Darüber hinaus speichern Tools wie APC (vorher) und OpCache (heute) die Ergebnisse der Kompilierungsphase zwischen, sodass diese Phase nur bei Bedarf ausgeführt wird.

Kurz gesagt, JIT ist eine Just-in-Time-Kompilierungsstrategie (zum richtigen Zeitpunkt), bei der der Code zunächst in eine Zwischendarstellung übersetzt wird, die dann während der Ausführung in einen architekturabhängigen Maschinencode umgewandelt wird.

In PHP bedeutet dies, dass JIT die Anweisungen für die virtuelle Maschine, die in der Kompilierungsphase empfangen wurden, als Zwischendarstellung betrachtet und Maschinencode ausgibt, der nicht mehr von der Zend VM, sondern direkt vom Prozessor ausgeführt wird.

Warum braucht PHP JIT?


Kurz vor dem Aufkommen von PHP 7.0 lag der Schwerpunkt des PHP-Teams auf der Sprachleistung. Die meisten wesentlichen Änderungen in PHP 7.0 betrafen den PHPNG-Patch, der die Art und Weise, wie PHP Speicher und Prozessor verwendet, erheblich verbesserte. Seitdem muss jeder von uns einen Blick auf die Leistung der Sprache werfen.

Nach der Veröffentlichung von PHP 7.0 wurden die Leistungsverbesserungen fortgesetzt: Eine Hash-Tabelle (die Hauptdatenstruktur in PHP) wurde optimiert, die Spezialisierung bestimmter Opcodes in Zend VM und die Spezialisierung bestimmter Sequenzen im Compiler wurden implementiert, das Optimierungsprogramm (OpCache-Komponente) wurde ständig verbessert und viele andere Änderungen wurden implementiert.

Die harte Wahrheit ist, dass wir uns aufgrund all dieser Optimierungen schnell der Grenze der Möglichkeiten zur Produktivitätsverbesserung nähern.

Bitte beachten Sie: Mit „Begrenzung der Verbesserungsmöglichkeiten“ meine ich die Tatsache, dass die Kompromisse, die für weitere Verbesserungen getroffen werden müssen, nicht mehr attraktiv aussehen. Wenn es um die Optimierung der Leistung geht, sprechen wir immer über Kompromisse. Oft müssen wir aus Gründen der Produktivität auf Einfachheit verzichten. Jeder würde gerne denken, dass der einfachste Code auch der schnellste ist, aber in der modernen Welt der C-Programmierung ist dies nicht der Fall. Am schnellsten ist der Code, der bereit ist, die interne Struktur der Architektur oder die in die Plattform / den Compiler integrierten Strukturen zu nutzen. Einfachheit allein garantiert keine bessere Leistung.

Daher ist zu diesem Zeitpunkt die Implementierung von JIT der beste Weg, um noch mehr Leistung aus PHP herauszuholen.

Wird JIT meine Website beschleunigen?


Höchstwahrscheinlich unbedeutend.

Dies ist möglicherweise nicht die Antwort, die Sie erwartet haben. Tatsache ist, dass PHP-Anwendungen im Allgemeinen durch Eingabe / Ausgabe (E / A-gebunden) begrenzt sind und JIT am besten mit Code funktioniert, der durch den Prozessor begrenzt ist (CPU-gebunden).

Was bedeutet "durch E / A und Prozessor begrenzt"?


Um die Merkmale der Gesamtleistung eines Codes oder einer Anwendung zu beschreiben, verwenden wir die Begriffe "begrenzt durch Eingabe-Ausgabe" und "begrenzt durch Prozessor".

Die einfachste Definition:

  • Durch E / A begrenzter Code funktioniert viel schneller, wenn wir einen Weg finden, die durchgeführten E / A-Operationen zu verbessern (zu reduzieren, zu optimieren).
  • Prozessor-begrenzter Code funktioniert viel schneller, wenn wir einen Weg finden, die vom Prozessor ausgeführten Anweisungen zu verbessern (zu reduzieren, zu optimieren) oder die Prozessortaktrate auf magische Weise zu erhöhen.

Code und Anwendung können durch E / A, Prozessor oder beides eingeschränkt werden.

Im Allgemeinen sind PHP-Anwendungen in der Regel durch E / A eingeschränkt: Ihr Hauptengpass sind häufig E / A-Vorgänge - Verbinden, Lesen und Schreiben in die Datenbank, Caches, Dateien, Sockets usw.

Wie sieht prozessorbeschränkter PHP-Code aus?


Möglicherweise sind einige PHP-Programmierer aufgrund der Natur der meisten PHP-Anwendungen neu in prozessorbeschränktem Code: Sie fungieren normalerweise als Verknüpfung zur Datenbank oder zum Cache, erfassen und produzieren kleine Mengen von HTML / JSON / XML-Antworten.

Sie können sich Ihre Codebasis ansehen und viel Code finden, der nichts mit E / A zu tun hat. Code, der Funktionen aufruft, die nichts mit E / A zu tun haben. Und Sie können verwirrt sein, dass dies Ihre Anwendung nicht durch den Prozessor einschränkt, obwohl der Code mehr Zeilen enthält, die nicht mit E / A funktionieren als funktionieren.

Tatsache ist, dass PHP eine der am schnellsten interpretierten Sprachen ist. Es gibt keinen merklichen Unterschied zwischen dem Aufrufen einer Funktion, die keine E / A in Zend VM verwendet, und im Maschinencode. Natürlich gibt es einige Unterschiede, aber sowohl der Maschinencode als auch die Zend-VM verwenden die Aufrufkonvention. Es spielt also keine Rolle, -___() Sie -___() in den Opcodes oder im Maschinencode -___() - dies hat keine spürbaren Auswirkungen auf die Leistung der gesamten Anwendung, die den Anruf tätigt.

Hinweis: In einfachen Worten ist eine Aufrufkonvention eine Folge von Anweisungen, die vor der Eingabe einer anderen Funktion ausgeführt werden. In beiden Fällen übergibt die aufrufende Konvention Argumente an den Stapel.

Sie fragen: "Was ist mit Loops, Tail Calls und mehr?" PHP ist intelligent genug - und wenn Optimizer von OpCache aktiviert ist, wird Ihr Code auf magische Weise in eine effizientere Version Ihrer Texte konvertiert.

Hierbei ist zu beachten, dass JIT die Zend VM-Aufrufkonventionen nicht ändert. Dies geschieht, weil PHP jederzeit zwischen JIT- und VM-Modus wechseln kann (daher haben sie beschlossen, die aktuellen Konventionen beizubehalten). Infolgedessen funktionieren Anrufe, die Sie überall mit JIT sehen, nicht viel schneller.

Wenn Sie sehen möchten, wie prozessorbeschränkter PHP-Code aussieht, schauen Sie hier: https://github.com/php/php-src/blob/master/Zend/bench.php . Dies ist ein extremes Beispiel, aber es zeigt, dass die ganze Pracht der JIT in der Mathematik offenbart wird.

Musste einen so extremen Kompromiss eingehen, um mathematische Berechnungen in PHP zu beschleunigen?


Nein. Wir haben dies getan, um den Anwendungsbereich der Sprache zu erweitern (und signifikant zu erweitern).

Wir wollen nicht prahlen, aber PHP dominiert das Web. Wenn Sie sich mit Webentwicklung beschäftigen und nicht in Betracht ziehen, PHP in Ihrem nächsten Projekt zu verwenden, dann machen Sie etwas falsch (laut einem sehr voreingenommenen PHP-Entwickler).

Auf den ersten Blick scheint die Beschleunigung mathematischer Berechnungen in PHP eine sehr enge Anwendung zu haben. Dies eröffnet uns jedoch beispielsweise den Weg zum maschinellen Lernen, 3D-Rendering, 2D-Rendering (GUI) und Datenanalyse.

Warum kann dies nicht in PHP 7.4 implementiert werden?


Oben habe ich JIT als extremen Kompromiss bezeichnet, und ich denke wirklich: Dies ist eine der schwierigsten Kompilierungsstrategien unter allen existierenden, wenn nicht die schwierigste. Die Implementierung von JIT erhöht die Komplexität erheblich.

Wenn Sie Dmitry, den Autor von JIT, fragen, ob er PHP kompliziert gemacht hat, antwortet er: „Nein, ich hasse Komplexität“ (dies ist ein Zitat).

Im Wesentlichen bedeutet "komplex" "das, was wir nicht verstehen". Und heute verstehen nur wenige Sprachentwickler die bestehende Implementierung von JIT wirklich.

Die Arbeit an PHP 7.4 schreitet zügig voran, und die Einführung von JIT in dieser Version wird dazu führen, dass nur wenige die Sprache debuggen, reparieren und verbessern können. Dies ist nicht akzeptabel für diejenigen, die in PHP 7.4 gegen JIT gestimmt haben.

Vor der Veröffentlichung von PHP 8 werden viele von uns die JIT-Implementierung verstehen. Es gibt Funktionen, die wir implementieren möchten, und Tools, die wir für die achte Version neu schreiben möchten. Daher müssen wir zuerst die JIT verstehen. Wir brauchen diese Zeit und sind sehr dankbar, dass die Mehrheit dafür gestimmt hat, sie uns zu geben.

Komplex ist nicht gleichbedeutend mit dem Schrecklichen. Komplexität kann so schön sein wie ein Sternnebel, und hier geht es nur um JIT. Mit anderen Worten, selbst wenn 20 Personen in unserem Team beginnen, JIT nicht schlechter als Dmitry zu verstehen, wird dies die Komplexität der Natur von JIT nicht ändern.

Wird sich die PHP-Entwicklung verlangsamen?


Es gibt keinen Grund, dies zu glauben. Wir haben genug Zeit, daher kann argumentiert werden, dass es bis zur Fertigstellung von PHP 8 genug unter uns geben wird, die JIT genug beherrschen, um nicht weniger effizient als heute zu arbeiten, wenn es darum geht, Fehler zu beheben und PHP zu entwickeln.

Wenn Sie versuchen, dies mit der Idee der ursprünglichen Komplexität von JIT in Verbindung zu bringen, denken Sie daran, dass die meiste Zeit, die wir für die Einführung neuer Funktionen aufwenden, damit verbracht wird, diese zu diskutieren. Wenn Sie an Funktionen arbeiten und Fehler beheben, dauert das Schreiben eines Codes meistens Minuten oder Stunden, und Diskussionen dauern Wochen oder Monate. In seltenen Fällen muss der Code stunden- oder tagelang geschrieben werden, aber selbst dann dauern Diskussionen immer länger.

Das ist alles was ich sagen wollte.

Und da wir über Leistung sprechen, lade ich meinen Kollegen Pavel Murzakov zu dem Bericht am 17. Mai auf der PHP Russia- Konferenz ein . Pascha weiß, wie man die letzte CPU-Sekunde aus dem PHP-Code herausdrückt!

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


All Articles