Jörg: Wo ist der Flaschenhals in miener Seite

Hallo,

ich habe eine Seite, die bis zu ihrem Erscheinen auf meinem Bildschirm recht lange braucht.

Ich habe zu begin und zum Ewnde der Seite eine Zeitmessung gesetzt und durch die Differenz der Beiden ermittle ich die gesamte Zeit der Seite.

Seltsamerweise braucht sie aber immer recht lange, wobei manchmal über 6 Sekunden gemessen wird und manchmal nur 0,20 Sekunden.

Dieses seltsame Verhaölren macht es m ir recht schwer, den Flaschenhals dieser Seite zu finden. Es mag an den Tabellen und Indices der hier betroffenen mysql-db liegen, es kann im php verborgen sein. Es sind aber zu viele Codezeilen, um jede Query oder ´jede php-Anweisung einzeln zu hinterfragen, bevor ich mich nicht in Richtung der Codepassage angenähert habe, die hier so stockt.

Könnt Ihr mir Tips geben, wie ich hier vorgehen kann? (insb., da die Seite in ihren Messreihen so große Unterschiede macht und die Seite dennoch meist lange benötigt)

Jörg

  1. Hello,

    der Flaschenhals steckt 1000%ig dort, wohin Du uns nicht gucken lässt.

    Ohne Input keine Tipps :-(

    Glück Auf
    Tom vom Berg

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.
  2. Hallo Jörg,

    was Du möchtest, ist profiling. Leider ist das in PHP nicht eingebaut.

    Lösung 1: Profiling "von Hand".

    Dafür verteilst Du an strategischen Stellen Funktionsaufrufe über den Code. Sowas wie profile_mark("Open DB") oder ein Pärchen wie

    $time = profile_begin("Fetch User");
    // do work
    profile_end("Fetch User", $time);
    

    Und diese Funktionen schreiben einen Timestamp und den Text in eine Logdatei. Die wertest Du nachher aus. profile_end würde noch die Zeitdifferenz zwischen aktueller Zeit und $time loggen.

    Wenn Du kein Profiling machst, kommentierst Du die Funktionsinhalte aus. Oder Du verwendest zwei verschiedene Include-Dateien: die eine included Dummys, die andere den echten Kram.

    Vorteil:

    • funktioniert mit Bordmitteln, keine Erweiterungen nötig

    Nachteil:

    • Viel Arbeit beim Verteilen der Funktionsaufrufe
    • geringer Overhead beim Nicht-Profilen

    Lösung 2: Einsatz eines echten Profilers

    Den muss man nur erstmal auf den Server bekommen - wenn Du das nicht darfst, hat sich diese Variante erledigt.

    https://stackoverflow.com/questions/21133/simplest-way-to-profile-a-php-script

    Hier wird vorgeschlagen, man könnte das mit xdebug machen.

    Sucht man im PHP Handbuch nach "profiling", findet man an dieser Stelle den Hinweis auf die PECL-Erweiterung XHProf (PECL, GitHub), die für PHP 7 und PHP 8 verfügbar ist.

    Ich hab's noch nie benutzt, ich weiß nicht, wie gut man das auf gehosteten Webspace bekommt. Good Luck 😉

    Vorteil:

    • Du brauchst maximal einen oder zwei Funktionsaufrufe im Code, um das Profiling ein- und auszuschalten
    • externe Profiler dürften keinen Overhead haben wenn sie nicht laufen

    Nachteil:

    • man muss sie erstmal zum Laufen bringen

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hello,

      [...]
      und man könnte diese Funktion standardmäßig in die Module einbauen und nur aktivieren, wenn ein dafür eigenes Debug-Flag gesetzt ist. Eventuell sollte man auch unterschiedliche Flags für unterschiedliche Funktionsgruppen vorsehen. (MVC, Router-Hierarchie, ...)

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
    2. Hallo Rolf,

      was Du möchtest, ist profiling. Leider ist das in PHP nicht eingebaut.

      Lösung 1: Profiling "von Hand".

      Ja, so könnte ichs machen.

      Nachteil:

      • Viel Arbeit beim Verteilen der Funktionsaufrufe

      Es geht.

      Mein größeres Problem ist eher, dass ich mich nicht wirklich auf den Server "verlassen kann". Soll heißen, die Messungen selber sind schlicht nicht reproduzierbar. Aber ok, vielleicht sind es die Tendenzen. Ich werde neben der absoluten zeit immer die Relation zur gesamten Scriptlaufzeit loggen.

      Lösung 2: Einsatz eines echten Profilers

      Den muss man nur erstmal auf den Server bekommen - wenn Du das nicht darfst, hat sich diese Variante erledigt.

      Ich fürchte, das hat sich erledigt. Ich habe zwar relativ guten Webspace, aber der ist shared und somit dürfte sich das erledigt haben.

      Jörg

      1. Hallo Jörg,

        Mein größeres Problem ist eher, dass ich mich nicht wirklich auf den Server "verlassen kann". Soll heißen, die Messungen selber sind schlicht nicht reproduzierbar

        Das ist natürlich Käse. Du hast aber Anfang-zu-Ende Messungen im PHP drin, und die liefern die monströsen Schwankungen. Das schließt schonmal eine Menge von infrastrukturellen Problemen aus:

        • Verbindungszeit Browser -> Server
        • Wakeup-Zeit Apache
        • Request Queueing (der Apache bzw. ein FastCGI Prozess kann nur eine gewisse Anzahl von PHP Requests gleichzeitig bedienen; ist es zu viel, müssen neue Requests warten)
        • Wakeup-Zeit PHP (welche es bei einem ordentlichen Server eh nicht geben sollte)
        • Verbindungszeit Server -> Browser

        Dann schriebst Du:

        Es sind aber zu viele Codezeilen, um jede Query oder ´jede php-Anweisung einzeln zu hinterfragen, bevor ich mich nicht in Richtung der Codepassage angenähert habe, die hier so stockt.

        Und das ist nun das Entscheidende. Du musst die Knackpunkte finden, und das geht nur mit Kenntnis deines Codes.

        Für das Sammeln der Profiling-Punkte solltest Du entweder ein globales Array anlegen, oder eine statische Klasse definieren (also eine Klasse ohne Instanz, die nur static-Methoden und -Properties enthält). Am Ende des Requests stoppst Du die Uhr und haust alle gesammelten Messpunkte in den Footer der Antwortseite. Das stellt sicher, dass Du nicht Messungen verschiedener Requests in einer Datei vermengst und die auseinander dröseln musst.

        Wenn Du einen Autoloader für Klassen verwendest und den im Sourcecode hast: Messpunkte für das Autoloading schreiben. "Load class XY" - "Class XY loaded"

        Hoffentlich holst Du Dir nur einmal eine DB-Verbindung. Vorher/Nachher.

        Und dann teile deinen Code grob in 2-3 gleichgroße Abschnitte und schreibe Messpunkte, wenn Du dahin kommst. Die Hoffnung wäre, dass Du einen Abschnitt findest auf den sich der Zeitverlust konzentriert.

        Bei einem Shared Hoster wäre meine Befürchtung allerdings, dass der Webserver oder der DB Server überlastet ist und generell zu langsam antwortet. Die overcommitten gerne ihr Blech und stellen nicht genug davon hin. Dann hilft nur meckern meckern meckern bei der Hotline - mit etwas Glück bekommst Du dann das Flag "sensibler user, nicht auf überlastete Server legen".

        Oder dein Hosting-Angebot ist zu billig. Wat nix kost, dat taugt nix 😉

        Rolf

        --
        sumpsi - posui - obstruxi
  3. Hallo Jörg,

    Könnt Ihr mir Tips geben, wie ich hier vorgehen kann? (insb., da die Seite in ihren Messreihen so große Unterschiede macht und die Seite dennoch meist lange benötigt)

    https://forum.selfhtml.org/self/2021/mar/04/css-performance-killer/1785253#m1785253

    Gruss
    Henry

    --
    Meine Meinung zu DSGVO & Co:
    „Principiis obsta. Sero medicina parata, cum mala per longas convaluere moras.“
    1. Hallo Henry,

      ich hatte Jörg so verstanden, dass er serverseitig die Laufzeit misst. Kann CSS da einen Einfluss haben?

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Hallo Rolf,

        ich hatte Jörg so verstanden, dass er serverseitig die Laufzeit misst. Kann CSS da einen Einfluss haben?

        nee. ging mir eh um diese seite: https://www.giftofspeed.com, wollte aber nicht nochmal schreiben 😉

        Gruss
        Henry

        --
        Meine Meinung zu DSGVO & Co:
        „Principiis obsta. Sero medicina parata, cum mala per longas convaluere moras.“
      2. ich hatte Jörg so verstanden, dass er serverseitig die Laufzeit misst. Kann CSS da einen Einfluss haben?

        Wenn man es mit ausreichend vielen ausreichend lang dauernden Datenbankabfragen zusammenbaut und/oder ausreichend viele ausreichend große base64-codierte Grafiken reinschiebt kann es das...

  4. wobei manchmal über 6 Sekunden gemessen wird und manchmal nur 0,20 Sekunden.

    Bevor ich messe würde ich nachdenken. Meine Glaskugel meint: Es kann „gut“ sein, dass bei den schnell laufenden SQL-Abfragen die Ergebnisse aus den Cache kommen (weil die der SQL-Server NOCH im Antwort-Cache hat), bei anderen ein Full-Table-Scan erforderlich wird. Und die sind „teuer“.

    1. Wie viele Datensätze hast Du denn so in den Tabellen? Weniger als 1000? Dann wechsle den Anbieter.

    2. Sind bei allen SQL-Anfragen die Spalten in WHERE, LIKE und ORDER-Klauseln indiziert?

    Helfer im Terminal:

    cd /var/www/htdocs;
    grep -RnPi "WHERE |LIKE |ORDER ";
    

    Wenn ja

    1. Gibt es Zugriffe, bei denen etwas wie LIKE "%foo" oder "%foo%" verwendet wird?
    cd /var/www/htdocs;
    grep -Rni "LIKE.*%";
    

    Wenn ja:

    Stelle sicher, dass die Tabellen im InnoDB- oder MyIsam-format gespeichert sind und Lese hier und erzeuge einen FULLTEXT-Index. - oder hier: https://dev.mysql.com/doc/refman/5.6/en/fulltext-search.html

    NUR wenn das nicht geht:

    Baue eine Spalte foo_DESC, mach ein

    UPDATE tabelle set `foo_DESC` REVERSE(`foo`);
    

    und dann statt

    SELECT foo, bar, baz FROM tabelle WHERE foo LIKE "%str";
    

    eben

    SELECT `foo`, `bar`, `baz` FROM tabelle WHERE `foo_DESC` LIKE "str%";
    

    Du musst dann künftig jeden Neueintrag auch passend behandeln, alsofoo_DESC mit dem REVERSE($string) befüllen...

    Bei etwas wie

    SELECT foo, bar, baz FROM tabelle WHERE foo LIKE "%str%";
    

    musst Du ohne Fulltext-Index leider mit dem Problem leben.

    1. SELECT `foo`, `bar`, `baz` FROM tabelle WHERE `foo_DESC` LIKE "str%";
      

      so liefert es die erwarteten Ergebnisse:

      SELECT `foo`, `bar`, `baz` FROM `tabelle` WHERE `foo_DESC` LIKE REVERSE("str%");