Shopware 6 Performance Blog

Shopware 6 PHP Performance – Config Tweaks im Detail

Shopware 6 gibt in ihrem Performance-Guide einige Empfehlungen für PHP-Einstellungen, die ich mir heute mal genauer anschauen und dabei die Auswirkung der Performance-Optimierung auf die Serverantwortzeiten messen möchte 😊

Weiter unten siehst Du Erklärungen zu den einzelnen Einstellungen, wie Du sie einrichtest und den genauen Impact auf die Ladezeit in Sekunden.

PHP-Einstellungen

Falls Du Dir die Tipps direkt ansehen willst, hier der Link zu den Shopware PHP-Einstellungen und den allgemeinen Performance Guidelines: Shopware Performance Tweaks. Auf diese PHP-Einstellungen fokussiere ich mich heute:

zend.assertions=-1

opcache.enable_file_override=1

opcache.interned_strings_buffer=20

opcache.validate_timestamps=0

zend.detect_unicode=0

realpath_cache_ttl=3600

opcache.preload=...

opcache + JIT

Vorbereitung Performance Tests

Zum Testen habe ich mir einen frischen Shopware 6 Shop bei Hetzner mit einem LAMP-Stack aufgesetzt. Schau gerne vorbei: shop.pagespeedy.de. Der Shop läuft auf Apache2, PHP 8.3 und MySQL 8.0. Zu Beginn sind natürlich die Standardeinstellungen aktiv gewesen.

Um die Performance zu messen, nutze ich das PHP-Profiling Tool Tideways. Das Tool ist super, um den genauen Callgraph beim Aufruf einer Seite zu sehen und die einzelnen Bestandteile der Ladezeit zu messen.

Für den Test habe ich mich dazu entschieden, die Kategorieseite im ungecachten Zustand aufzurufen – einmal mit und einmal entsprechend ohne die PHP-Config-Tweaks. Dann rechne ich den Durchschnitt der TTFB (Time to First Byte) aus.

PHP-Ladezeiten im Vergleich

Da ich einen Standard Shopware 6 Shop mit Demo-Daten habe, ist die Ladezeit aktuell bereits sehr gut. Ohne die PHP-Tweaks lag der Durchschnitt bei etwa 370ms. Soweit so gut.

Mit den optimierten Einstellungen konnte ich die Ladezeit auf rund 195ms drücken. Selbst im gecachten Zustand gab es nochmal eine Verbesserung von rund 10ms. Das entspricht, zumindest bei meinen Tests, einer Verbesserung von fast 50%!

An dieser Stelle muss man natürlich noch erwähnen, dass ein Test in einer “sterilen” Umgebung nur bedingt aussagekräftig ist. Gerade wenn viel traffic im Shop ist, können die Ladezeiten nochmal etwas anders aussehen. Außerdem können Shops mit schlecht geschriebenen Plugins auch mit den Config-Tweaks schlechte Ladezeiten aufweisen, wenn z.B. Datenbank-Abfragen sehr lange brauchen und PHP gar nicht das Bottleneck ist.

Nichtsdestotrotz bringen die Config-Tweaks definitiv einen Vorteil für die Ladezeit & Auslastung des Servers und dürfen daher in einem prod-System nicht fehlen. Sie bieten die Basis für einen stabilen Shop und weiter Performance Analysen bzw. Optimierungen.

So viel zu dem Vergleich der Ladezeiten. Aber was genau bewirken die Einstellungen genau? Arbeiten wir uns von oben nach unten durch 🙂

Shopware 6 PHP-Einstellungen im Detail

zend.assertions

Wenn Du schon mal Unit-Tests in PHP geschrieben hast, bist Du bestimmt über die Funktion assert() gestolpert. Die wird nämlich vor allem benutzt, um Annahmen zu definieren, ob Dein Code ein erwartetes Ergebnis zurückgibt.

assert() funktioniert generiert hierbei im Vorhinein Code, der, wenn auch nur in einem kleinen Ausmaß, die Performance negativ beeinflusst.

Gerade weil assert nur in development-Umgebungen benötigt wird und wir im production-Shop die schnellste Ladezeit brauchen, sollten wir zend.assertions in der php.ini mit -1 deaktivieren.

