19.Mesure de temps d'exécution (benchmark) d'une fonction PHP

19.3.Avec PHPBench

19.3.1.Introduction

PHPBench permet d'encapsuler vos fonctions ou méthodes (sans impact sur le code de votre bibliothèque) afin d'en mesurer les performances (délais d'exécution). L'utilisation de PHPBench demande une installation préalable et l'écriture d'un petit peu de code mais le jeu peut en valoir la chandelle. Le principe de mise en place est assez similaire à la mise en place de tests avec PHPUnit[c'est quoi?].

19.3.2.Installation

19.3.2.1.Introduction

Vous pouvez choisir d'installer PHPBench comme une commande de votre système ou bien comme un élément de votre projet (via composer[c'est quoi?]).

19.3.2.2.Installation en tant que commande (indépendante du projet)

Pour cela, vous devrez télécharger les fichiers phpbench.phar et phpbench.phar.pubkey, les copier dans votre répertoire préféré (idéalement, un répertoire répertorié dans le PATH de votre environnement. Typiquement /usr/local/bin). Vous devrez vous assurer d'avoir bien mis les droits d'exécution sur le fichier initialement en .phar. Fichier que vous pourrez renommer sans l'extension (pour avoir une commande phpbench plutôt que phpbench.phar). Pensez à renommer le fichier .phar.pubkey selon le même modèle.
Ceci peut se faire en ligne de commande, comme suit, si vous avez installé curl:
curl -o phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar
curl -o phpbench.phar.pubkey https://phpbench.github.io/phpbench/phpbench.phar.pubkey
chmod 0755 phpbench.phar
sudo mv phpbench.phar /usr/local/bin/phpbench
sudo mv phpbench.phar.pubkey /usr/local/bin/phpbench.pubkey
Et voilà, vous disposez maintenant de la commande phpbench!

19.3.2.3.Installation en tant que dépendance du projet

Dans l'espace racine de votre projet lancez la commande suivante afin d'ajouter phpbench aux dépendances (de développement) du projet.
composer require phpbench/phpbench @dev --dev

19.3.3.Utilisation

19.3.3.1.Principe de base

Comme nous vous le disions en introduction du chapitre "benchmarking", il faut mettre en place un peu de code pour procéder aux mesures.
En particulier, il faut créer un fichier contenant une classe avec un suffix Bench. Cette classe doit simplement contenir des méthodes préfixées bench qui font appel aux fonctions/méthodes dont on veut mesurer les temps de réponse.
<?php
class MaClassBench
{
    public function benchMaFonction()
    {
        maFonction();
    }
}
Il est alors possible d'obtenir le délai de réponse de la fonction avec la commande > phpbench run MaClasseBench.php --report=default
La commande pourra alors retourner quelque chose comme
PhpBench 0.15-dev (04a1a1b). Running benchmarks.
Using configuration file: /home/phpfacile/monprojet/phpbench.json

\MaClassBench

    benchMaFonction               I0 P0 	[μ Mo]/r: 460.000 460.000 (μs) 	[μSD μRSD]/r: 0.000μs 0.00%

1 subjects, 1 iterations, 1 revs, 0 rejects, 0 failures, 0 warnings
(best [mean mode] worst) = 460.000 [460.000 460.000] 460.000 (μs)
⅀T: 460.000μs μSD/r 0.000μs μRSD/r: 0.000%
suite: 133ee170930a6209dff27608562758c4220e8b2c, date: 2018-05-02, stime: 22:16:38
+--------------+-----------------+--------+--------+------+------+----------+-----------+--------------+----------------+
| benchmark    | subject         | groups | params | revs | iter | mem_peak | time_rev  | comp_z_value | comp_deviation |
+--------------+-----------------+--------+--------+------+------+----------+-----------+--------------+----------------+
| MaClassBench | benchMaFonction |        | []     | 1    | 0    | 516,448b | 460.000μs | 0.00σ        | 0.00%          |
+--------------+-----------------+--------+--------+------+------+----------+-----------+--------------+----------------+
Dans ce cas, la fonction n'a été exécutée qu'une seule fois, la résultat obtenu ne peut donc pas être considéré comme probant. Le délai d'exécution de la fonction peut très bien avoir été perturbé par des ralentissements sur le système. Pour pouvoir juger de la fiabilité de la mesure, il convient de multiplier les appels à la fonctions.

19.3.3.2.Fiabilisation de la mesure avec les révolutions et itérations

Pour multiplier les itérations, PHPBench propose 2 paramètres qu'il est possible d'indiquer sous forme d'annotation dans le code PHP. Le premier @Revs permet d'indiquer le nombre de fois où le code sera exécuté au sein d'une même mesure afin d'en faire une moyenne (temps d'exécution = temps mesuré/nombre de révolutions), le second @Iterations permet d'indiquer le nombre de fois où l'on va faire la mesure (afin d'en évaluer la stabilité).
<?php
class MaClassBench
{
    /**
     * @Revs(30)
     * @Iterations(3)
     */
    public function benchMaFonction()
    {
        maFonction();
    }
}
La classe ainsi modifiée, le résultat pourra être comme suit:
\MaClassBench

    benchMaFonction               I2 P0 	[μ Mo]/r: 471.522 471.720 (μs) 	[μSD μRSD]/r: 2.409μs 0.51%

1 subjects, 3 iterations, 30 revs, 0 rejects, 0 failures, 0 warnings
(best [mean mode] worst) = 468.533 [471.522 471.720] 474.433 (μs)
⅀T: 1,414.567μs μSD/r 2.409μs μRSD/r: 0.511%
suite: 133ee177380f978b3a8541c0b84d19c1a337b1b1, date: 2018-05-02, stime: 23:11:08
+--------------+-----------------+--------+--------+------+------+----------+-----------+--------------+----------------+
| benchmark    | subject         | groups | params | revs | iter | mem_peak | time_rev  | comp_z_value | comp_deviation |
+--------------+-----------------+--------+--------+------+------+----------+-----------+--------------+----------------+
| MaClassBench | benchMaFonction |        | []     | 30   | 0    | 516,448b | 468.533μs | -1.24σ       | -0.63%         |
| MaClassBench | benchMaFonction |        | []     | 30   | 1    | 516,448b | 471.600μs | +0.03σ       | +0.02%         |
| MaClassBench | benchMaFonction |        | []     | 30   | 2    | 516,448b | 474.433μs | +1.21σ       | +0.62%         |
+--------------+-----------------+--------+--------+------+------+----------+-----------+--------------+----------------+
On peut préférer dans ce cas, un affichage plus synthétique. On pourra alors opter pour le rapport aggregate > phpbench run MaClasseBench.php --report=aggregate
\MaClassBench

    benchMaFonction               I2 P0 	[μ Mo]/r: 471.211 472.682 (μs) 	[μSD μRSD]/r: 2.560μs 0.54%

1 subjects, 3 iterations, 30 revs, 0 rejects, 0 failures, 0 warnings
(best [mean mode] worst) = 467.600 [471.211 472.682] 473.233 (μs)
⅀T: 1,413.633μs μSD/r 2.560μs μRSD/r: 0.543%
suite: 133ee186f723807c01d3a791357b741fa34e6919, date: 2018-05-04, stime: 12:14:50
+--------------+-----------------+--------+--------+------+-----+----------+-----------+-----------+-----------+-----------+---------+--------+-------+
| benchmark    | subject         | groups | params | revs | its | mem_peak | best      | mean      | mode      | worst     | stdev   | rstdev | diff  |
+--------------+-----------------+--------+--------+------+-----+----------+-----------+-----------+-----------+-----------+---------+--------+-------+
| MaClassBench | benchMaFonction |        | []     | 30   | 3   | 516,448b | 467.600μs | 471.211μs | 472.682μs | 473.233μs | 2.560μs | 0.54%  | 1.00x |
+--------------+-----------------+--------+--------+------+-----+----------+-----------+-----------+-----------+-----------+---------+--------+-------+

19.3.3.3.Autoloader

L'exemple donné ci-dessus n'est pas totalement représentatif de l'utilisation de PHPBench dans le context d'un véritable projet. En effet, les fonctions ou plus surement les méthodes que vous voudrez tester seront probablement dans des fichiers inclus automatiquement via un fichier autoload.php. Pour que PHPBench puisse fonctionner vous devrez donc lui indiquer comment charger ces fichiers. En d'autres termes vous devrez lui indiquer où trouver le fichier autoload.php. Pour cela, vous devrez ajouter à la racine de votre projet un fichier phpbench.json dont le contenu typique est
{
    "bootstrap": "vendor/autoload.php"
}

19.3.3.4.Enregistrement des résultats

Il est très simple d'enregistrer les résultats des benchs. Pour cela, il suffit d'ajouter l'option --store. Eventuellement, vous pouvez attribuer une étiquette a cet enregistrement via l'option --tag=<etiquette>. En l'absence d'étiquette, il sera toujours possible de faire référence à cet enregistrement via l'identifiant unique automatiquement assigné par PHPBench au test sous la forme d'une chaîne de 40 lettres et chiffres (à la façon des identifiants de commit dans git). > phpbench run MaClasseBench.php --report=default --store --tag=maFonctionVUn
rem
  • PHPBench n'est toujours qu'en version que l'on qualifiera de beta. Et bien que le libellé est censé devoir être composé de caractères alphanumériques ou du caractère souligné (undescore), il s'avère qu'avec la version 0.15-dev (mai 2018), l'étiquette ne peut pas contenir de chiffre (ainsi "maFonctionV1" est rejeté)

19.3.3.5.Consultation des enregistrements

Pour consulter la liste et le résumé des résultats enregistrés, vous pouvez faire appel (là, encore à l'image de ce qu'il se fait avec git) à la commande log > phpbench log
Pour consulter le détail d'un enregistrement en particulier vous ferez appel à la commande show suivi de l'identifiant (complet) du test (i.e. les 40 caractères) ou bien suivi de "tag:" puis l'étiquette assignée au moment de l'enregistrement > phpbench show 133ee177380f978b3a8541c0b84d19c1a337b1b1 ou > phpbench show tag:maFonctionVUn
Il est également possible de consulter le dernier enregistrement via > phpbench show latest
Ou bien un N-ième enregistrement précédent via latest-<n>. Ainsi la commande suivante retourne l'antépénultième (i.e. l'avant avant dernier) enregistrement. > phpbench show latest-2

19.3.3.6.Différent formats de rapport

PHPBench propose différents formats de rapport default, aggregate et ils peuvent être personnalisés avec les modes generator et extend.
PHPBench propose également un rapport sur l'environnement de test env (mais ces données ne semblent pas vouloir être enregistrées avec l'option --store) > phpbench run MaClasseBench.php --report=dev