Linuchs: Leerzeichen und Kommentare im PHP-Code: Kosten die Laufzeit?

Moin,

ich habe den Ehrgeiz, extrem schnelle Webseiten zu erstellen.

  • lange (interne) Listen werden als pure Daten im CSV-Format an den Browser gesendet. Ebenfalls ein minimales Javascript, das die Daten zu HTML-Positionen aufbereitet. Serverzeit bei 999 Adress-Positionen 0,45 sec., die üblichen 50 Positionen in 0.379 sec. Konzept vor vielen Monaten hier diskutiert, wurde kritisiert. Aber anderswo (Supermarkt, Schnellimbiss) hilft derselbe Kritiseur kräftig mit, stellt die Ware selbst zusammen und entlastet den „Servierer”.

  • Include-Dateien werden erst geladen, wenn sie - abhängig von if-Entscheidungen - gebraucht werden anstatt pauschal bei Programmstart.

Mein PHP-Code ist sehr übersichtlich gegliedert, enthält Kommentare, Leerzeilen, Leerzeichen. Also viel Redundanz für den Interpreter.

Auf anderen schnellen Seiten (z.B. Google-HTML) ist sogar der Zeilenumbruch raus.

Der PHP-Interpreter muss doch auch z.B. alle Kommentarzeilen lesen, sonst würde er ja das Ende des Kommentars nicht finden.

Wer hat sich schon mal mit dem Frisieren (Begriff aus dem Kfz-Tuning) von PHP und HTML beschäftigt? Wer kann Erfahrung zu PHP-Compilern beisteuern? Wundert mich, dass von Compilern keine Lese ist („keine Rede“ kann man hier ja nicht „sagen“).

