Was ist falsch an populären Artikeln, die besagen, dass foo schneller als bar ist?

Anmerkung des Übersetzers: Ich dachte auch, dass die Zeit für die Artikel "Was ist schneller - doppelte oder einfache Anführungszeichen?" Ist. Es hat noch 10 Jahre gedauert. Aber hier hat ein ähnlicher Artikel ("Welche Leistungstricks tatsächlich funktionieren") kürzlich eine relativ hohe Bewertung für Reddit erhalten und ist sogar in den PHP-Digest für Habré eingestiegen. Dementsprechend habe ich beschlossen, den Artikel mit einer kritischen Analyse dieser und ähnlicher "Tests" zu übersetzen.


Es gibt viele Artikel (und sogar ganze Websites), die sich mit dem Starten verschiedener Tests befassen, in denen die Leistung verschiedener syntaktischer Konstruktionen verglichen wird und auf dieser Grundlage angegeben wird, dass einer schneller als der andere ist.


Hauptproblem


Solche Tests sind aus vielen Gründen falsch, von Fragen bis hin zu Implementierungsfehlern. Vor allem aber sind solche Tests bedeutungslos und gleichzeitig schädlich.


  • Sie sind bedeutungslos, weil sie keinen praktischen Wert haben. Kein wirkliches Projekt wurde jemals mit den in solchen Artikeln bereitgestellten Methoden beschleunigt. Nur weil nicht die Unterschiede in der Syntax für die Leistung von Bedeutung sind, sondern die Datenverarbeitung.
  • Sie sind schädlich, weil sie zum Auftreten des wildesten Aberglaubens führen und - noch schlimmer - ahnungslose Leser dazu ermutigen, schlechten Code zu schreiben, weil sie denken, dass sie ihn "optimieren".

Das sollte ausreichen, um die Frage zu schließen. Aber selbst wenn Sie die Spielregeln akzeptieren und so tun, als hätten diese „Tests“ zumindest einen gewissen Sinn, stellt sich heraus, dass ihre Ergebnisse nur auf eine Demonstration des Mangels an Wissen des Testers und seiner mangelnden Erfahrung reduziert werden.


Einzel gegen Doppel


Nehmen Sie die berüchtigten Zitate "einfach gegen doppelt". Natürlich sind keine Zitate schneller. Erstens gibt es so etwas wie einen Opcode-Cache , der das Ergebnis des Parsens des PHP-Skripts im Cache speichert. In diesem Fall wird der PHP-Code im Opcode-Format gespeichert, in dem dieselben Zeichenfolgenliterale als absolut identische Entitäten gespeichert werden, unabhängig davon, welche Anführungszeichen im PHP-Skript verwendet wurden. Was bedeutet, dass nicht einmal ein theoretischer Leistungsunterschied vorliegt.


Aber selbst wenn wir keinen Opcode-Cache verwenden (obwohl wir dies tun sollten, wenn unsere Aufgabe darin besteht, die Leistung wirklich zu steigern), werden wir feststellen, dass der Unterschied beim Parsen von Code so gering ist (mehrere bedingte Übergänge, bei denen Einzelbyte-Zeichen verglichen werden, buchstäblich mehrere Prozessoranweisungen), dass dies absolut der Fall ist nicht nachweisbar. Dies bedeutet, dass alle erhaltenen Ergebnisse nur Probleme in der Testumgebung zeigen. Es gibt einen sehr detaillierten Artikel des PHP-Kernentwicklers Nikita Popov, in dem dieses Problem ausführlich analysiert wird. Trotzdem erscheint fast jeden Monat ein Energietester, um der Gesellschaft einen imaginären „Leistungsunterschied“ aufzuzeigen.


Logische Inkonsistenzen


