Hier im Forum bekommt ihr bei euren fragen schnelle hilfe.Hier geht es rund um das Web SeitenProgrammieren.Alles rund ums Javascript,Html,Php,Css und Sql.Auf fast allen Fragen haben wir eine Antwort.
Der Soforthilfe-chat verspricht das ,was sein Name sagt. Hier sind Leute Online die sofort ihre hilfe anbieten.Seht in der OnlineListe nach und wenn einer Online ist werdet ihr auch antwort bekommen. Admine ,Moderatoren und Helfer sind unsere Spezialisten in Sachen Web Programierung

Sie sind hier : sebastian1012.bplaced.net/ homepage-neu / kreuz-und-quer / index.php

Random Themen, Tutorials und co

24.09.2019 21:36:13

Nachtest zu gepufferten und ungepufferten SQL-Abfragen

Aufgrund eines fatalen Denkfehlers im letzten Beitrag schiebe ich hier nochmal einen korrigierenden Nachtest nach. Es geht wieder um gepuffertes vs. ungepuffertes Ausführen von SQL-Querys.

Das letzte Mal hab ich eine völlig ungeeignete Abfrage verwendet:

  1. Sie war zu schnell für einen Vergleich bzw. eine eindeutige Entscheidung, ob mysql_unbuffered_query() Sinn macht
  2. Sie enthielt ein ORDER BY, was nicht clever ist, denn MySQL weiß ja auch erst nach Behandlung aller Datensätze, welcher der größte bzw. kleinste Wert ist.

Außerdem habe ich beim letzten Mal lediglich die Gesamtausführungszeit gemessen, was auch sinnfrei war.

Jedenfalls hab ich diesmal eine wesentlich komplexere Abfrage verwendet. Mangels ordentlicher Testdatenbank, mit der ich lange dauernde Querys erzeugen kann (habs einfach nicht gebacken bekommen, eine Abfrage zu schreiben, die 1 – 5 Sekunden dauert – entweder wars im Zehntelsekunden-Bereich oder im Minutenbereich) habe ich folgende, völlig sinnfreie Abfrage verwendet.

SELECT *  FROM `tabelle` a1  INNER JOIN tabelle a2 ON a1.ID=a2.ID  INNER JOIN kategorien ON a1.kategorien_ID=kategorien.ID  WHERE RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5

Mir war es wichtig, eine Anfrage zu wählen, die so beschaffen ist, dass die Prüfung eines einzlenen Datensatzes genügt, um zu bestimmen, ob er zum Result-Set gehört oder nicht. Dies wäre mit ORDER BY oder GROUP BY nicht möglich.
Die eigentliche Abfrage ist aber völlig egal, denn ihr habt bestimmt auch „richtige“ Abfragen, die einige Sekunden dauern. Grundsätzlich sei an dieser Stelle schon mal gesagt: Je länger eine Abfrage dauert (Gesamtzeit laut PHPMyAdmin), desto sinnvoller ist der Einsatz von mysql_unbuffered_query().

Jedenfalls habe ich mit obiger Query die gepufferte Abfrage-Variante getestet, also mysql_query(). Dabei kam ich auf folgendes Ergebnis:
Zeit bis zur Verarbeitung des ersten Datensatzes: 5.005784034729 s
Zeit bis zur verarbeitung aller Datensätze: 5.0073800086975 s

mysql_unbuffered_query() im Gegenzug kommt auf dieses Ergebnis:
Zeit bis zur Verarbeitung des ersten Datensatzes: 0.24099016189575 s
Zeit bis zur verarbeitung aller Datensätze: 5.2411839962006 s

Leider konnte ich für diesen Test nicht mein Benchmark-Script nutzen, da ich damit ja nur die Gesamtbearbeitungszeit (basierend auf den Ergebnissen des Apache Benchmark-Tools ab.exe) herausbekomme. Das ist auch der Grund, warum es diesmal keine hübsche, bunte Tabelle gibt – ich hoffe die blanken Zahlen reichen diesmal.

