Bottlenecks identifizieren

Performance von PHP Code profilen und optimieren

Im vorherigen Abschnitt haben wir die Grenzen der Applikation auf einem bestimmten System herausgefunden. Nun legen wir Hand an den Code an ;)

Hierbei wollen wir jedoch wissen, WANN wird WO WIEVIEL Zeit verbracht und WARUM?

Bottlenecks mit xDebug und KCachegrind identifizieren

Für diese Möglichkeit gibt es für PHP mehrere Tools. Zu nennen wäre hier XDebug und hxprof von Facebook. Eingehen möchte ich hier auf xDebug. xDebug ist eine Debug Erweiterung mit vielen Komponenten. Eine Möglichkeit ist die Erzeugung von sogenannten Cachgrind Dateien. Diese Cachegrind Datei enthält Informationen darüber welche Stelle im Code wie oft aufgerufen wurde.

Zunächst müssen wir dafür xDebug installieren. Dies beschreibe ich hier auf Basis der Linux Distribution Ubuntu.

xDebug unter Ubuntu installieren und konfigurieren

Mit folgendem Kommando kann xDebug unter Ubuntu installiert werden:

apt-get install php5-xdebug

Nachdem die Installation durchgeführt wurde ist unter "/etc/php5/conf.d/xdebug.ini"
Die Konfigurationsdatei für xDebug zu finden.

Unter der folgenden Adresse sind die möglichen Konfigurationoptionen zu finden:

xdebug.org/docs/all_settings

Für unsere Profiling sind folgende Settings relevant und deshalb hinzuzufügen oder anzupassen.

xdebug.profiler_enable_trigger = 1
xdebug.profiler_output_dir = /tmp/xdebug

Durch "profiler_enable_trigger" ist die Aufzeichnung eines Applikationsprofiles aktiviert, wenn einem Request der Parameter "?XDEBUG_PROFILE" angehängt wird.

Alternativ kann diese Einstellung auch in der .htaccess Datei folgendermaßen erfolgen:

php_value xdebug.profiler_enable 1

Nachdem die Konfiguration durchgeführt wurde kann eine Seite mit dem Parameter "?XDEBUG_PROFILE" aufgerufen werden. Danach befindet sich im Ausgabeverzeichnis für jeden Skriptaufruf eine cachegrind Datei.

WICHTIG: Der Profiler sollte natürlich nur auf Entwicklungssystemen gezielt aktiviert werden, da die Anwendung langsamer wird und schnell viele Profilingdaten entstehen.

Cachegrinds mit KCachegrind auswerten

Für die Auswertung der Cachegrinds gibt es einige Tools. Meiner Meinung nach bietet KCachegrind hier die meisten Möglichkeiten und wird deshalb hier näher vorgestellt.

Installation von KCachegrind

Zunächst muss kcachegrind mit apt-get installiert werden:

apt-get install kcachegrind

Nach der Installation können die Profileergenisse mit KCachegrind geöffnet werden.

Analyse mit KCachegrind

Folgenden Funktionen in KCachegrind finde ich persönlich besonders nützlich.

1. Die Callee Map

Die Callee Map zeigt visuel welcher Codeblock beim Aufruf die meiste Zeit beansprucht. Je größer der Platz auf der Callee Map, umso länger der Zeitverbrauch.

2. Der Call Graph

Im Callgraph sieht man in welcher Reihenfolge wie oft, welcher Code aufgerufen wird.

3. Flat Profile

Das Flat Profile zeigt die Stellen im Code in Ihrer Reihenfolge sowie deren "self" Wert an. Der "self" Wert zeigt wieviel Zeit für diesen Codeblock selbst (also ohne später Codeblöcke) gebraucht wurde.

Codeblöcke mit Optimierungspotential identifizieren

Folgende Kriterien weisen auf ein hohes Optimierungspotential hin:

1. Hohe / Mittelhoher "self" Anteil und viele Aufrufe:

