PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


PHP Profiling mit XDebug und KCachegrind

with 6 comments

So, nun will ich das Thema nachholen, was ich am letzten Donnerstag hab anklingen lassen. Am Wochenende ist dann jedoch das Gewinnspiel dazwischengekommen, sodass ich nun etwas zum Thema PHP-Profiler schreibe.

Profiler? Sind das nicht die FBI-Psychiater, die anhand von Tatorten und den Opfern etwas über den Täter aussagen können, wie er lebt und denkt? Naja, vielleicht…

Profiler gibt es auch für die meisten Programmiersprachen. Profiler schauen „unter die Haube“ und untersuchen die kleinsten Einheiten und Funktionen einen PHP-Programms. Sie messen die Zeiten für die Kommandos, zählen die Anzahl der Aufrufe, merken sich welche Funktion andere Funktionen aufruft und kann daraus sehr ausführliche und interessante Tabellen und Graphen generieren. Diese nutzt man dann am häufigsten, um (zeitliche) Flaschenhälse bei der Programmierung zu finden.

Aber wie erhält man diese Zeiten? Wenn man nur einen kleinen Teil seines Programms beobachten möchte und die Zeit messen möchte, die es braucht, macht man das wahrscheinlich so:

$start = microtime(true);
 // Hier einige Befehle, die gemessen werden sollen
 $end = microtime(true);
 $diff = $end - $start;
 echo "Benötigte Zeit: " . $diff;

Das ist meistens völlig ausreichend für den Anfang, um einen kurzen Überblick zu finden. Wenn das Programm aber sehr groß ist, und man nicht tausende dieser Messungen einbauen möchte, kann man gleich das ganze Script profilen lassen.

Wir machen das ganze mal praktisch an einem kleinen Beispiel:

class User
{
	private $username;
	private $newsletter;

	public function __construct() {
		$this->username = $this->getRandomString(8);
		$this->newsletter = rand(0, 1);
	}

	public function getUsername() {
		usleep(500000);
		return $this->username;
	}

	public function getNewsletter() {
		return $this->newsletter;
	}

	private function getRandomString($stringLength) {
	    //srand ((double)microtime() * 1000000);
	    return substr(md5(rand()), 0, $stringLength);
	}
}

for ($i = 0; $i < 5; $i++) {
	$user = new User();
	if (rand(0, 1)) {
		echo $user->getUsername()."\n";
	} else {
		echo $user->getNewsletter()."\n";
	}
}

Nun muß ich noch XDebug in der php.ini aktivieren. Dazu aktiviert man einfach die PHP-Extension XDebug wie folgt in der php.ini:

[XDebug]
; Only Zend OR (!) XDebug
zend_extension_ts=“C:\xampp\php\ext\php_xdebug.dll“
xdebug.remote_enable=true
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.profiler_enable=1
xdebug.profiler_output_dir=“C:\xampp\tmp“

Natürlich sollte man vorher die php_xdebug.dll downloaden und in den entsprechenden Ordner legen. Nachdem man die php.ini so geändert hat, wird für jedes PHP-Script ein sogenannter CacheGrind-Dump im Output-Dir abgelegt. Diese Datei kann, je nach Komplexität und Umfang des Script, auch mehrere hundert MB groß werden.

Nun führe ich das Script ganz normal aus, im Hintergrund wird dann die CacheGrind Datei erzeugt.

run

Die generierte cachegrind.out.2916 sieht so aus, und ist (noch) nicht wirklich brauchbar:

cachegrundtxt

Öffnen und tabellarisch bzw. grafisch darstellen kann man diese Textdatei dann zum Beispiel mit dem Windows Programm WinCacheGrind:

wincachegrind2916

Hier erkennt man zwar schon etwas, aber viel besser ist unter Linux KCachegrind, das sieht dann so aus:

kcachegrind_2916

Man schaue sich nun die Anzahl der Aufrufe sowie die Zeitwerte an, und kann unzweifelhaft feststellen, dass in unserem einfachen Beispiel die usleep-Funktion die meiste Zeit gebraucht hat. Aber auch getRandomString() ist nicht zu vernachlässigen, vielleicht könnte man da noch etwas optimieren. Bei großeren Programmen mit SQL-Abfragen und komplexeren Algorithmen und Abläufen wird das ganze natürlich noch viel interessanter, probiert es einfach mal bei euren großen Projekten aus!