Nun also zur Auswertung:
Der Unterschied in der Gesamtzeit ist irrelevant, weil ich wie gesagt nur per microtime() messen konnte. Die Aussage, dass die ungepufferte Version grundsätzlich langsamer sei, würde ich so nicht unterschrieben wollen.
Viel interessanter ist aber der riesige Unterschied bis zur Bearbeitung des ersten Datensatzes. Und genau das ist ja das Ziel von mysql_unbuffered_query(), wie ich dank Tims Kommentar nun auch begriffen habe ;-). Das bedeutet, dass die Bearbeitung durch PHP bereits sehr viel früher beginnen kann.

Also funktionierts doch hervorragend. Eines muss allerdings gesagt werden: mysql_unbuffered_query() macht keinen Sinn bei Querys mit ORDER BY, da sind die Ausführungszeiten zwischen mysql_query() und mysql_unbuffered_query() gleich (Grund: MySQL scheint Quicksort zu verwenden. Mit Heapsort wäre ein Geschwindigkeitsunterschied durchaus möglich).
Außerdem möchte ich noch sagen, dass ungepufferte SQL-Behandlung auch nur sinnvoll ist, wenn die Verarbeitung des Datensatzes schneller geht als die Ermittlung des nachfolgenden im Result-Set. Ansonsten muss MySQL zwangsläufig puffern, weil die Anwendung nicht hinterherkommt.

Außerdem ist wichtig, dass die Wirkung des schnelleren Ausgebens durch PHP verpufft (zumindest merkt der Client davon nix), wenn ihr die Ausgabe durch PHP puffert (z.B. um den Output zu gzippen). Deshalb ists für mich wohl uninteressant, aber ich wollte euch trotzdem die Möglichkeit aufzeigen, die MySQL und PHP da bieten.

Falls Rechtschreibfehler in diesem Beitrag entdeckt werden, bitte ich dies zu entschuldigen. Ich wollte den Test und den Bericht möglichst schnell veröffentlichen, damit ihr nicht von dem vorigen Beitrag verstimmt werdet.

php

24.09.2019 06:37:21

Volltextsuche in MySQL-Datenbanken

Dieser Beitrag zeigt, welche Möglichkeiten MySQL zur Suche in Texten bietet und wie man diese justieren kann, um optimale Suchergebnisse zu erhalten. Dies ist ein Gastbeitrag von Robert Westenkirchner.

Am Beispiel der Open Source Technologien

  • MySQL (Datenbank / Backend)
  • PHP (Middleware / Klebstoff zwischen Front- und Backend)
  • XHTML als Front-End.

Lernziele:

  • Verständnis für das Gebiet Volltextsuche gewinnen
  • Volltextsuche mit allereinfachsten Mitteln realisieren: LIKE %suchwort% (Performance: niedrig)
  • Volltextsuche mit internem MySQL Volltext realisieren MATCH AGAINST (Performance: mittel / skaliert mittelmäßig)
  • Professionelle Volltextsuche (wie Suchmaschine) mit drei Extra-Tabellen kennenlernen: 1. Suchwort-Tabelle 2. Suchindex-Tabelle (Verknüpfung Suchwort mit Fundstelle / Viele-zu-Viele Beziehung) 3. Such-Zeitstempel Tabelle für Neu-Indizierung bei Änderungen der Artikel (Performance: hoch) – skaliert gut

Was ist eine Volltextsuche? (Variante I)

Eine Volltextsuche findet beispielsweise die drei Suchwörter „Berlin London Paris“ wenn die Wörter nicht nur genau in dieser Reihenfolge im Text vorkommen, sondern jeweils an beliebiger Stelle im Text vorkommen. Eine Einwortsuche kann simpel mit

SELECT *   FROM articles WHERE body LIKE %Berlin%

realisiert werden.

Wollen wir nun nach „Berlin London Paris“ suchen, dann müssen wir den Suchstring in Einzelwörter zerlegen und obige LIKE Abfrage dreimal für die jeweiligen Suchwörter durchführen. Das ist nicht sehr performant, da für jedes Suchwort eine Abfrage durchgeführt werden muss und wird an dieser Stelle nicht weiter vertieft. Kommen wir lieber zur zweiten, interessanten Methode: MATCH AGAINST

Volltextsuche mit MySQL Bordmitteln (Variante II)

