Benchmark

Gerade bei Webapplikationen ist Performance extrem wichtig. Kein Besucher wartet freiwillig länger als 10s auf eine Webseite. Um bereits bei der Entwicklung das Thema Performance nicht aus den Augen zu verlieren stellt das Framework mit dem BenchmarkTimer eine Benchmark-Komponente zur Verfügung, mit der alle Ausführungszeiten gemessen und anschließend dargestellt werden können.

Die UI-Schicht (Page-Controller und Front-Controller) unterstützen das Beurteilen der Ausführungszeiten mit vielen bereits eingebauten Messpunkten. Beispielsweise werden die Ausführungszeiten von (Document-)Controller ohne Zutun des Entwicklers aufgenommen und im Report dargestellt.

1. Anwendung des Benchmarkers

Die Anwendung des BenchmarkTimer gestaltet sich sehr einfach. Es ist lediglich folgender Code vor und nach dem zu messenden Events einzufügen:

PHP-Code
$t = Singleton::getInstance(BenchmarkTimer::class); $t->start('MyEvent'); // // Zu messender PHP-Code // $t->stop('MyEvent');

Zu beachten ist dabei lediglich, dass der BenchmarkTimer immer singleton instanziiert werden muss, da sonst Informationen über Teile der Messung verloren gehen können. Es muss zudem auf den Gültigkeitsbereich von Variablen geachtet werden. Sind Messungen über die Grenzen von Funktionen und Klassen hinweg notwendig muss sichergestellt sein, dass die Variable, die die Referenz auf die Instanz des BenchmarkTimers nicht null enthält. Hier ist es ratsam folgenden Code zu verwenden:

PHP-Code
$t = Singleton::getInstance(BenchmarkTimer::class); $t->start('MyEvent'); // // Zu messender PHP-Code // $t = Singleton::getInstance(BenchmarkTimer::class); $t->stop('MyEvent');

Dieser Fall findet jedoch nur in Ausnahmen Anwendung.

2. Generieren eines Reports

Das APF empfielt die Nutzung des Bootstrap-Konzeptes, bei dem eine einzige Datei den Einstieg in die Applikation bildet. Dies ist zumeist die Datei index.php. Dies ermöglicht es auf sehr einfache Weise einen Reports durch

PHP-Code
$t = Singleton::getInstance(BenchmarkTimer::class); echo $t->createReport();

am Ende der index.php zu genrieren. Möchten Sie selbst entscheiden, wann der Report angezeigt wird um auch im Live-Betrieb ein Gefühl für die Performance der Applikation behalten zu können kann das durch das Einfügen des Codes

PHP-Code
if(isset($_REQUEST['benchmarkreport']) && $_REQUEST['benchmarkreport'] == 'true'){ $t = Singleton::getInstance(BenchmarkTimer::class); echo $t->createReport(); }

am Ende der index.php bewerkstelligt werden. Ein Report der aktuell angezeigten Seite hat folgende Gestalt:

Benchmark Report Prozess Baum

Um einen aktuellen Report für diese Seite anzeigen zu lassen, bitte hier klicken.

3. Performance-Tweek

Um die Performance des APF zu verbessern ist es möglich den BenchmarkTimer zu deaktivieren. Dies kann genutzt werden, um die Abarbeitungszeit eines Requests zu steigern. Empirische Werte mit Tests an der vorliegenden Webseite zeigen eine Steigerung um bis zu 15% in deaktiviertem Zustand. Da der BenchmarkTimer in einer Produktions-Umgebungen keine essentielle Funktion erfüllt, ist das Abschalten mit keinen Nachteilen verbunden.

Das Abschalten kann in der Bootstrap-Datei mit folgendem Code erledigt werden:

PHP-Code
$t = Singleton::getInstance(BenchmarkTimer::class); $t->disable();
Um eine möglichst effiziente Abschaltung zu erziehlen, sollten die in der Codebox gezeigten Zeilen direkt nach dem Einbinden der APF/core/bootstrap.php platziert werden.

4. Erweiterung

Bitte beachten Sie, dass dieses Feature erst ab Version 3.2 zur Verfügung steht.

Zur Aufnahme der effektiven Zeiten der einzelnen Mess-Punkte nutzt der BenchmarkTimer intern eine Implementierung des Interfaces StopWatch. Das APF liefert dazu die DefaultStopWatch mit, die Mess-Punkte basierend auf der Klasse DefaultProcess aufzeichnet. Dabei werden der vergebende Name, der Hierarchie-Level des Prozesses und die verbrauchte Zeit gespeichert.

Zur Generierung eines Berichts wird dient eine Implementierung des Interfaces Report. Im Auslieferungszustand sind zwei Formen enthalten: der HtmlReport zeigt die Ergebnisse in einem aufbereiteten HTML-Format an und PlainTextReport generiert eine einfache textuelle Ausgabe.

In den folgenden Kapiteln erfahren Sie wie das Messen der Zeiten sowie die Erzeugung der Berichte beinflusst und erweitert werden kann.

4.1. Stoppuhr

Möchten Sie zur Untersützung eines speziellen Anwendungsfalls die Implementierug der Stoppuhr auszutauschen, dann lässt sich dies nach dem Einbinden der APF/core/bootstrap.php durch Konfiguration wie folgt vornehmen:

PHP-Code
BenchmarkTimer::$watchClass = MyStopWatch::class;

Der BenchmarkTimer nutzt nun die Klasse MyStopWatch um die Messpunkte aufzuzeichnen.

Bitte beachten Sie, dass die Klasse MyStopWatch das Interface StopWatch implementieren muss um zur API des BenchmarkTimer kompatibel zu sein. Das Interface gestaltet sich wie folgt:
PHP-Code
interface StopWatch { public function start($name = null); public function enable(); public function disable(); public function stop($name); public function createReport(Report $report = null); public function getTotalTime(); }
Details zum Interface entnehmen Sie bitte der API-Dokumentation.

4.2. Bericht

Bei der Generierung eines Berichts greift der BenchmarkTimer auf eine Implementierung des Report-Interfaces zurück. Die Methode compile() erhält dazu die Liste der aufgenommenen Mess-Punkte und gibt den fertigen Bericht zurück.

Im Auslieferungszustand erzeugt der BenchmarkTimer bei Aufruf der Methode createReport() einen HTML-Bericht basiend auf der Klasse HtmlReport. Möchten Sie das Format der Ausgabe anpassen, können Sie jederzeit der Methode createReport eine eigene Implementierung mitgeben:

PHP-Code
echo $t->createReport(new PlainTextReport());

Der Bericht wird nun als einfacher Text ausgegeben.

Möchten Sie die Ausgabe der Messpunkte für einen speziellen Anwendungsfall aufbereiten, lässt sich dies sehr einfach mit einer eigenen Implementierung des Report-Interfaces erreichen.

Die folgende Code-Box zeigt eine Beispiel-Implementierung zur Gruppierung der Messpunkte onParseTime(), onAfterAppend() und transform():

PHP-Code
class SummaryPlainTextReport implements Report { public function compile(array $processes) { $onParseTime = 0; $onAfterAppend = 0; $transform = 0; foreach (array_slice($processes, 1) as $process) { /* @var $process Process */ if (strpos($process->getName(), '::onParseTime()') !== false) { $onParseTime += $process->getDuration(); } else if (strpos($process->getName(), '::onAfterAppend()') !== false) { $onAfterAppend += $process->getDuration(); } else if (strpos($process->getName(), '::transform()') !== false) { $transform += $process->getDuration(); } } $buffer = $processes[0]->getName() . ' ' . $processes[0]->getDuration() . 's' . PHP_EOL; $buffer .= 'Sum onParseTime() ' . $onParseTime . 's' . PHP_EOL; $buffer .= 'Sum onAfterAppend() ' . $onAfterAppend . 's' . PHP_EOL; $buffer .= 'Sum transform() ' . $transform . 's' . PHP_EOL; return $buffer; } }
Bitte beachten Sie, dass die abgedruckte Implementierung nur als Beispiel dient und durch die einfache Aufsummierung keine mathematisch korrekten Werte liefert.

Zur Nutzung des neuen Formats übergeben Sie der Methode createReport() einfach eine Instanz der Klasse SummaryPlainTextReport.

Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
Für diesen Artikel liegen aktuell keine Kommentare vor.