Man kann sowohl nach den absoluten als auch den prozentualen Zeitwerten sortieren, sieht Callees und Caller, kann sich einen Call Graph anzeigen lassen usw, ein wirklich tolles Programm.

Ich würde auf jeden Fall für ernsthafte Profiler das KCachegrind empfehlen. Falls man gerade kein laufendes Linux-System zur Hand hat, ist es höchste Zeit, mittels VirtualBox und Ubuntu schnell eins aufzusetzen. Beides ist natürlich kostenlos, und nach einer Stunde ist es lauffähig. Man braucht auch keine umständlichen Dual-Boot-Sachen und zerschießt sich wohlmöglich sein System, VirtualBox kann „ein Betriebssystem in einem Fenster“ laufen lassen. Einfach mal ausprobieren und informieren.

Mittels Profiling kann man sehr einfach herausfinden, wie oft welche Funktion aufgerufen wird. Man erkennt ziemlich schnell, wieviele Datenbank-Queries gemacht werden und von wo diese kommen. Man sieht, wieviele Objekte erstellt werden und wo dies passiert (und wie lang das dauert).

Die gefundenen Flaschenhälse kann man dann beseitigen. Entweder durch effizientere SQL-Abfragen, bessere Algorithmen, Caching oder oder. Das ist von Fall zu Fall verschieden.

Mich würde interessieren, ob und wie ihr profiled, und was ihr dadurch für gewöhnlich an Fehlern/Flaschenhälsen findet und wie ihr sie behebt.

Written by Michael Kliewe

Juli 15th, 2009 at 9:13 am

Posted in PHP

Tagged with , , ,

6 Responses to 'PHP Profiling mit XDebug und KCachegrind'

Subscribe to comments with RSS or TrackBack to 'PHP Profiling mit XDebug und KCachegrind'.

  1. Alternativ zum Desktoptool finde ich Webgrind sehr toll.

    Frank

    15 Jul 09 at 13:06

  2. Whao, Danke für den Link, das kannte ich noch nicht! Sieht ja ganz brauchbar aus, die Frage ist, ob es bei großen Dateien (wir haben einige, die 80 MB oder 540 MB groß sind) performant ist.

    Michael Kliewe

    15 Jul 09 at 13:55

  3. Hab mir Webgrind jetzt auch mal angeschaut, ist für „mal eben“ echt ganz süß, aber grad den Call-Graph vermiss ich etwas. Es geht auch recht gemütlich zu Werke. Man muss auch etwas aufpassen, weil aktiviert man das Profiling direkt in der php.ini wird webgrind selbst auf profiliert… Das ergibt locker ebenfalls 200 bis 300MB Dateien. Es empfiehlt sich das Profiling nur für einzelne Projekte per htaccess zu aktivieren.

    KingCrunch

    21 Jul 09 at 17:23

  4. Exakt die selben Beobachtungen hab ich auch gemacht. Installation und Konfiguration sehr einfach. Eine 300MB Datei mit Webgrind zu öffnen kann man aber getrost sein lassen. Und manchmal kann er den Caller nicht auflösen, man kann also nicht weiter „nach oben“ browsen.

    Dass der Webgrind auch selbst profiled wird hatte ich auch 😉 Nach der Deaktivierung in der php.ini gings dann aber bei kleinen Dateien.

    Trotzdem empfehle ich nach wie vor KCachegrind für ernsthaftes Profiling. Aber Danke für die Alternative!

    Michael Kliewe

    21 Jul 09 at 18:19

  5. Sehr schöner informativer Artikel den man schnell findet wenn bei google nach cachegrind findet. Leider hat sich ein kleiner fehle reingeschlichen. Das Linuxtool heißt „KCachegrind“. Ich habe nämlich grad wie in Irrer nach Deiner Schreibweise gesucht 🙂

    Oliver

    10 Aug 10 at 12:28

  6. @Oliver: Da hast du Recht. Aber Google interessiert Groß/Kleinschreibung nicht, die Suchergebnisse sind die selben. Es gibt nur ein KCachegrind. 😉
    Habe es im Artikel und in meinen Kommentaren berichtigt.

    Michael Kliewe

    10 Aug 10 at 12:44

Leave a Reply

You can add images to your comment by clicking here.