steht nur in Tabellen von Typ MyISAM zur Verfügung. Damit kann man sehr schön nach Wörtern in Textblöcken suchen, z.B. in Artikeln.

Mit dem folgenden SQL-Code erstellst Du eine MyISAM-Tabelle mit Volltextindex:

CREATE TABLE article (  articleID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) , url VARCHAR(255), body text, fulltext (title, body, url) );

Die Volltextsuche führst Du wie folgt aus:

SELECT title FROM article WHERE MATCH (title, body, url) AGAINST ('Berlin London Paris');

Beachte, dass wir alle Artikel finden, die mindestens eines der Wörter enthalten. Der Artikel muss nicht alle drei Wörter enthalten.

Leider unterstützt MySQL gegenwärtig keine Wortstämme. Die Suche nach „Ausgehen“ und „Ausgang“ müssten wir separat durchführen. Die Wortstammunterstützung ist eine Technik, die in vielen anderen Volltext-Suchsystemen implementiert ist. Sie kann verschiedene Wortabwandlungen eines Stammwortes wie oben beschrieben erkennen.

Ranking optimieren

Jedem gefundenen Treffer wird ein Relevanzwert zugeordnet. Die Ergebnisse können dann in Übereinstimmung mit diesem Relevanzwert automatisch sortiert werden. In der von mir entwickelten www.LinkMatrix.de kommt die MySQL Volltextsuche zum Einsatz, allerdings das automatische Sortieren der Treffer war nicht zufriedenstellend, außerdem hat man darauf kaum Einfluss. In der Regel hat man aber eine genaue Vorstellung welche Ergebnisse zuerst ausgegeben werden sollen.

Beispielsweise soll die Suche nach „PHP“ die Seite www.php.net weit vorne liegen und nicht Seiten, die sehr häufig das Suchwort PHP im Artikeltext beinhalten.

Die Reihenfolge, also das Ranking, ist sehr wichtig für die Zufriedenheit der Anwender.
In einem Online-Shop oder bei einer Auktions-Plattform soll bei den Treffern möglichst der Artikel vorne liegen, den der Anwender mit seinen Suchwörtern finden will. Das ist eine Kunst für sich, dazu werden viele Doktorarbeiten geschrieben.

Daher solltest Du das Ranking optimieren. Ein Artikel, der das Suchwort in der URL oder im Titel beinhaltet wird höher gerankt als wenn das Suchwort nur im Text vorkommt. Ganz simpel kann man das Ranking mit nem Punktesystem ermitteln, z.B.:

  • 200 Punkte für Vorkommen des Suchwortes in der URL
  • 50 Punkte für Vorkommen des Suchwortes im Titel
  • 10 Punkte für Vorkommen des Suchwortes im Fließtext (body)

Vorgehensweise:
Erstmal lässt Du Dir irgendeinen Algorithmus wie oben beschrieben einfallen und dann testet Du diesen auf Deinem Datenbestand und guckst, ob das so hinhaut. Meist muss man das immer wieder optimieren. Die erfolgreichste Suchmaschine der Welt, Google verbesserte die Qualität des Rankings seiner Treffer dramatisch durch diesen einfachen Trick:
Wie macht man das in der Wissenschaft? Welcher Artikel ist relevant, ist wertvoll? Mit dem das meiste Geld verdient wurde? Nein. Der Wert der Veröffentlichungen von Wissenschaftlern misst sich einfach daran, wie oft dieser zitiert wird. Auch wie bedeutend der jenige ist, der ihn zitiert hat. Das hat sich Google abgeguckt und die Idee aufs Netz erfolgreich übertragen.

Einschränkungen bei der MySQL-Volltextsuche

Leider findet man mit obiger Methode nur Wörter, die vier oder mehr Buchstaben haben. Wenn man nach „Rom“ sucht, gibts immer null Treffer, auch wenn Rom in Tabellenfeldern vorkommt.

Lösungsvarianten für kurze Worte

Kurze Worte werden nicht indiziert, d.h. Worte mit weniger als vier Zeichen werden standardmäßig ignoriert. Kurze Wörter wie die Abkürzungen PHP oder XML sind aber wichtig für das Suchen, daher muss man die MySQL-Volltextsuche an der Stelle verbessern.

