Close search
Hoa

Hack book of Hoa\Bench

Analyzing performances of algorithms or programs is a very common task, to which Hoa\Bench answers.

Table of contents

  1. Introduction
  2. Place marks
    1. Quick usage
    2. Manipulate marks
    3. Filter marks
  3. DTrace
  4. Conclusion

Introduction

Hoa\Bench provides two ways to analyze an application: with PHP itself, or with DTrace, thanks to program written in the hoa://Library/Bench/Dtrace/ directory.

Place marks

The Hoa\Bench library has a very intuitive use: we only need to place mark in the program and that's all. Each time a mark is executed, it collects informations we will exploit.

There is two classes to know: Hoa\Bench\Bench which allows to group a set of marks, and Hoa\Bench\Mark which represents a single mark.

Quick usage

The most common usage of Hoa\Bench\Bench is to place some marks and print their statistics. Thus:

$bench = new Hoa\Bench\Bench();

// Start two marks: “one” and “two”.
$bench->one->start();
$bench->two->start();

usleep(50000);

// Stop the mark “two” and start the mark “three”.
$bench->two->stop();
$bench->three->start();

usleep(25000);

// Stop all marks.
$bench->three->stop();
$bench->one->stop();

// Print statistics.
echo $bench;

/**
 * Will output:
 *     __global__  ||||||||||||||||||||||||||||||||||||||||||||||||||||    77ms, 100.0%
 *     one         ||||||||||||||||||||||||||||||||||||||||||||||||||||    77ms,  99.8%
 *     two         ||||||||||||||||||||||||||||||||||                      51ms,  65.9%
 *     three       ||||||||||||||||||                                      26ms,  33.9%
 */

Note, to declare a mark we simply call an attribute on the class Hoa\Bench\Bench, which will return a mark (or create if it does not exist). Once we have a mark, we can start it by calling the Hoa\Bench\Mark::start method. To stop it, we call the Hoa\Bench\Mark::stop method. Finally, printing the Hoa\Bench\Bench object will print the statistics of marks: their name, a little graph, their execution time and a percentage according to all marks.

Manipulate marks

Marks can be in three different states:

If we start and stop a mark, and then start it again, it will be reset. Notwithstanding, it exists the Hoa\Bench\Mark::reset method that will have the same behavior while forcing the reset whatever the state of the mark. Thus:

$bench = new Hoa\Bench\Bench();
$mark  = $bench->aMark;
$mark->start(); // =  0ms
usleep(10000);
$mark->pause(); // = 10ms
usleep(10000);
$mark->start(); // = 10ms
usleep(10000);
$mark->stop();  // = 20ms
echo $bench;

/**
 * Will output:
 *     __global__  ||||||||||||||||||||||||||||||||||||||||||||||||||||    33ms, 100.0%
 *     aMark       ||||||||||||||||||||||||||||||||||                      22ms,  66.3%
 */

$mark->reset();
$mark->start(); // =  0ms
usleep(10000);
$mark->stop();  // = 10ms
echo $bench;

/**
 * Will output:
 *     __global__  ||||||||||||||||||||||||||||||||||||||||||||||||||||    45ms, 100.0%
 *     aMark       |||||||||||||                                           11ms,  24.8%
 */

We can also pause all marks and get which marks were running with the Hoa\Bench\Bench::pause method. The Hoa\Bench\Bench::resume method allows to restart a set of marks.

Other operations are available, such as:

We said that an object Hoa\Bench\Bench allows to group several marks together. We have seen how to declare a mark, but we can also check if a mark exists or remove it with PHP native constructions:

$bench = new Hoa\Bench\Bench();

// Create a mark.
$bench->foo;

// Is “foo” exists?
if (isset($bench->foo)) {
    // Then, delete it, for example.
    unset($bench->foo);
}

Obviously, we can remove all marks thanks to the Hoa\Bench\Bench::unsetAll method. In addition, the Hoa\Bench\Bench class behaves like an iterator on all marks, thus:

foreach ($bench as $id => $mark) {
    echo $id, ' => ', ($mark->isRunning() ? 'running' : 'stopped'), "\n";
}

/**
 * Could output:
 *     one => stopped
 *     two => running
 *     three => running
 */

We can also count the number of marks:

var_dump(count($bench));

/**
 * Will output:
 *     int(3)
 */

Finally, we can get statistics thanks to the Hoa\Bench\Bench::getStatistic or Hoa\Bench\Bench::getLongest methods, respectively to get statistics in an array corresponding to the output previously seen and to get the longest mark in term of execution time.

Filter marks