Einige Tests sind im Allgemeinen bedeutungslos, einfach unter dem Gesichtspunkt, die Frage zu stellen: Zum Beispiel der Test mit dem Titel "Ist Werfen wirklich eine super teure Operation?" Dies ist im Wesentlichen die Frage "Ist es wirklich so, dass die Verarbeitung eines Fehlers teurer ist als die Nichtbearbeitung?". Ist das dein ernst Wenn Sie dem Code einige grundlegende Funktionen hinzufügen, wird er natürlich „langsamer“. Dies bedeutet jedoch nicht, dass unter einem so lächerlichen Vorwand überhaupt keine neuen Funktionen hinzugefügt werden müssen. Wenn Sie so reden, dann ist das schnellste Programm eines, das überhaupt nichts tut! Das Programm sollte nützlich sein und in erster Linie fehlerfrei funktionieren. Und erst wenn dies erreicht ist und nur wenn es langsam funktioniert, muss es optimiert werden. Aber wenn die Frage selbst keinen Sinn ergibt, warum dann die Leistung testen? Es ist lustig, dass der Tester selbst diesen sinnlosen Test nicht korrekt implementieren konnte, der im nächsten Abschnitt gezeigt wird.


Oder ein anderes Beispiel, ein Test mit dem Titel "Wird $row[id] wirklich langsamer sein als $row['id'] ?" Dies ist im Wesentlichen die Frage "Welcher Code ist schneller - derjenige, der mit oder ohne Fehler arbeitet?" (Da das Schreiben einer id ohne Anführungszeichen in diesem Fall ein Fehler der E_NOTICE Ebene ist und ein solches Schreiben in zukünftigen Versionen von PHP veraltet ist). WTF? Was ist der Sinn der allgemeinen Messung der Leistung von Fehlercode? Der Fehler sollte einfach behoben werden, weil es sich um einen Fehler handelt und nicht, weil dadurch der Code langsamer ausgeführt wird. Es ist lustig, dass der Tester selbst diesen sinnlosen Test nicht korrekt implementieren konnte, der im nächsten Abschnitt gezeigt wird.


Testqualität


Und wieder - selbst ein wissentlich nutzloser Test sollte konsistent und konsistent sein - das heißt, vergleichbare Werte messen. In der Regel werden solche Tests jedoch mit der linken Ferse durchgeführt, sodass die erzielten Ergebnisse bedeutungslos und für die Aufgabe nicht relevant sind.


Zum Beispiel hat sich unser dummer Tester verpflichtet, "übermäßige Nutzung des try..catch Operators" zu messen. Im aktuellen Test maß er jedoch nicht nur den try catch , sondern auch zu throw , wobei bei jeder Iteration der Schleife eine Ausnahme ausgelöst wurde. Ein solcher Test ist jedoch einfach falsch, da im wirklichen Leben nicht bei jeder Skriptausführung Fehler auftreten.


Natürlich sollten Tests nicht mit Beta-Versionen von PHP durchgeführt werden und Mainstream-Lösungen sollten nicht mit experimentellen verglichen werden. Und wenn sich der Tester verpflichtet, die "Parsing-Geschwindigkeit von json und xml" zu vergleichen, sollte er die experimentelle Funktion in den Tests nicht verwenden.


Einige Tests zeigen lediglich ein völliges Missverständnis des Testers der von ihm gestellten Aufgabe. Ein ähnliches Beispiel aus einem kürzlich veröffentlichten Artikel wurde bereits oben erwähnt: Der Autor des Tests hat versucht herauszufinden, ob der Code, der den Fehler verursacht hat ("Verwendung einer undefinierten Konstante"), langsamer ist als der Code ohne Fehler (der ein syntaktisch korrektes Zeichenfolgenliteral verwendet), ist jedoch fehlgeschlagen Selbst bei diesem offensichtlich bedeutungslosen Test wird die Leistung einer zitierten Zahl mit der Leistung einer ohne Anführungszeichen geschriebenen Zahl verglichen. Natürlich können Sie in PHP Zahlen ohne Anführungszeichen schreiben (im Gegensatz zu Zeichenfolgen). Infolgedessen hat der Autor eine völlig andere Funktionalität getestet und falsche Ergebnisse erhalten.


Es sind noch weitere Aspekte zu berücksichtigen, z. B. die Testumgebung. Es gibt PHP-Erweiterungen wie XDebug, die einen großen Einfluss auf die Testergebnisse haben können. Oder der bereits erwähnte Opcode-Cache, der bei Leistungstests enthalten sein muss, damit die Testergebnisse zumindest einen Sinn ergeben.