Hierdurch gibt assert immer true zurück und generiert keinen Code mehr, der die Performance beeinflusst.

Die Einrichtung ist recht simpel:

zend.assertions=-1

opcache.enable_file_override

Diese Einstellung sorgt dafür, dass der OPcache für PHP-Funktionen wie file_exists und is_file genutzt wird. Das ist besonders cool, weil diese Funktionen bei Shopware unter anderem oft beim Template-Rendering benutzt wird.

Der OPcache sorgt dann dafür, dass Dateisystemabfragen nicht immer wieder neu ausgeführt werden müssen, was die Performance erheblich verbessert.

Achtung: Shopware warnt, dass es beim Cache-Clear ggf. zu Fehlermeldungen kommen kann. Bei meinen Tests ist mir das bisher noch nicht aufgefallen.

Auch hier ist die Einstellung einfach:

opcache.enable_file_override=1

opcache.validate_timestamps

Normalerweise muss bei jedem Request unser “von Menschen lesbare” PHP-Code erstmal von einem PHP-Interpreter in “von Maschinen lesbaren” Bytecode kompiliert werden.

Ist der OPcache aktiviert, wird dieser Bytecode im Arbeitsspeicher gecacht und kann dann beim nächsten Request, ohne nochmals kompiliert zu werden, direkt verwendet werden.

Hierbei nutzt der OPcache im Standard den Timestamp einer php-Datei, um alle “opcache.revalidate_freq” Sekunden zu schauen, ob die Datei verändert wurde und diese ggf. neu zu cachen.

Gerade in seriösen Produktiv-Umgebungen werden PHP-Dateien nicht einfach so geändert. Daher können wir über die Einstellung “opcache.validate_timestamps=0″ das erneute Validieren der Dateien ausstellen.

Das hat zwar den Nachteil, dass wir bei einem Deployment den OPcache leeren müssen, sorgt aber auch für eine bessere Performance. Das Leeren kannst Du z.B. über dieses Tool durchführen: Cache-Tool

opcache.validate_timestamps=0

opcache.interned_strings_buffer

Neben dem Bytecode kann der OPCache auch sogenannte „interned PHP Strings“ im RAM zwischenspeichern. Das reduziert den Speicherbedarf bei jedem Request und verbessert so die Performance.

Aber was sind „interned Strings“ eigentlich? Das können ganz einfache Strings sein, wie zum Beispiel bei echo "Hello world!", aber auch Klassen-Namen, Dateinamen, Property-Namen und mehr.

Damit der OPcache optimal arbeitet, sollte er niemals komplett ausgelastet sein. Der Standardwert liegt bei 8MB, aber für Shopware wird empfohlen, diesen auf mindestens 20MB zu erhöhen. In dieser Einstellung wird der Speicherplatz in MB festgelegt, den PHP für diese Strings reserviert.

Um den Status des OPcaches im Blick zu behalten, kannst Du Tools wie dieses Cache-Tool nutzen.

Bei meiner Standard-Shopware6-Installation sind von den 20MB etwa 13MB belegt. Wenn Du einen größeren Shop betreibst, kann es sinnvoll sein, den Wert noch weiter anzupassen, um Performance-Probleme zu vermeiden.

opcache.interned_strings_buffer=20

zend.detect_unicode

Wenn diese Einstellung aktiv ist, überprüft der PHP-Interpreter, ob Skripte im Unicode-Format (UTF-16 oder UTF-32) vorliegen. Das kann die Ladezeit unnötig verlangsamen.

Da in der Regel alle Skripte im UTF-8-Format vorliegen, ist diese Prüfung überflüssig und kann bei Shopware bedenkenlos deaktiviert werden.

zend.detect_unicode=0

realpath_cache_ttl

Um die Effizienz bei Dateizugriffen zu steigern, speichert PHP die absoluten Pfade von Dateisystemressourcen (wie Dateien oder Verzeichnissen) im sogenannten Realpath-Cache.

In der Konfiguration kannst Du festlegen, wie lange diese Einträge im Cache gespeichert bleiben sollen, gemessen in Sekunden.

