Auch bei leistungsfähiger Hardware lohnt sich der Blick ins Innere von Programmen. Ein Tool dazu ist der so genannte Profiler. XHProf ist ein solches Werkzeug, was PHP-Skripte analysiert und zur Optimierung eingesetzt werden kann.
Ein wenig im Schatten von XDebug und dessen Profiling-Fähigkeiten liegt der hierarchische Profiler namens XHProf. XHProf wurde von Facebook entwickelt und im März 2009 als Open-Source freigegeben. XHProf besteht aus zwei Komponenten – einer in C geschriebenen PHP-Extension sowie den Reporting- bzw. User-Interface-Skripten in PHP. Die Installation und Einrichtung ist ausführlich in der Dokumentation beschrieben. Sofern erfolgreich, ist ein Abschnitt „xhprof“ in der Ausgabe von phpinfo() zu finden.
XHProf theoretisch
XHProf bietet hierarchisches Profiling. Dabei wird die Anzahl und Zeit von Funktionsaufrufen gemessen und mittels der Reporting-Skripte dargestellt. Dabei werden verschiedene Metriken erfasst: die Gesamtzeit inkl. aller aufgerufenen Funktionen (Inclusive Time / Subtree Time), die Zeit, die in der Funktion selbst verbraucht wurde (Exclusive Time / Self Time), die gesamte Laufzeit (Wall Time / Elapsed Time), die CPU-Zeit (User- und Kernel-Space). Die einzelnen Funktionsaufrufe lassen sich wiederum im Detail untersuchen, d.h. Metriken der aufgerufenen und aufrufenden Funktionen darstellen. Einen grafischen Überblick bietet der so genannte „Callgraph“, der die Beziehungen der Funktionsaufrufe untereinander verdeutlicht. „Funktionen“ schließt Methodenaufrufe selbstverständlich mit ein. Des Weiteren zeigt XHProf den Speicherverbrauch als Differenz zwischen Funktionsaufruf und -ende. Ebenso wird der Gesamtverbrauch in der Zusammenfassung angegeben.
XHProf praktisch
Da die Schichtentrennung, etwa anhand MVC-Modell, in PHP immer häufiger vorzutreffen ist, jeder PHP-Entwickler ein CMS, ein Framework oder eine Template-Engine programmiert hat, besteht das Praxisbeispiel aus einem Vergleich zweier Template-Systeme. Dabei tritt SithTemplate gegen Twig an. Beide Template-Engines verwenden eine ähnliche Syntax, die vom Python-Framework Django inspiriert ist. Twig ist dabei die neuere Entwicklung und ist Bestandteil der bislang unter Symfony Reloaded bekannten Preview Version 2.0 des Symfony-Frameworks. SithTemplate war die erste PHP-Template-Engine mit Django-ähnlicher Syntax, ist jedoch weniger bekannt. Da die Template-Syntax sowie Handhabung ähnlich ist, fällt ein Umstieg auf die jeweils andere Template-Engine weniger schwer als die Migration auf andere Template-Engines wie etwa Smarty.
Beispiel Profiling Template-Engines
Das Test-Template ist bewusst simpel gehalten – zwei Ersetzungen von Variablen und eine Schleife, die 100-mal den Index und dazu gehörigen Wert eines Arrays ausgibt. Die Templates sind zu 100% identisch. Der Unterschied im PHP-Code bezieht sich ausschliesslich auf die Einbindung und Nutzung der jeweiligen Template-Engine.
Die Ergebnisse der Zusammenfassung:
SithTemplate 1.1a2 | Twig 0.9.9 | |
Total Incl. Wall Time (microsec): | 5,525 microsecs | 14,630 microsecs |
Total Incl. CPU (microsecs): | 0 microsecs | 10,000 microsecs |
Total Incl. MemUse (bytes): | 532,256 bytes | 1,188,984 bytes |
Total Incl. PeakMemUse (bytes): | 527,088 bytes | 1,189,944 bytes |
Number of Function Calls: | 79 | 598 |
SithTemplate geht als klarer Gewinner hervor. Geringere Laufzeit, geringerer Speicherverbrauch, und wesentlich weniger Funktionsaufrufe bei sehr ähnlicher Funktionalität. Sicherlich liesse sich Twig noch optimieren, im besten Fall nicht auf Kosten von Flexibilität und Code-Qualität. Das selbst gesteckte Ziel, „so schnell wie möglich“ zu sein, hat Twig noch nicht erreicht.
Urteil und Resultate
In den letzten Tagen habe ich XHProf intensiv genutzt, um Bottlenecks zu entdecken und den zugrunde liegenden Code zu optimieren. Zunächst bestand das Interesse, die o.g. Template-Engines zu vergleichen und eine Entscheidung für oder gegen den Umstieg auf Twig zu treffen. Angesichts der Ergebnisse fiel die Wahl leicht – es wird weiterhin SithTemplate bleiben. Dafür spricht zum einen der Aufwand zur Anpassung der eigenen Plugins sowie die schlechteren Ergebnisse von Twig aus dem Profiling.
Gegenüber XDebug ist XHProf sehr leicht anzuwenden, die Installation lässt sich ebenso einfach auf einem produktiven Server vornehmen. Der Vorteil sind die validen Ergebnisse aus dem Live-Betrieb, inkl. gefülltem Cache, im Gegensatz zu einem Test-System, auf dem andere Bedingungen herrschen. Der Frontend-Controller schaltet XHProf einfach per speziellem Parameter hinzu. Daraufhin wird auf die XHProf-UI zugegriffen.
Entdeckt wurden dabei ein Fehler im Caching eines Inhaltsmoduls, sowie viele überflüssige Funktionsaufrufe. Im Code kapselt ein Inhalts-Objekt alle dazu gehörigen Informationen, jedoch wurden bei der Ausgabe nicht in jedem Fall alle Angaben benötigt. Dennoch hielt das Objekt alle Informationen vor, teilweise durch Methodenaufruf, aber auch der Memcache und ggf. die Datenbank wurde beansprucht. Die Optimierung bestand darin, alle nicht zwingend notwendigen Methodenaufrufe zu entfernen und ausschliesslich die minimal benötigten Informationen zu behalten, alles andere ist optional. Weiterhin konnten Datenbank-Verbindungen gespart werden, so wurde in einer abstrakten Elternklasse in jedem Fall eine Verbindung zur Datenbank geöffnet, obwohl nicht immer notwendig. Eine Optimierung bestand im Caching der Ergebnisse eines (internen) Web-Services, denn die Nutzung von Zend_Rest_Client erzeugte durchaus einigen Overhead.
Sicherlich gibt es noch etliche Stellen im Code, die hinterfragt und optimiert werden können. Neben der Steigerung der Performance, die in einem Fall um 75% – 100% erhöht werden konnte (validiert durch Benchmarks mit JMeter und Siege), macht der Einblick in den realen Ablauf und deren Optimierung nicht zuletzt Spass – mitunter ausgefeilter Architektur und Struktur zum Trotz.