Wie Tests durchgeführt werden, ist ebenfalls wichtig. Da der PHP-Prozess nach jeder Anforderung vollständig abbricht, ist es sinnvoll, die Leistung des gesamten Lebenszyklus zu testen, angefangen beim Erstellen einer Verbindung zu einem Webserver bis hin zum Schließen dieser Verbindung. Es gibt Dienstprogramme wie Apache Benchmark oder Siege, mit denen Sie dies tun können.


Echte Leistungsverbesserung


Das alles ist gut, aber welche Schlussfolgerung sollte der Leser aus diesem Artikel ziehen? Welche Leistungstests sind per Definition nutzlos? Natürlich nicht. Aber was wirklich zählt, ist der Grund , warum sie anfangen sollten. Testen von Grund auf ist Zeitverschwendung. Es sollte immer einen bestimmten Grund für die Durchführung von Leistungstests geben. Und dieser Grund wird als "Profiling" bezeichnet. Wenn Ihre Anwendung langsam ausgeführt wird, müssen Sie eine Profilerstellung durchführen. Dies bedeutet, dass Sie die Geschwindigkeit verschiedener Codeabschnitte messen müssen, um den langsamsten zu finden. Nachdem eine solche Site gefunden wurde, müssen wir die Ursache ermitteln. In den meisten Fällen ist dies entweder viel größer als erforderlich, die Menge der verarbeiteten Daten oder eine Anforderung an eine externe Datenquelle. Im ersten Fall besteht die Optimierung darin, die Menge der verarbeiteten Daten zu reduzieren und im zweiten Fall die Abfrageergebnisse zwischenzuspeichern.


In Bezug auf die Leistung spielt es beispielsweise keine Rolle, ob wir eine explizit vorgeschriebene Schleife oder die integrierte PHP-Funktion verwenden, um Arrays zu verarbeiten (was im Wesentlichen nur syntaktischer Zucker ist). Was wirklich wichtig ist, ist die Datenmenge , die wir zur Verarbeitung übertragen. Wenn es unangemessen groß ist, müssen wir es kürzen oder die Verarbeitung an einen anderen Ort (in die Datenbank) verschieben. Dies gibt uns einen enormen Leistungsschub, der real sein wird . Während der Unterschied zwischen den Methoden zum Aufrufen der Schleife für die Datenverarbeitung wahrscheinlich überhaupt nicht erkennbar ist.


Erst nachdem solche obligatorischen Leistungsverbesserungen durchgeführt wurden oder wenn wir die Menge der verarbeiteten Daten nicht reduzieren können, können wir Leistungstests starten. Andererseits sollten solche Tests nicht von Grund auf neu durchgeführt werden. Um die Leistung einer expliziten Schleife und einer Inline-Funktion vergleichen zu können, müssen wir sicher sein, dass die Schleife die Ursache des Problems ist und nicht deren Inhalt (Spoiler: Dies ist natürlich der Inhalt).


Ein aktuelles Beispiel aus meiner Praxis: Im Code gab es eine Abfrage mit Doctrine Query Builder, die mehrere tausend Parameter annehmen sollte. Die Abfrage selbst ist schnell genug, aber Doctrine benötigt eine ganze Weile, um mehrere tausend Parameter zu verarbeiten. Infolgedessen wurde die Abfrage in reinem SQL neu geschrieben und die Parameter wurden in die execute () -Methode der PDO-Bibliothek übertragen, die so viele Parameter fast sofort verarbeitet.


Bedeutet dies, dass ich Doctrine Query Builder niemals verwenden werde? Natürlich nicht. Es ist perfekt für 99% der Aufgaben und ich werde es weiterhin für alle Abfragen verwenden. Und nur in Ausnahmefällen lohnt es sich, eine weniger bequeme, aber produktivere Methode zu verwenden.


Die Abfrage und die Parameter für diese Auswahl wurden in einer Schleife erstellt. Wenn ich eine blöde Idee hätte, wie der Zyklus aufgerufen wird, würde ich einfach Zeit verlieren, ohne ein positives Ergebnis zu erzielen. Und das ist das Wesentliche aller Leistungsoptimierungen: Nur den Code zu optimieren, der in Ihrem speziellen Fall langsam ausgeführt wird. Und nicht der Code, der vor langer Zeit in einer fernen Galaxie als langsam galt, oder der Code, den jemand aufgrund bedeutungsloser Tests als langsam bezeichnete.

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


All Articles