Lösungsvariante I:
– mit der Variablen ft_min_word_len kann man diese Beschränkung überwinden.

Lösungsvariante II:
– bei LinkMatrix hab ich einfach auf die bereits oben beschriebene Suche mit LIKE zurückgegriffen. Sicherlich gibts noch weitere, bessere Lösungsvarianten.

Performance steigern durch Stop-Wörter

Volltextindizes verwenden Stop-Wörter. Ein Stop-Wort ist ein Wort ohne semantische Bedeutung. Normalerweise handelt es sich dabei um häufige Wörter, die für den Satzaufbau wichtig, aber in der Regel bedeutungslos für die Suche sind. „bald“ „sehr“ „ganz“ „oder“ sind für die Suche ungeeignet. MySQL enthält hierfür nicht nur Standard-Wortlisten, sondern bietet auch die Möglichkeit, für die jeweils benötigte Sprache eigene Stop-Wortlisten zu erstellen.

Leistungseinbußen bei sehr großen Tabellen

Laut MySQL ist die Performance der eingebauten Volltextsuche bis 1.000.000 Zeilen gut einsetzbar. Bei kleinen Anwendungen wird dies keine Probleme bereiten – bei größeren jedoch solltest Du diese Tatsache im Hinterkopf behalten.

Hierfür ist die dritte Lösungsvariante am besten geeignet, allerdings ist diese auch mit mehr Aufwand verbunden, weil das was MySQL mit der eingebauten Volltextsuche für einen erledigt, muss man sich hier selber kümmern:

Professionelle Volltextsuche wie bei Suchmaschinen mit drei Extra-Tabellen (Variante III)

Volltextsuche auf Basis eines Indizes mit allen Suchworten. Bei der vorherigen eingebauten MySQL Volltextsuche war das beim Erstellen der Tabelle mit dem Zusatz „fulltext“ erledigt. Jetzt müssen wir uns da selber drum kümmern, gewinnen dadurch aber mehr Einfluss.

Nach diesem Prinzip arbeiten alle Suchmaschinen: Ein Crawler, Spider oder auch Bot (von Roboter) genannte Software hangelt sich von Link zu Link durchs Netz und sammelt alle Texte ein, die er findet und schreibt sie in eine Datenbank. Wir gehen davon aus, das wir schon eine Datenbank mit Inhalten haben und für diese bauen wir uns eine maßgeschneiderte Volltextsuche.

Volltextsuche mit drei Zusatz-Tabellen

        +-------------------------+     +-----------------------+        |                         |     |                       |         |                         v     v                       |     +-----+----------+   +----+-------------------+   +---+------------------+    | swid| Suchwort |   | id | swid doc_id count |   | id| doc_id timestamp |    +-----+----------+   +----+-------------------+   +---+------------------+    |     |          |   |    |                   |   |   |                  |    | 1   | Berlin   |   |  1 |   3    23      3  |   | 1 | 23   2009-01-22  |    | 2   | London   |   |  2 |   1    17      1  |   | 2 | 17   2009-01-22  |    | 3   | Paris    |   |  3 |   2    12      1  |   | 3 | 12   2009-01-22  |    |     |          |   |    |                   |   |   |                  |    +-----+----------+   +----+-------------------+   +---+------------------+    1. Suchwort-Tabelle     2. Such-Index-Tabelle     3. Such-Zeitstempel Tab. 

Zu der vorhanden Artikel Tabelle brauchen wir drei zusätzliche Tabellen:

  1. Suchwort-Tabelle (mit allen Suchworten wie Berlin, London, Paris – Stop-Worte wie „oder“ kommen nicht in die Suchwort-Tabelle)
  2. Such-Index-Tabelle (enthält die Verknüpfung zwischen den Suchworten und den Tabellen-Zeilen, in denen die Suchworte enthalten sind)
  3. Such-Zeitstempel-Tabelle (wann wurde diese Dokument das letzte Mal indiziert)

Da das schon im Netz ausführlich auf www.phpbar.de [1] beschrieben wurde, verweise ich da nur drauf.

