Xdebug - PHP Performance Analyse und Benchmark

Wenn Webseiten langsam und träge werden, die Aufrufe mehrere Sekunden dauern oder der Server bei vielen Besuchern eine hohe Auslastung zeigt, dann kann dies an einer ineffektiven Programmierung oder langsamen Datenbankabfragen liegen. Aber wie soll man die Schwachstellen und Flaschenhälse in der PHP-Programmierung und den Datenbankabfragen finden? Hierfür benötigt man ein Benchmark-Tool, welches die einzelnen Vorgänge analysiert und die Geschwindigkeit zur Laufzeit aufzeichnet. Vor allem folgende Fragen zu zur Analyse entscheidend:

  • Welche Funktionen und Programmabschnitte sind besonders langsam?
  • Wie lange benötigt eine bestimmte Datenbank-Abfrage?
  • Wie ist die Gesamtdauer eines PHP-Skripts?
  • Wie groß ist der Speicherverbrauch?
  • Kurz gefasst: wo sind die Leistungsbremsen in der PHP-Programmierung?

Die Antworten auf diese Fragen geben so genannte Profiler wie Xdebug for PHP. Diese sehr nützliche Erweiterung analysiert sämtliche Funktionsaufrufe in einem PHP-Skript und notiert dazu die Dauer und Laufzeit. Es entspricht demzufolge einem detaillierten Benchmark, der dem Programmierer ermöglicht, langsame Stellen im Quellcode und die Performance zu analysieren.

Installation von xdebug - PHP Profiler und Benchmark

Die Installation von xdebug erfolgt in zwei Schritten. Nachdem man das Paket installiert hat, wird xdebug in der PHP.ini aktiviert. Folgendes Beispiel zeigt die Installation unter Linux Gentoo:

# emerge -av xdebug

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild  N     ] dev-php/xdebug-client-2.2.1  USE="-libedit" 243 kB
[ebuild  N     ] dev-php/xdebug-2.2.1  PHP_TARGETS="php5-4 -php5-3" 0 kB

Abhängig von der jeweiligen Linux Distribution bzw. der PHP-Konfiguration muss xdebug aktiviert werden. Unter Gentoo wurde eine separate xdebug.ini in das PHP-Verzeichnis kopiert, die automatisch eingelesen wird. Wo dies nicht der Fall ist, kann der Inhalt der xdebug.ini in die php.ini kopiert werden.

Um jedes PHP-Skript automatisch zu analysieren, kann der Wert xdebug.auto_trace auf 1 gesetzt werden. Bei jedem Seitenaufruf werden nun die Benchmark-Messwerte des ausführenden PHP-Skriptes analysiert.

# ls /etc/php/apache2-php5.4/ext/
xdebug.ini

# # vi /etc/php/apache2-php5.4/ext/xdebug.ini
zend_extension=/usr/lib/php5.4/lib/extensions/no-debug-non-zts-20100525/xdebug.so
xdebug.auto_trace="1"
xdebug.trace_output_dir="/tmp"
xdebug.trace_output_name="trace.%c"
xdebug.trace_format="0"
xdebug.trace_options="0"
xdebug.collect_includes="1"
xdebug.collect_params="0"
xdebug.collect_return="0"
xdebug.collect_vars="0"
xdebug.default_enable="1"
xdebug.extended_info="1"
[...]

Abschließend starten wir den Apache-Webserver neu, um die PHP-Einstellungen neu einzulesen.

# /etc/init.d/apache2 restart

Nun sollte phpinfo(); das aktivierte xdebug anzeigen und folgendes zu sehen sein:

phpinfo zeigt ein aktiviertes Xdebug

Konfiguration von Xdebug

Analyse der Benchmarkwerte

Kommen wir nun zum interessanten Teil, nämlich der Auswertung der Laufzeiten des PHP-Skriptes. In der Standard-Konfiguration legt xdebug im Verzeichnis /tmp/ die Logdateien an:

# ls /tmp/
trace.2043925204.xt

Der Inhalt der Log-Daten ist eine detaillierte chronologische Auflistung aller PHP-Funktionen. Die erste Spalte zeigt die Zeit in Sekunden, wann der jeweilige Befehl gestartet wurde. Hieran kann man sehr schön nachvollziehen, wie lange die Ausführung bestimmter Programmbereiche dauert und wo besonders viel Zeit verloren geht. In unten gezeigten Beispiel benötigt die Datenbank-Abfrage in Zeile 492 in der Datei attibute.class.php fast 2,2 Sekunden, welches ein vergleichsweise langer Zeitraum ist. Hier wäre eine Performance-Optimierung ratsam.

TRACE START [2014-04-30 08:09:17]
0.0007 154076 -> {main}() /www/index.php:0
0.0009 154600 -> error_reporting() /www/index.php:2
0.0010 154792 -> ini_set() /www/index.php:3
0.0011 155000 -> strpos() /www/index.php:9
0.0012 155072 -> explode() /www/index.php:10
0.0013 155220 -> date() /www/index.php:12
[...]
0.8096 2075100 -> mysql_query() /www/seo_urls.inc.php:877
0.8141 2075192 -> mysql_num_rows() /www/seo_urls.inc.php:879
0.8142 2075192 -> mysql_fetch_array() /www/seo_urls.inc.php:881
0.8143 2075972 -> mysql_query() /www/class/attribute.class.php:492
3.0799 2076140 -> mysql_num_rows() /www/class/attribute.class.php:495
3.0801 2076140 -> mysql_fetch_assoc() /www/class/attribute.class.php:496
3.0803 2076652 -> mysql_fetch_assoc() /www/class/attribute.class.php:496
3.0803 2077124 -> mysql_fetch_assoc() /www/class/attribute.class.php:496
[...]
3.3316 2099612 -> count() /www/class/category.class.php:573
3.3317 2099612 -> count()
[...]
TRACE END [2014-04-30 08:09:20]

Mehr ist zu diesem Thema nicht zu sagen. Weitere Informationen findet man in der offiziellen Dokumentation von xdebug.