The Hoa\Bench\Bench class allows to filter marks during the computation of results (example with the Hoa\Bench\Bench::getStatistic or Hoa\Bench\Bench::__toString to draw the graph). A filter is a callable (for example a function) which receives three arguments: the identifier of the mark, its execution time in milliseconds and a percentage (according to the longest mark). A filter is also a predicate, it means that its result is a boolean: true to accept the mark, false to reject it.

For example, if we have a lot of marks and we would like to filter the “noise”, it means marks with a low execution time (let say, lower than 5%), we can use the following filter:

$bench->filter(function ($id, $time, $pourcent) {
    return $pourcent > 5;
});

If we name our marks with a specific classification, we can filter according to the name of identifiers. For example, we would like all the marks that contain the sub-word _critical:

$bench->filter(function ($id, $time, $pourcent ) {
    return 0 !== preg_match('#_critical#', $id);
});

Obviously, we can add many filters.

Notice that the Hoa\Bench\Bench::getStatistic method allows to not consider filters thanks to its sole argument $considerFilters, which is set to true by default. To get all the result, we have to set it to false.

DTrace

The Hoa\Bench library also provides DTrace programs in the hoa://Library/Bench/Dtrace/ directory. DTrace allows to generate traces in order to detect issues in realtime in a production environment. It is integrated into the kernel level and applicative level, which ensures excellent performances. DTrace uses the D language. We also recommend reading the DTraceBook. Attention, DTrace is not available on all platforms.

PHP is able to use DTrace if it has been compiled with the --enable-dtrace option (see DTraces probes for PHP).

We will start by analyzing the execution of the following Dtrace.php file:

<?php

function f() { g(); h(); }
function g() { h();      }
function h() {           }

f();

This file is very simple but it will be our test to execute the first DTrace program, namely hoa://Library/Bench/Dtrace/Execution.d. Attention, we have to execute DTrace as a super-user; thus:

$ exed=`hoa protocol:resolve hoa://Library/Bench/Dtrace/Execution.d`
$ sudo $exed -c "php Dtrace.php"
Request start
     2ms ➜ f()        …/Dtrace.php:007
    37ms   ➜ g()      …/Dtrace.php:003
    26ms     ➜ h()    …/Dtrace.php:004
    28ms     ← h()
    37ms   ← g()
    44ms   ➜ h()      …/Dtrace.php:003
    25ms   ← h()
    30ms ← f()
Request end

Let's see what happens if we add errors and exceptions in Dtrace.php:

<?php

function f() { ++$i; g(); h();             }
function g() { h();                        }
function h() { throw new Exception('foo'); }

try                   { f(); }
catch (\Exception $e) {      }
  

And now, reexecute the DTrace program:

$ sudo $exed -c "php Dtrace.php"
Request start
     2ms ➜ f()                              …/Dtrace.php:007
✖          Undefined variable: i            …/Dtrace.php:003
    37ms   ➜ g()                            …/Dtrace.php:003
    26ms     ➜ h()                          …/Dtrace.php:004
●              Exception has been thrown
    28ms     ← h()
    37ms   ← g()
    44ms   ➜ h()                            …/Dtrace.php:003
    25ms   ← h()
    30ms ← f()
✔          Exception has been caught
Request end

The second DTrace program is hoa://Library/Bench/Dtrace/Stat.d and allows to collect some statistics on an execution such as the list of all called functions and methods with their respective invocations number and the distribution of the execution time. For example, if we try with our first version of Dtrace.php, we will have:

$ exed=`hoa protocol:resolve hoa://Library/Bench/Dtrace/Stat.d`
$ sudo $exed -c "php Dtrace.php"
Count calls:
  • h                                                                        2
  • g                                                                        1
  • f                                                                        1

Execution distribution (values are in nanoseconds):

  f
           value  ------------- Distribution ------------- count
             256 |                                         0
             512 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
            1024 |                                         0

  h
           value  ------------- Distribution ------------- count
               4 |                                         0
               8 |@@@@@@@@@@@@@@@@@@@@                     1
              16 |                                         0
              32 |                                         0
              64 |                                         0
             128 |                                         0
             256 |@@@@@@@@@@@@@@@@@@@@                     1
             512 |                                         0

  g
           value  ------------- Distribution ------------- count
             128 |                                         0
             256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
             512 |                                         0

Total execution time: 108ms

This DTrace program is very useful to know what are the most invoked functions or methods in your program and to also know their average execution time. This program is a true and quick answer to the problem of detecting critical sections regarding performances!

Conclusion

Hoa\Bench allows to analyze the execution of PHP programs thanks to PHP itself or DTrace. These tools are useful to improve performances by allowing to detect bottlenecks, too numerous calls, slow executions etc., in order to fix them. In short, all you need to get quality.

An error or a suggestion about the documentation? Contributions are welcome!

Comments

menu