Der Standardwert liegt oft bei 120 Sekunden, aber auf Produktionssystemen lässt sich dieser Wert problemlos auf 3600 Sekunden erhöhen. Das sorgt dafür, dass Dateizugriffe schneller ablaufen und die Ladezeiten verbessert werden.

realpath_cache_ttl=3600

opcache.preload

Mit PHP 7.4 wurde eine neue Funktion eingeführt: OPcache preloading. Damit können z.B. PHP-Dateien im Arbeitsspeicher vorab geladen und während der gesamten Laufzeit des Webservers verfügbar gehalten werden.

Dadurch verbessert sich die Leistung von PHP-Anwendungen erheblich, weil vorkompilierte Dateien nicht bei jedem Aufruf erneut von der Festplatte geladen und geparst werden müssen.

Shopware 6 erstellt dazu eine opcache-preload.php im Cache-Ordner, die man in der php.ini einbinden kann. Sobald der Webserver (sei es Apache oder Nginx) startet, lädt der OPCache diese Datei vor, kompiliert sie einmalig und hält sie effizient während der gesamten Laufzeit bereit.

Wichtig: Preloading funktioniert nur mit persistenten PHP-Prozessen wie FPM (FastCGI Process Manager) oder mod_php in Apache. Bei einfachen PHP-Skripten, die ohne solche Prozesse ausgeführt werden (z. B. über die Kommandozeile), ist Preloading nicht sinnvoll.

Laut Shopware bringt Preloading einen Performance-Boost von etwa 2-5%. Der Vorteil liegt also auf der Hand. Der Nachteil? Wenn Änderungen an PHP-Dateien (z.B. ein Deployment) vorgenommen werden, muss der Server oder der PHP-FPM-Prozess neu gestartet werden, damit die Änderungen wirksam werden.

In meinem Testshop musste ich als User www-data verwenden. Auch der Pfad zur preload-Datei kann bei jedem Hoster anders aussehen.

opcache.preload=/var/www/html/var/cache/opcache-preload.php
opcache.preload_user=nginx

OPcache & JIT

Eines der wichtigsten neuen Features von PHP 8 ist sicherlich der JIT (Just-In-Time) Compiler. Der JIT-Compiler wird ähnliche wie der OPcache genutzt, um unnötiges Verarbeiten von PHP-Scripten zu verhindern, funktioniert aber etwas anders.

Beim OPcache werden bestimmte Schritte im Verarbeitsprozess eines PHP-Requests im RAM zwischengespeichert. Hierbei werden nach dem “kompilieren”-Schritt sogenannte “opcodes” im OPcache gespeichert, die dann beim nächsten Request bequem wiederverwendet werden können.

Diese opcodes sind zwar schon ziemlich weit im Verarbeitungsprozess, müssen am Ende allerdings dennoch in “machine code” verarbeitet werden, bevor sie in der CPU ausgeführt werden.

Den JIT-Compiler kann man sich vereinfacht als ein Tool zwischen dem OPcache und der CPU vorstellen, das besonders wichtige Parts im Code noch schneller verfügbar macht und daher eine sinnvolle Ergänzung zum OPcache ist.

Eine genauere Erklärung findet ihr hier. Eine interessante Benchmark hier.

Die Einrichtung ist recht simpel. Zunächst muss der OPcache aktiv sein. Zusätzlich werden folgende Einstellungen in der php.ini benötigt:

opcache.jit_buffer_size=100M
opcache.jit=1255

Nähere Infos zu den Einstellungen findet ihr in diesem Beitrag.

Fazit

All diese Anpassungen verbessern die Ladezeit ein wenig – und gerade in der Summe machen sie einen spürbaren Unterschied. In meinen Tests habe ich nur mit einem frischen, unbesuchten Shop gearbeitet, aber gerade bei einem großen Shop mit viel Traffic sind die Auswirkungen auf die Auslastung & Ladezeit wahrscheinlich noch deutlicher zu sehen.

In meinem nächsten Beitrag möchte ich einen kleinen Guide bzw. eine Checkliste für alle PHP-Einstellungen, die aus meiner Sicht sinnvoll sind, schreiben 😊

Falls Du meinen letzten Beitrag verpasst hast, kannst Du hier lesen, weshalb Performance überhaupt wichtig ist.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert