Performance is am extremely important topic on web applications. No visitor is likely to wait longer than 10 seconds to see a web page. For this reason a benchmark component was introduced to this framework. With the BenchmarkTimer component is is easy to control the performance of an application or just a single module during development. The component measures all relevant parts by setting measuring points within application parts and contains methods to display the process tree of the applications performance values.

The Page controller and Front controller already include a lot of important measuring points to guarantee accurate performance tracking out-of-the-box. In example, the performance of each (Document-)Controller is recorded by default.

1. Benchmarker practice

Benchmarking applications or parts of an application is quite easy. To measure a particular part of the code, the following code must be introduced to the program:

PHP code
use APF\core\singleton\Singleton; use APF\core\benchmark\BenchmarkTimer; $t = Singleton::getInstance(BenchmarkTimer::class); $t->start('MyEvent'); // // code to be benchmark'ed // $t->stop('MyEvent');

Please note, that the BenchmarkTimer must always be instantiated singleton style, because all timing information have to be stored in a single instance of the BenchmarkTimer to generate a proper process tree. If this is fact is not kept in mind, benchmark information might be lost. Further, the scope of applications must be considered. This means, that breakpoints throughout functions or classes determine, that the instance of the BenchmarkTimer must be fetched by using the Singleton class every time start() or stop() is called. In such situations, the following code can be used:

PHP code
$t = Singleton::getInstance(BenchmarkTimer::class); $t->start('MyEvent'); // // code to be benchmark'ed // $t = Singleton::getInstance(BenchmarkTimer::class); $t->stop('MyEvent');

Please note that this case is rather seldom.

2. Benchmark reports

APF is designed to implement the bootstrap concept where one single file is used as the main entry point of the application. Most of the time this file is index.php. This allows you to easily integrate report generation by adding

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

at the end of your index.php file. If you want to dynamically decide when to display the report within your production system to get a better sense of performance you may want to use the following code snippet:

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

If this parameter is present the following report will be displayed at the end of the HTML page:

benchmark report process tree

To view a live benchmark please click here.

3. Performance tweak

In order to increase the performance it is possible to deactivate the benchmarker. This can be used to reduce the time needed for request processing. Experimental tests proof, the request processing can be boosted on 15% with the benchmarker being deactivated. Because the benchmarker fulfils no necessary function in production environments, switching off the tool is not combined with any disadvantages.

Switching off the benchmarker can be done using the subsequent code snippet:

PHP code
$t = Singleton::getInstance(BenchmarkTimer::class); $t->disable();
In order to guarantee efficient disabling, the above presented lines of code should be directly placed after the inclusion of the APF/core/bootstrap.php file.

4. Extension

Please note that this feature is available starting with version 3.2.

Recording effective times for dedicated samples the BenchmarkTimer uses a StopWatch implementation internally. As a standard implementation, the APF contains DefaultStopWatch recording events based on class DefaultProcess. With each event the name of the process, the hierarchy level, and the duration time is stored.

Report generation is conducted based on an implementation of the Report interface. The basic setup contains two types of reports: HtmlReport shows results as formatted HTML and PlainTextReport generates a simple text representation.

Thw following chapters provide details on how time measuring and report generation can be influenced and extended.

4.1. Stop watch

In case you want to exchange the implementation of the stop watch delivered along with the APF you can adapt it by configuring a custom class right after including APF/core/bootstrap.php:

PHP code
BenchmarkTimer::$watchClass = MyStopWatch::class;

The BenchmarkTimer now uses class MyStopWatch to record events.

Please note that MyStopWatch must be implementing interface StopWatch to be compatible with BenchmarkTimer's API. The interface is as follows:
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(); }
Please see API documentation for further details.

4.2. Report

For report generation the BenchmarkTimer uses an implementation of the Report interface. Method compile() will return the report representation applying the list of events as an argument.

With default configuration the BenchmarkTimer creates an HTML report based on HtmlReport calling createReport(). To adapt the output you may want to provide a custom implementation while calling createReport:

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

Now, the report will be presented as plain text.

In case you want to adapt the presentation of the events recorded you can use above mechanism to apply your own implementation of the Report interface easily.

The following code box shows an implementation that groups onParseTime(), onAfterAppend(), and transform() events:

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; } }
Please note that above implementation is meant as an example and due to it's simple implementation does not produce accurate values.

Using the new format please provide an instance of SummaryPlainTextReport while calling createReport().


Do you want to add a comment to the article above, or do you want to post additional hints? So please click here. Comments already posted can be found below.
There are no comments belonging to this article.

In order to provide a state-of-the-art web experience and to continuously improve our services we are using cookies. By using this web page you agree to the use of cookies. For more information, please refer to our Privacy policy.