Close search
Hoa

Hack book de Hoa\Bench

Analyser les performances de ses algorithmes ou de ses programmes est une tâche courante, à laquelle Hoa\Bench permet de répondre.

Table des matières

  1. Introduction
  2. Placer des marques
    1. Utilisation rapide
    2. Bien manipuler les marques
    3. Filtrer les marques
  3. DTrace
  4. Conclusion

Introduction

Hoa\Bench propose deux manières d'analyser son application : avec PHP lui-même, ou alors avec DTrace, grâce à des programmes présents dans le dossier hoa://Library/Bench/Dtrace/.

Placer des marques

La bibliothèque Hoa\Bench fonctionne de manière très intuitive : il suffit de placer des marques dans notre programme et c'est tout. À chaque fois qu'une marque est exécutée, elle récolte des informations que nous pouvons exploiter par la suite.

Il y a deux classes à considérer : Hoa\Bench\Bench qui permet de regroupe un ensemble de marques, et Hoa\Bench\Mark qui représente une seule marque.

Utilisation rapide

L'utilisation la plus courante de Hoa\Bench\Bench sera la suivante : placer quelques marques et afficher leurs statistiques. Ainsi :

$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%
 */

Nous remarquons que pour déclarer une marque, nous faisons simplement appel à un attribut sur la classe Hoa\Bench\Bench ce qui va retourner une marque (ou la créer avant si elle n'existe pas). Une fois que nous avons notre marque, nous pouvons la démarrer avec la méthode Hoa\Bench\Mark::start. Pour arrêter les marques, nous appelons la méthode Hoa\Bench\Mark::stop. Enfin, afficher l'objet Hoa\Bench\Bench va afficher les statistiques des marques : leur nom, un petit graphique, leur temps d'exécution et le pourcentage de ce temps par rapport aux autres marques.

Bien manipuler les marques

Les marques peuvent être dans trois états différents :

Si nous démarrons puis arrêtons une marque, pour ensuite la démarrer à nouveau, elle sera remise à zéro. Toutefois, il existe la méthode Hoa\Bench\Mark::reset qui aura le même effet mais en forçant une remise à zéro quelque soit l'état de la marque. Ainsi :

$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%
 */

Nous pouvons également mettre en pause toutes les marques et récupérer quelles marques étaient en cours d'exécution à ce moment grâce à la méthode Hoa\Bench\Bench::pause. La méthode Hoa\Bench\Bench::resume permet de redémarrer un ensemble de marques.

D'autres opérations sont possibles, comme :

Nous avons précisé qu'un objet Hoa\Bench\Bench permettait de regrouper plusieurs marques ensemble. Nous avons vu comment déclarer une marque, mais nous avons d'autres opérations comme savoir si une marque existe ou alors la supprimer avec les constructions de PHP correspondantes :

$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);
}

Bien entendu, nous pouvons supprimer toutes les marques grâce à la méthode Hoa\Bench\Bench::unsetAll. De plus, la classe Hoa\Bench\Bench se comporte comme un itérateur sur toutes les marques, ainsi :

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

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

Nous pouvons également compter le nombre de marques de la manière suivante :

var_dump(count($bench));

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

Enfin, nous pouvons obtenir des statistiques grâce aux méthodes Hoa\Bench\Bench::getStatistic et Hoa\Bench\Bench::getLongest, respectivement pour obtenir les statistiques sous forme de tableau correspondant au graphique vu précédemment et pour obtenir la marque qui a la plus longue exécution.

Filtrer les marques

La classe Hoa\Bench\Bench permet de filtrer les marques lors de calculs de résultats (par example avec la méthode Hoa\Bench\Bench::getStatistic ou Hoa\Bench\Bench::__toString pour dessiner le graphique). Un filtre est un callable (par exemple une fonction) qui reçoit trois arguments : l'identifiant de la marque, sa durée d'exécution en millisecondes et en pourcentage (par rapport à la marque la plus longue). Un filtre est également un prédicat, c'est à dire que son résultat est un booléen : true pour accepter la marque, false pour la rejeter.

Par exemple, si nous avons beaucoup de marques et que nous voulons filtrer le « bruit », c'est à dire les marques avec des petits temps d'exécutions (disons inférieur à 5%), nous pouvons utiliser le filtre suivant :

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

Si nous nommons nos marques avec une nomenclature particulière, nous pouvons filtrer à partir de leurs identifiants. Par exemple, nous voulons toutes les marques comportant le sous-mot _critical :

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

Nous pouvons bien sûr ajouter plusieurs filtres.

Notons que la méthode Hoa\Bench\Bench::getStatistic permet de ne pas considérer les filtres grâce à son seul argument $considerFilters, par défaut à true. Il suffit de le passer à false pour avoir tous les résultats.

DTrace

La bibliothèque Hoa\Bench fournit également des programmes DTrace dans le dossier hoa://Library/Bench/Dtrace/. DTrace permet de générer des traces afin de détecter des problèmes en temps réel dans un environnement de production. Il est intégré au niveau du noyau et au niveau applicatif, ce qui lui assure d'excellentes performances. DTrace utilise le langage D. Nous conseillons également la lecture de DTraceBook. Attention, DTrace n'est pas disponible sur toutes les plateformes.

PHP peut utiliser DTrace s'il a été compilé avec l'option --enable-dtrace (voir DTraces probes for PHP).

Nous allons commencer par analyser l'exécution du fichier Dtrace.php suivant :

<?php

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

f();

Ce fichier est très simple mais il va nous servir de test pour exécuter le premier programme DTrace, à savoir hoa://Library/Bench/Dtrace/Execution.d. Attention, il faut exécuter DTrace en étant super-utilisateur ; ainsi :

$ 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

Voyons si nous ajoutons des erreurs et des exceptions dans notre fichier Dtrace.php :

<?php

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

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

Et réexécutons notre programme DTrace :

$ 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

Le second programme DTrace est hoa://Library/Bench/Dtrace/Stat.d et permet de collecter quelques statistiques sur l'exécution comme la liste de toutes les fonctions et méthodes appelées avec leur nombre d'appels et la distribution du temps d'exécution. Par exemple, si nous essayons sur notre première version de Dtrace.php, nous allons avoir :

$ 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

Ce programme DTrace est très utile pour connaître quelles sont les fonctions ou méthodes les plus appelées dans votre programme et de connaître également leur temps d'exécution moyen. Autant dire que pour détecter les sections critiques d'un point de vue performance, ce programme apporte une véritable réponse et rapidement !

Conclusion

Hoa\Bench permet d'analyser l'exécution de programmes PHP à l'aide de PHP lui-même ou de DTrace. Ces outils sont utiles pour améliorer les performances en permettant de détecter des goulots d'étranglements, des appels trop nombreux, des ralentissements etc., afin de les corriger. Bref, tout ce qu'il faut pour obtenir des programmes de qualité.

Une erreur ou une suggestion sur la documentation ? Les contributions sont ouvertes !

Comments

menu