0256-food-hot_dog

Zusammenfassung

  • Verständnis für das Gebiet Volltextsuche gewonnen
  • Volltextsuche mit allereinfachsten Mitteln realisiert
  • Volltextsuche mit internem MySQL Volltext realisiert
  • Professionelle Volltextsuche (wie Suchmaschine) mit drei Extra-Tabellen kennengelernt

Viel Spaß beim kreativen Experimentieren!

Weiterführende Informationen zum Thema Volltextsuche:

[1] http://www.phpbar.de/w/Volltextsuche
[2] Welling, L.; Thomson, L.: „MySQL Tutorial – Die kompakte Einführung in die Arbeit mit MySQL“, Reihe MySQL Press, Addison-Wesley (Pearson Education) München, 2004
Seite 155-158: Volltextsuche in MyISAM-Tabellen
[3] Zawodny, J., Balling D.: „High Performance MySQL – Optimierung, Datensicherung, Replikation & Lastverteilung“, O’Reilly Koelln, 2005
[4] http://www.linkmatrix.de/volltextsuche

Robert Westenkirchner
Creative Commons License
Such, Bello, such! 3 x Volltextsuche in Datenbanken von Robert Westenkirchner steht unter einer Creative Commons Namensnennung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Über diese Lizenz hinausgehende Erlaubnisse erhalten Sie möglicherweise unter www.linkmatrix.de.

datenbank

10.11.2019 04:39:47

Optimierungen von CSS und JavaScript on-the-fly

In meinem letzten Beitrag habe ich einige Möglichkeiten aufgezeigt, wie man HTML-, CSS und JavaScript-Code verkleinern kann ohne die Darstellung und Funktionalität zu beeinträchtigen. Wenn man jedoch keine Lust hat, immer nach einem Bearbeiten die Optimierungen wieder vorzunehmen, hab ich hier eine praktikablere Lösung. Außerdem wollen wir zusätzlich die Komprimierung der Daten einsetzen sowie das Client-side Caching einsetzen.
Das Ziel soll also eine Lösung sein, bei der wir mit unseren wohlstrukturierten Dateien weiterarbeiten können, aber trotzdem sollen möglichst wenig Daten vom Client geladen werden müssen.

Warum wollen wir eigentlich um jeden Preis die zu ladenden Daten so klein wie möglich halten? Es gibt 2 Gründe:

  • schnellerer Seitenaufbau: jedes Byte, was nicht geladen werden muss, beschleunigt den Seitenaufbau beim anfordernden besucher
  • geringere Kosten: oft ist das Trafficvolumen beim Webhosting oder Server begrenzt bzw. wird immer teurer je mehr Daten an den Client geschickt werden

Wenn wir es also schaffen, mit wenig Aufwand die Datenmenge zu verringern, können wir sowohl etas für unsere User als auch für unseren Geldbeutel tun.

Zuerst schauen wir uns CSS-Dateien an. Ich habe bereits bei Projekten mitgemacht, bei denen die CSS-Datei 40 kB groß war. Schönen Gruß an die 56k-Modem-User! Wir gehen hier natürlich davon aus, dass in der CSS-Datei nur wirklich genutzte Klassen und Definitionen enthalten sind – ansonsten wirkt dieser Tuningversuch lächerlich für das Projekt. Gut, wir nehmen und als Beispiel eine CSS-Datei mit einer Definition für das DIV-HTML-Element. Natürlich haben wir Multiformateigenschaften genutzt, um dadurch schon mal einige Bytes zu sparen, denn dabei geht die Übersicht auf keinen Fall verloren.

div {   font:bold 0.9em/12px Arial; /* fett groesse/zeilenabstand schriftart */   border:solid 1px red; /* typ breite farbe */   background:url(images/bild.jpg) top repeat-x; /* url position wiederholung */ }