Hierbei handelt es sich scheinbar um eine "teure" Operation. Eventuell kann das Ergebnis gecacht werden, oder es kann gar ganz auf den Aufruf verzichtet werden?

2. Früh im Callgraph aber nicht benötigt:


Möglicherweise gibt es im Callgraph schon ganz früh "Abzweigungen" die garnicht aufgerufen werden müssten. Dadurch das deren Aufruf und somit auch die Ausführung dieses Codes im selben "Ausführungsast" wegfallen, kann unter Umständen viel Zeit gespart werden.

Mit Caches AUsführungszeit gegen Speicher tauschen

Heutige Server verfügen meist über sehr viel Arbeitsspeicher, der nur selten voll genutzt wird. Die CPU ist hingegen oft sehr stark ausgelastet. Bei stark optimierten Anwendungen können wir uns dies zu nutze machen und von der CPU berechnete Zwischenergebnisse im Arbeitsspeicher zwischenspeichern.

Wenn die selbe Anwendung die gleiche Operation nocheinmal durchführt, kann sie den Inhalt aus dem Cache verwenden und muss so die Berechnung nicht nochmal ausführen.

In modernen PHP Frameworks gibt es deshalb fast immer ein Caching Framework. Dieses Caching Framework bietet die Möglichkeit Daten in einem Cache Backend zu speichern. Das Cache Backend benutzt dabei verschiedene Technologien um den Cache Inhalt zu speichern.

Folgende Backends sind hierbei gängig:

  • Datenbank Backend: Der Cache speichert seine Daten in einer SQL Datenbank. Hierbei muss geprüft werden inwiefern der Cache tatsächlich die Applikation beschleunigt.

  • APC Backend: Der Alternative PHP Cache ist ein Cache der seine Daten im Speicherbereich des Webservers ablegt. Der Cacheinhalt ist somit für folgende Skriptaufrufe erhalten. Sehr performant.

  • Dateisystem Backend: Der Cache speichert seine Daten im Dateisystem. Hierbei ist individuell zu prüfen ob der Cache die Applikation beschleunigt.

  • Memory Backend: Das Cachebackend speichert seine Daten in einer statischen Variable zur PHP Laufzeit. Nachdem das Skript beendet wurde, ist der Inhalt des Caches ebenso verloren. Kann mehrfach Operationen im gleichen Skript beschleunigen.


Folgende PHP Frameworks mit Caching Backend kenne ich:

1. TYPO3 / FLOW3 Ausgereiftest Cachingframework, von FLOW3 zurückportiert für TYPO3

2. Zend Framwork. Caching Framework mit vielen Backend Komponenten.

Kein Code ist besser als schneller Code

Neben der Optimierung durch Caches kann eventuell noch Code gefunden werden, der eigentlich nicht aufgerufen werden müsste. Je früher im Callgraph und je höher der Anteil umso höher die Ersparnis bei der Ausführung.

Schneller Code ist besser als langsamer Code

Neben den vorherigen "großen" Optimierungen können eventuell noch viele kleine Optimierung durchgeführt werden.

  1. Werden in Schleifen eventuell Werte immer wieder berechnet, die man auf vorher, ausserhalb der Schleife berechnen könnte?

  2. Gibt es Funktionen die oft aufgerufen werden und die viel Zeit brauchen? Hierbei lohnt es sich nach alternativen zu schauen. z.B. curl statt file_get_contents zu verwenden oder regexes statt eines DOM Objektes für hochfrequentierte aufrufe.

  3. Kann man Schleifen schon früher beenden oder in Funktionen schon früher Werte zurückgeben ohne das unnützer Code ausgeführt wird?

Ingesammt würde ich jedoch der Regel folgen "Lesbarer Code vor Überoptimierung", denn der ganze Code soll ja auch wartbar bleiben. Wenn das Potential also nur im promille Bereich liegt, würde ich eher auf diese Optimierung verzichten.

Navigation