Gruß, Linuchs

  1. Wer hat sich schon mal mit dem Frisieren (Begriff aus dem Kfz-Tuning) von PHP und HTML beschäftigt? Wer kann Erfahrung zu PHP-Compilern beisteuern? Wundert mich, dass von Compilern keine Lese ist („keine Rede“ kann man hier ja nicht „sagen“).

    Du solltest dich in erster Linie mit dem Messen und Aufspüren von Performance-Engpässen beschäftigen. Optimieren auf Verdacht ist nämlich nicht zielführend.

    Zum Messen der PHP-Performance benutzt man sogenannte Profiler. xdebug hat einen solchen an Bord. Damit solltest du dich vertraut machen. Zum Aufspüren sporadisch auftretender Performance-Lags und für Messungen im Produktiv-Betrieb ist xdebug allerdings ungeeignet. Für diesen Fall gibt es sogenannte passive Profiler, zum Beispiel von blackfire.

    Zum Messen der Performance des Netzwerk-Verkehrs und des clientseitigen Codes, kannst du die Browse-Entwickler-Tools benutzen. In Chrome gibt es einen speziellen Reiter "Performance", damit kannst du Messdaten aufzeichnen und analysieren. Außerdem gibt es einen Reiter "Lighthouse", dort kannst du automatische Tests ausführen lassen und einen Bericht erhalten, der konkrete Handlungsempfehlungen enthält.

  2. Tach!

    Include-Dateien werden erst geladen, wenn sie - abhängig von if-Entscheidungen - gebraucht werden anstatt pauschal bei Programmstart.

    Hast du den Unterschied gemessen und Ersparnis dabei festgestellt? Kennst du den Autoload-Mechanismus für Klassen?

    Mein PHP-Code ist sehr übersichtlich gegliedert, enthält Kommentare, Leerzeilen, Leerzeichen. Also viel Redundanz für den Interpreter.

    Dein Quellcode wird zunächst in OpCode übersetzt und dann erst ausgeführt. Dabei verschwinden alle nicht benötigten Dinge. Zudem gibt es den OpCode-Cache, der dafür sorgt, dass die Übersetzung nicht jedes Mal durchgeführt werden muss.

    Auf anderen schnellen Seiten (z.B. Google-HTML) ist sogar der Zeilenumbruch raus.

    HTML (und JS und CSS) muss zu Browser transportiert werden. Das Komprimieren dieser Daten erfolgt, um Datenvolumen und damit auch Zeit zu sparen.

    Wer hat sich schon mal mit dem Frisieren (Begriff aus dem Kfz-Tuning) von PHP und HTML beschäftigt? Wer kann Erfahrung zu PHP-Compilern beisteuern?

    Für PHP ist das unnötig. Für den Datenverkehr zwischen Server und Browser gibt es die Möglichkeit der Transportkomprimierung (gzip und deflate). Und wenn du darüber hinaus noch die unnötigen Teile weglassen möchtest, sieh dich nach Software mit Minifier oder Uglyfier im Namen um.

    Wundert mich, dass von Compilern keine Lese ist („keine Rede“ kann man hier ja nicht „sagen“).

    Ich kann dir nicht sagen, warum du davon nichts weißt, aber wann immer PHP in einer neuen Version erscheint, gibt es auch Informationen darüber, was geändert wurde. In dem Fall wurde der OpCode-Cache mit Version 5.5 zum Default-Bestandteil. Auch zuvor gab es mit APC und der Version 1 vom ZendOpcache eine optionale alternative Lösung. Das Kompilieren zu OpCode vor der Ausführung gibt es aber schon seit Version 4.

    dedlfix.

  3. Hallo Linuchs,

    PHP ist ein Interpiler. Oder Compreter? D.h. dein Sourcecode wird in Bytecode übersetzt, die dann von der ZEND Engine interpretiert werden (Standard-PHP, es gibt auch andere Engines, z.B. HHVM von Facebook).

    D.h. Leerzeichen und Kommentare kosten Zeit, ja, und zwar genau einmal, wenn die Bytecode-Übersetzung stattfindet.

    Bei einer FastCGI Installation ist es nun so, dass der FastCGI Prozess den Bytecode cached. D.h. die Compilierung in Bytecode findet genau einmal statt, und danach sind Spaces, Kommentare völlig egal.

    Die Länge von Variablennamen übrigens auch, man könnte ja meinen, durch Variablen wie $i und $j statt $eingabeIndex und $durchlauf Interpretationszeit einsparen zu können.

    Anders ist es, wenn PHP als einfacher CGI Prozess oder Apache Modul läuft. DANN wird für jeden Webrequest ein neuer Prozess gestartet, und neu compiliert. Ob es in diesen Szenarien auch Bytecode-Caches gibt, weiß ich nicht.

    Tools, mit denen Du den Bytecode vorcompilieren kannst, so dass der Sourcecode auf dem Server gar nicht mehr nötig ist und nur der Bytecode geladen werden muss, gibt es nicht so richtig. Facebook hatte mal HipHop, das haben sie durch HHVM ersetzt. HHVM verwendet Hack als Sprache (PHP Obermenge) und ist - wie PHP - ein Just-In-Time Compiler. Andere PHP "Compiler" compilieren für die .net Runtime. .net Assemblies sind ebenfalls Bytecode, werden aber immerhin zur Ausführung Just In Time in Maschinencode übersetzt.

    Also: Bei Tempoproblemem auf deiner Seite (die Du aber erstmal messen musst) kann die PHP Ausführung ein Problem sein. FastCGI wäre die anzustrebende Ausführungsart. Wenn Du isoliert testest, kommst Du mit Sicherheit nicht in Probleme mit Cache-Misses; aber bei einer sehr großen Seite und vielen Zugriffen kann es natürlich passieren, dass der FastCGI Cache nicht reicht und ständig Code verdrängt wird. Ich habe keine Ahnung, ob das irgendwo geloggt wird, aber wenn die Seite ab einer gewissen Userzahl signifikant in die Knie geht, könnte ein Cache-Überlauf eingetreten sein. Man kann dann versuchsweise den Cache vergrößern. FastCGI cached auch Sessions, auch das kann einen Einfluss haben.

    Abhilfe gegen Cache-Überlauf schafft weniger Code, d.h. refaktorieren des Codes, finden von Codewiederholungen und Auslagern in Funktionen, aber das ist viel Arbeit.

    In den meisten Fällen wird das aber nicht der Auslöser für eine langsame Seite sein. Deine Seite ist datenintensiv, d.h. du solltest mehr Augenmerk auf die DB Zugriffe legen und deren Laufzeit messen, so dass Du mit Indexen oder auch optimierten Zugriffen eingreifen kannst.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Tach!

      Bei einer FastCGI Installation [...]

      ... sollte man zu PHP-FPM wechseln. Das ist moderner und soll sich auch unter Last besser verhalten.

      dedlfix.

      1. Hallo dedlfix,

        na gut. Mein Test-PHP läuft unter Windows auf IIS, das ist sozusagen FPM für Arme. Darum hatte ich den nicht auf dem Schirm.

        Rolf

        --
        sumpsi - posui - obstruxi
    2. Tools, mit denen Du den Bytecode vorcompilieren kannst, so dass der Sourcecode auf dem Server gar nicht mehr nötig ist und nur der Bytecode geladen werden muss, gibt es nicht so richtig. Facebook hatte mal HipHop, das haben sie durch HHVM ersetzt. HHVM verwendet Hack als Sprache (PHP Obermenge) und ist - wie PHP - ein Just-In-Time Compiler.

      PHP ist ab Version 8 übrigens auch ein richtiger JIT-Compiler, der die OPCodes nicht ausschließlich interpretiert, sondern auch zur Laufzeit in Maschinencode kompilieren kann. Ich hab mir letzte Woche mal den Release-Candidate installiert, konnte aber noch keine Benchmarks machen; mich haben erstmal die neuen Sprach-Features interessiert.

  4. Lieber Linuchs,

    JavaScript im Browser, welches einen Request absetzen muss, um eine CSV-Datei zu erhalten, um aus deren Daten anschließend ein passendes DOM aufzubauen, ist prinzipbedingt langsamer, als die CSV-Datei von PHP auslesen zu lassen und ein passend generiertes DOM an den Browser zu versenden.

    ich habe den Ehrgeiz, extrem schnelle Webseiten zu erstellen.

    Aha...

    Ich bin mir sicher, dass ein HTML-Dokument von 500KB schneller übertragen ist, als eine eventuell kleinere CSV-Datei mit anschließendem clientseitigen Parsen und DOM-Erstellung. Dieser Download skaliert sogar besser, da die Datenübertragung komprimiert stattfinden kann, während die clientseitigen Berechnungen nicht "komprimiert" werden können und sogar mit steigender Datenmenge einigermaßen linear ansteigen.

    Da sind Kommentare im Quelltext echt nicht das, worüber Du Dir einen Kopf machen solltest! Parser ignorieren die im Allgemeinen sehr performant, weil das für fast alle Programmiersprachen ein längst gelöstes Problem ist.

    Liebe Grüße

    Felix Riesterer

    1. JavaScript im Browser, welches einen Request absetzen muss, um eine CSV-Datei zu erhalten, um aus deren Daten anschließend ein passendes DOM aufzubauen, ist prinzipbedingt langsamer, als die CSV-Datei von PHP auslesen zu lassen und ein passend generiertes DOM an den Browser zu versenden.

      Es ist auch lästiger für den Anwender, sofern zwar das Grundgerüst der Seite sofort da ist, sich die dann aber trotzdem spürbar stufenweise aufbaut.

      Zu sehen bei etwa freien Mailanbietern oder Zeitungsportalen usw. usw.
      Da ist die Überschrift, dann mal los… oh nochmal ein Werbeblock der alles nach unten schiebt… dann ändert sich die Schriftart und wieder sieht alles anders aus…

      Passiert sicherlich häufiger mit älteren Endgeräten, lahmer Internetleitung, wenn man CPU als auch Netz parallel mit etwas anderem beschäftigt. Aber es passiert eben und hinterlässt Eindruck.

      1. Hallo encoder,

        oh nochmal ein Werbeblock der alles nach unten schiebt…

        DA habe ich aber oft den Eindruck, dass das Vorsatz ist. Gerade auf Mobilgeräten, wo man durch Berühren und Schieben scrollt, ist schnell auch mal ein Klick passiert.

        Oder wenn dort, wo Links sind, bei Überfahren mit der Maus Werbung erscheint und die Links nach unten schiebt. Analog zu den Popups, die erscheinen, wenn man mit der Maus in Richtung Adresszeile fährt. Das ist Verhaltensbeobachtung des Anwenders, um abhängig davon Werbung zu platzieren und Werbeklicks zu generieren. Das kann nicht im Sinne der Anzeigenkunden sein.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Ich frage mich auch immer wieder, ob die Werbekunden deren immer gleiche Werbevideos man vor Videos sehen muss, das wirklich so wollen.
          Egal ob Youtube, 3-buchstabige Mailhosts mit leider des öfteren nur sehr seichten Videoinhalten oder andere Seiten.

  5. @@Linuchs

    ich habe den Ehrgeiz, extrem schnelle Webseiten zu erstellen.

    Guter Plan.

    • lange (interne) Listen werden als pure Daten im CSV-Format an den Browser gesendet. Ebenfalls ein minimales Javascript, das die Daten zu HTML-Positionen aufbereitet. Serverzeit bei 999 Adress-Positionen 0,45 sec., die üblichen 50 Positionen in 0.379 sec.

    Du rühmst dich mit der Entlastung deines Servers und bürdest die Rechenlast den Clients auf – und die verfügen größtenteils über weitaus weniger Rechenpower als dein Server. Schlechter Plan.

    Übertragung ist das eine; die Daten wollen auch dargestellt werden, und das geschieht bei einem serverseitig generierten HTML-Dokument wohl deutlich schneller als wenn das DOM mittels JavaScript clientseitig generiert wird.

    Wenn du extrem schnelle Webseiten willst, bist du auf dem falschen Weg.

    😷 LLAP

    --
    „Sag mir, wie Du Deine Maske trägst, und ich sage Dir, ob Du ein Idiot bist.“ —@Ann_Waeltin
    1. Übertragung ist das eine; die Daten wollen auch dargestellt werden, und das geschieht bei einem serverseitig generierten HTML-Dokument wohl deutlich schneller als wenn das DOM mittels JavaScript clientseitig generiert wird.

      Sehr guter Einwand! In Chrome kann man u.a. messen, wie lange es dauert bis der Hauptinhalt angezeigt wird (Largest Contentful Paint) und wie lange bis Benutzer*innen-Eingaben möglich sind (Time to Interactive). Die Werte sind m.M.n. wesentlich aussagekräftiger als das DOMContentLoaded- oder Onload-Event.

    2. Hallo Gunnar,

      Schlechter Plan.

      Ich möchte Dir ja zustimmen, aber ein Hinweis kribbelt mir doch in den Fingern.

      Die Qualität des Plans hängt an den Umständen. Wenn der Server auf dem letzten Loch pfeift, ist eine Auslagerung von Rechenzeit auf die Clients eine Möglichkeit, den Service aufrecht zu erhalten. Desktop-Clients sind auf jeden Fall schnell genug, und ich würde behaupten: die meisten Kleingerät-Clients sind es heutzutage auch.

      "Extrem schnelle Webseiten" sind natürlich ein anderer Anspruch, d.h. in dem Fall wäre die Alternative "zweiter Server" zu betrachten. Und die Frage zu prüfen, wo sich auf dem ersten Server eine Optimierung des vorhandenen Codes und der DB (Design+Queries) lohnen kann. Dafür braucht es gründliches Profiling.

      Der schnellste Server hilft aber nichts bei einer dünnen Leitung, und fertiges HTML bedeutet auch viel Datentransfer. Trotz GZIP Transfer ist ein reiner Datenbatzen kleiner als aufbereitetes HTML. Die vorher genannte JSON Idee konterkariert das etwas; wenn man da zwanghaft das Datenvolumen minimieren will, muss man wohl vor der JSON Serialisierung noch die Objekte in reine Arrays verwandeln, so dass [ 1, 2, "Foo", "Bar ] über die Leitung geht statt { id: 1, parentId: 2, name: "Foo", givenName: "Bar" }. Selbst die Objekt-Version ist immer noch viel kleiner als das HTML-Fragment für dieses Objekt. Keine Ahnung, wieviel Bytes man spart, aber 200 Bytes pro Satz sind bei 1000 Sätzen 200KiB. Auch da gilt jedoch: Messen, Übertragungsvolumen beobachten, und überlegen, was sich lohnt.

      Es dürfte auf jeden Fall effizienter sein, den JSON String ins HTML hineinzurendern statt ihn - wie ich vorher mal andeutete - per XMLHttpRequest separat abzuholen. Aber da hatte ich die Problemstellung anders verstanden.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Hi,

        Desktop-Clients sind auf jeden Fall schnell genug, und ich würde behaupten: die meisten Kleingerät-Clients sind es heutzutage auch.

        Rechenleistung auf Smartphones und Co zu verlagern heißt aber auch: deren Akku schneller zu entladen.

        cu,
        Andreas a/k/a MudGuard

        1. Desktop-Clients sind auf jeden Fall schnell genug, und ich würde behaupten: die meisten Kleingerät-Clients sind es heutzutage auch.

          Rechenleistung auf Smartphones und Co zu verlagern heißt aber auch: deren Akku schneller zu entladen.

          Ich weiß nicht, ob die zusätzlichen CPU-Zyklen den Akku stärker belasten als die längere Aktivität der Netzwerk-Karte. Bei GZip-Komprimierung lohnt es sich meist, die zusätzliche anfallende Arbeit für das Entpacken zugunsten des reduzierten Transfer-Volumens in Kauf zu nehmen. Zum Messen von Akkuladeständen gibt es leider kaum brauchbare Werkzeuge, korrigiert mich, wenn ich falsch liege. Performance-Optimierungen stehen leider mit vielen anderen Zielen in Konflikt. Prefetching, zum Beispiel, kann die gefühlte Performance enorm steigern, belastet aber auch die Netzwerk-Verbindung stärker. Maximale Performance zulasten aller anderer Ziele ist selten erstrebenswert. Man muss ständig abwägen und die Ziele priorisieren und vor allem ständig messen, messen, messen und vergleichen.

      2. Tach!

        Trotz GZIP Transfer ist ein reiner Datenbatzen kleiner als aufbereitetes HTML. Die vorher genannte JSON Idee konterkariert das etwas; wenn man da zwanghaft das Datenvolumen minimieren will, muss man wohl vor der JSON Serialisierung noch die Objekte in reine Arrays verwandeln, so dass [ 1, 2, "Foo", "Bar ] über die Leitung geht statt { id: 1, parentId: 2, name: "Foo", givenName: "Bar" }.

        Ich hab eben mal 100 Testdatensätze erstellen lassen und sie jeweils als Objekt und als Array über die Leitung geschickt. Unkomprimiert waren das 21KB und 15.5KB, komprimiert waren es 5.6KB und 5.4KB Das nimmt sich am Ende nicht mehr viel, die redundanten Eigenschaftsnamen werden anscheinend recht gut rausgekürzt.

        dedlfix.

        1. Hallo,

          Ich hab eben mal 100 Testdatensätze erstellen lassen und sie jeweils als Objekt und als Array über die Leitung geschickt. Unkomprimiert waren das 21KB und 15.5KB

          da hätte ich den Unterschied größer erwartet.

          komprimiert waren es 5.6KB und 5.4KB Das nimmt sich am Ende nicht mehr viel, die redundanten Eigenschaftsnamen werden anscheinend recht gut rausgekürzt.

          Ja, und das finde ich nicht so überraschend. Aber hast du auch eine Gegenüberstellung der auf der Server- und Clientseite anfallenden CPU-Last?
          Vermutlich nicht, aber ich halte den Aspekt durchaus auch für interessant.

          Live long and pros healthy,
           Martin

          --
          Paradox: Wieso heißen die Dinger Kühlkörper, obwohl sie höllisch heiß werden?
          1. Hallo,

            danke an dedlfix für die Mühe.

            155 Bytes pro Satz im Array oder 210 Bytes im Objekt, ok, klingt nach einer brauchbaren Simulation. Wie gut sie passt, muss Linuchs bewerten.

            Bei 1000 Sätzen sind es dann 210KB vs mutmaßlich 56KB. Das sollte auf einer DSL Leitung nur ein Zucken sein, auf einer 4G Funkstrecke auch. Merklich wird es nur auf einer Edge-Strecke sein.

            Ich habe mir gerade noch einen JSON String gezimmert, der ein Array mit 1000 Objekten darin enthält. Ca. 170K lang. JSON.parse braucht 1ms dafür. Ein handgemachter CSV-Parser (den ich jetzt nicht schreiben werde‼️) braucht garantiert länger.

            Rolf

            --
            sumpsi - posui - obstruxi