Diese CSS-Datei kann nun nur noch durch 2 Dinge optimiert werden: Entfernung von Zeilenumbrüchen und Entfernen der Kommentare. Allerdings kann man mit der entstehenden 1-Zeilen-Datei später kaum noch arbeiten, deshalb wäre es doch toll, wenn diese Optimierungen zwar gemacht würden, wir uns aber nicht darum kümmern müssten.
Wir brauchen demzufolge ein Lösung, die on-the-fly die optimierte CSS-Datei erstellt und an den Client schickt. Um überhaupt an der Datei etwas ändern zu können, bevor sie geladen wird, brauchen wir erstmal PHP bzw. dessen Output Buffering. Dazu lassen wir unsere CSS-Datei(en) durch den PHP-Parser laufen. Das legt man über einen zusätzlichen Eintrag in der .htaccess fest (wenn diese Datei im root ihres Webprojekts noch nicht existiert, legen sie sie einfach an).
AddType application/x-httpd-php .css
Eine andere PHP über das Stylesheet schicken zu können, wäre die .css-Datei in .php umzubenennen, aber dann müssten alle Referenzierungen in der Anwendung umgeschrieben werden.

Was tun wir nun damit? Wir packen den PHP Output Buffer mit einer eigenen Callback-Funktion in die CSS-Datei. Die Callbackfunktion wird aufgerufen, nachdem die gesamte Dateiausgabe feststeht (der eigentliche CSS-Code geladen wurde), und erhält als Parameter genau diesen CSS-Code als String.

<?php header("Content-type: text/css"); ob_start("compress"); header ("content-type: text/javascript"); header ("cache-control: must-revalidate; max-age: 3600"); header ("expires: " . gmdate ("D, d M Y H:i:s", time() + 3600) . " GMT");   function compress($buffer) {   // remove comments   $buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);   // remove tabs, newlines, etc.   $buffer = str_replace(array("\r\n", "\r", "\n", "\t"), '', $buffer);   //remove multiple spaces   $buffer = preg_replace('/\s\s+/', ' ', $buffer);   return $buffer; } ?> div {   font:bold 0.9em/12px Arial; /* fett groesse/zeilenabstand schriftart */   border:solid 1px res; /* typ breite farbe */   background:url(images/bild.jpg) top repeat-x; /* url position wiederholung */ } <?php ob_end_flush(); ?>

Die Funktion compress ist unsere Callback-Funktion. Wir bearbeiten den Buffer (den CSS-Code) durch das Entfernen von Kommentaren, Zeilenumbrüchen und Tabulatoren sowie mehrfachen Leerzeichen. Dadurch wird folgende CSS-Datei im Endeffekt wirklich an den Client geschickt:

div { font:bold 0.9em/12px Arial; border:solid 1px red; background:url(images/bild.jpg) top repeat-x; }

Durch diese recht trivialen Änderungen konnte die ursprüngliche Größe von 212 Bytes auf jetzt nur noch 103 Bytes verkleinert werden. Das sind über 50% weniger Daten! Und wenn man es mit der CSS-Datei vergleicht, bevor man Multiformateigenschaften genutzt hat (wenn man diese auflöst und alle Einzeleigenschaften aufschreibt kommt man auf 358 Bytes), beträgt die Speicherplatzeinsparung sogar über 70%!

Mit JavaScript-Dateien können wir ähnlich verfahren. Wir fügen die Endung .js zur .htaccess hinzu, damit sie vom PHP Parser verarbeitet wird. Bei JavaScript sind die gleichen Formatierungen möglich, nur müssen wir zusätzlich die einzeiligen Kommentare entfernen, da es sonst zu Problemen beim Entfernen von Zeilenumbrüchen kommen kann. Unsere Callback-Funktion sieht bei js-Dateien also so aus:

function function compress($buffer) {   // remove comments   $buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);   $buffer = preg_replace('!//[^\n\r]*!', '', $buffer);   /*   Konstrukte wie    var variable = {     var1:"test",     var2:function() {       doSomething();     }   }   müssen nach der letzten schließenden Klammer ein Semikolon bekommen --> funktioniert nicht   */   //$buffer = preg_replace('/var ([^=]*) = \{(([^\}]*\})*)[\n\r]+/', "var ".'$1'." = {".'$2'.";", $buffer);   // remove tabs, spaces, newlines, etc. - funktioniert nicht, weil das vorhergehende nicht funktioniert   //$buffer = str_replace(array("\r", "\n", "\t"), "", $buffer);   $buffer = str_replace("\t", "", $buffer);   // multiple whitespaces   $buffer = preg_replace('/(\n)\n+/', '$1', $buffer);   $buffer = preg_replace('/(\n)\ +/', '$1', $buffer);   $buffer = preg_replace('/(\r)\r+/', '$1', $buffer);   $buffer = preg_replace('/(\r\n)(\r\n)+/', '$1', $buffer);   $buffer = preg_replace('/(\ )\ +/', '$1', $buffer);   return $buffer; }

In JavaScript gibt es komplexe Konstrukt (welche genau, steht im Kommentar im Quellcode), die ich versuche, durch ein Semikolon zu ergänzen. Leider funktioniert das nicht richtig. Aus diesem Grund habe ich auch nicht alle Zeilenumbrüche entfernt, denn sonst kommt es da zu Fehlern. Trotzdem bringt diese Funktion einiges an eingespartem Traffic.
Bei einem von mir geschriebenen Script zur Darstellung der Tooltips auf SucheBiete.com brachte diese Veränderung eine Einsparung von ca 20 % (Original: 3,24 kB, optimiert: 2,65 kB). Bei viel kommentierten Scripten wie beispielsweise Lightbox konnte ich sogar etwa 40 % einsparen (Original: 22,9 kB, optimiert: 13,8 kB).
Trotzdem muss ich sagen, dass es mit einigen JavaScripts Probleme gibt, beispielsweise mit der Bibliothek Prototype, da darin in Strings ‚/*‘ und ‚//‘ vorkommen. Man sollte also überprüfen, ob es nach dem Einbau JavaScript-Fehlermeldungen gibt.

Wenn diese On-the-fly-Optimierungen durchgeführt wurden, kann man die entstandenen Code dann noch als GZip senden, was die Größe des optimierten Codes auf ca ein Drittel zusammenpackt. Außerdem habe ich noch eine Cache-Control eingebaut, damit die Datei nicht jedes mal vom selben User erneut geladen wird.
Für CSS:

header("Content-type: text/css"); header ("cache-control: must-revalidate; max-age: 2592000"); header ("expires: " . gmdate ("D, d M Y H:i:s", time() + 2592000) . " GMT"); ob_start("compress"); function compress($buffer) {   // remove comments   $buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);   // remove tabs, spaces, newlines, etc.   $buffer = str_replace(array("\r\n", "\r", "\n", "\t"), '', $buffer);   $buffer = preg_replace('/\s\s+/', ' ', $buffer);   if (stripos($_SERVER["HTTP_ACCEPT_ENCODING"],'x-gzip') !== false) {     header("Content-encoding:x-gzip");     $buffer = gzencode($buffer);   }   elseif (stripos($_SERVER["HTTP_ACCEPT_ENCODING"],'gzip') !== false) {     header("Content-encoding:gzip");     $buffer = gzencode($buffer);   }   elseif (stripos($_SERVER["HTTP_ACCEPT_ENCODING"],'deflate') !== false) {     header("Content-encoding:deflate");     $buffer = gzdeflate($buffer);   }   header('Content-Length: ' . strlen($buffer));   return $buffer; }

Entsprechend funktioniert es auch für JavaScript (außer eben mit den oben genannten Replaces). Wer eine js-Datei hat, die durch die angegebene Funktion nicht optimiert werden kann, weil dadurch Fehler entstehen, sollte zumindest gzippen. Wenn man aber nix mehr mit dem Buffer vorhat, reicht auch ob_start("ob_gzhandler");.

Durch diese kleinen Eingriffe lassen sich also durchaus ohne viel Aufwand (Vorsicht, Untertreibung) einige Bytes an Trafficvolumen einsparen.
An die On-the-fly-Optimierung von HTML-Code traue ich mich im Moment nicht so recht ran, weil das mittlerweile nicht mehr sauber ist. Erstens haben viele Seiten nicht valides HTML und zweitens erschweren Geschichten wie Conditional Comments und vorformatierte Bereiche (z.B. in Textareas und <pre>-Abschnitten) das Optimieren.

javascript

24.09.2019 21:36:22

PHP Code in Kommentaren

Heute nur ein kurzer Beitrag zu diesem Blog. Da viele schon die Erfahrung gemacht haben, dass es gar nicht so einfach ist hier in den Kommentaren Beispiel-Code zu posten, möchte ich hier kurz einige Tipps geben, wie es trotzdem funktioniert.

Das Problem ist, dass WordPress (die hier benutzte Blog-Software) in erster Linie für „normale“ Blogs gemacht ist, wo Kommentare nur in ganzen Sätzen geschrieben werden. Bei PHP Performance ist aber oft Code nötig, um Anmerkungen zu machen oder zusätzliche Informationen zu geben. Und dafür muss eine Lösung gefunden werden!
Weil es allerdings in regelmäßigen Abständen Updates für WordPress gibt, möchte ich nicht am Code rumpfuschen, denn dann müsste ich beim nächsten Update diese Arbeit wiederholen (und zwar bei jedem Update).

Problemlage ist folgende: WordPress versucht spitze Klammern als HTML-Tags zu verstehen, kommt damit aber nicht klar, wenn diese Klammern eigentlich für „größer als“ und „kleiner als“ stehen. Die Lösung für dieses Problem ist, dass man einfach HTML-Entities benutzt:
< wird zu &lt;
> wird zu &gt;

Damit wird der Code auch in den Kommentaren korrekt dargestellt.
Und nun hoffe ich einfach mal, dass genug Leute diesen Kommentar lesen, damit das Kommentieren zukünfig leichter fällt und nicht mehr so viel auf WordPress geschimpft wird.

PS: Wer ein Plugin kennt, der dieses Problem umgeht, darf es hier gern in den Kommentaren schreiben.

php

24.09.2019 21:36:33

Variablen in String einbetten oder konkatenieren

In PHP kann man Variablen auf zwei unterschiedliche Arten in einen String einbauen.

$zeit = time(); // Variable im String echo "Der aktuelle UNIX-Timestamp ist $zeit.";   // Variable konkateniert echo "Der aktuelle UNIX-Timestamp ist ".$zeit.".";

Ich finde die zweite Variante sauberer, da sie die Variablen eindeutig vom String trennt, trotzdem möchte ich aber – nicht zuletzt angestoßen durch soulrebels Beitrag im Forum mal untersuchen, inwiefern sich beide Arten in der Performance unterscheiden.

Ich will nicht lang drumrumreden, sondern direkt das Ergebnis präsentieren:

Datei Gesamtlaufzeit durchschnittliche Laufzeit pro Durchlauf Verhältnis zur schnellsten Variante
var_out_of_ string.php 27.359341 s 2.736 ms 100%
var_out_of_ string_singlequote.php 27.759916 s 2.776 ms 101 % (+ 1%)
var_in_string.php 29.492408 s 2.949 ms 108 % (+ 8%)

Die Konkatenierungsvariante ist also nicht nur sauberer sondern auch schneller (wenn es auch kein wirklich großer Unterschied ist).

Nun, wodurch kommt das? Es ist nur eine Mutmaßung, aber ich kann mir vorstellen, dass jedes Zeichen des Strings einzeln untersucht werden muss und dadurch das Parsen mit einem regulären Ausdruck beim direkten Einbetten in den String länger dauert, denn der Parser weiß ja, wenn er auf $z stößt, erstmal noch nicht, ob der Variablenname danach noch weiter geht. Deshalb holt er sich Stück für Stück noch das e, i und t und stößt danach auf einen Punkt (.), der in PHP-Variablennamen nicht vorkommen darf. Erst dann weiß er, wie der Variablenname heißt – nämlich $zeit – und kann den Wert dieser Variablen an diese Stelle einbauen.

Wenn man die Variable an den String konkateniert, steht sofort für den Parser fest, welche Variable gemeint ist. Und genau da ist dann auch der Geschwindigkeitsunterschied begründet.

Also sowohl der besseren Lesbarkeit als auch der Geschwindiskeit wegen empfehle ich Variablen in Strings zu konkatenieren statt sie einzubetten.

Zum Nachvollziehen: die Quellcodes zu diesem Beitrag sowie die Benchmark-Ergebnisse.

php