• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 25/08/2020

Validez un correctif de performance

Vous y êtes ! Il est plus que temps d'utiliser Blackfire et d'effectuer quelques modifications sur le projet de démonstration pour en améliorer la performance :soleil:.

Pour commencer, nous allons essayer d'optimiser la page d'accueil du projet de démonstration.

Comparaison de profils

La plateforme Blackfire.io est tout à fait capable de comparer deux profils ; c'est même sa principale fonctionnalité !

Si l'on regarde le graphe généré pour la page d'accueil du projet de démonstration, les fonctions les plus coûteuses en temps et en mémoire concernent le chargement des classes :

Exemple d'un profil Blackfire
Le chargement des classes est très coûteux !

Nous pourrions passer l'application en mode de production, mais si déjà nous agissions sur l'outil en charge du chargement des classes, j'ai nommé Composer ?

En effectuant une recherche dans la documentation, vous noterez qu'il existe une commande capable d'optimiser le chargement des classes ! Exécutez la commande  composer dump-autoload -o  à la racine du projet et relancez une analyse Blackfire pour estimer l'impact de cette commande.

Nommez ce profil "Deuxième profil" et retournez dans l'interface de Blackfire pour effectuer une comparaison entre le premier et le deuxième profil à l'aide du bouton "Compare". Un graphe de comparaison apparaît :

Graphe de comparaison entre 2 profils Blackfire
Graphe de comparaison entre 2 profils Blackfire

Wow ! Par une simple action, sans même changer le code de notre application, nous avons amélioré la performance de la page d'accueil.

Tout ce qui est noté en bleu est une amélioration de performance, et tout ce qui est noté en rose est une baisse de performance. Vous remarquez que nous avons optimisé le nombre d'appels à la fonction ClassLoader::findFile  de Composer de 595 appels à ... 0  :p !

Optimisation de la page "Liste d'articles"

Passez maintenant l'application en mode de production pour ne pas être biaisé par la perte de performance induite par l'utilisation du Web Profiler, par exemple.

Pour accéder à la liste d'articles, il faut naviguer vers l'admin et s'authentifier en tant qu'administratrice (Jane est administratrice de l'application). Cette page étant un peu plus complète que la précédente, vous devriez pouvoir trouver une optimisation pour en améliorer la performance.

Regardez les premières fonctions du graphe :

Profil Blackfire de la liste d'articles
Profil Blackfire de la liste d'articles

Nous avons déjà optimisé le chargement des classes précédemment. La seule solution pour optimiser le nombre d'appels à la fonction Autoload::includeFile serait de retirer des classes PHP. Intéressons-nous plutôt à la fonction json_decode appelée 21 fois, qui représente environ 8 % du temps d'exécution et 21 % de la mémoire consommée.

Il va falloir "enquêter" sur l'utilisation de cette fonction au sein de notre application. Cliquez sur la fonction pour découvrir par quelle(s) fonction(s) elle est appelée et remontez jusqu'à trouver la "coupable" :D :

Une fonction Twig responsable de 8% du temps de chargement
La fonction Twig "getLocales" est responsable de 12 % du temps de chargement

De fil en aiguille, en remontant dans le graphe, on voit que c'est la fonction AppExtension::getLocales qui est responsable de ce coût.

Sans même regarder le code, nous apprenons que cette extension est appelée une fois dans le bloc block_header du template base.html.twig, et qu'elle génère pas moins de 18 appels à la fonction LocaleBundle::getLocaleName  qui fait partie du composant Intl de Symfony !

Nous ne pouvons probablement pas optimiser le code du composant Intl, mais peut-être pouvons-nous optimiser le code de la fonction AppExtension::getLocales ?

<?php

/**
 * Takes the list of codes of the locales (languages) enabled in the
 * application and returns an array with the name of each locale written
 * in its own language (e.g. English, Français, Español, etc.).
 *
 * @return array
 */
public function getLocales()
{
    $localeCodes = explode('|', $this->locales);
    $locales = [];
    foreach ($localeCodes as $localeCode) {
        $locales[] = [
            'code' => $localeCode,
            'name' => Intl::getLocaleBundle()
                ->getLocaleName($localeCode, $localeCode)
        ];
    }
    return $locales;
}

D'accord, donc cette fonction va rechercher, pour chaque locale, la langue écrite dans la langue en question. Ces locales sont passées à l'extension et mises en configuration : elles ne devraient pas changer entre deux requêtes HTTP, n'est-ce pas ?

Nous avons là une possibilité de mettre en cache les locales (et leurs noms). Voici le code de la fonction mis à jour :

<?php

private $cache;

public function __construct(
    Markdown $parser,
    CacheItemPoolInterface $cache,
    $locales
)
{
    $this->parser = $parser;
    $this->cache = $cache;
    $this->locales = $locales;
}

public function getLocales()
{
    $cacheLocales = $this->cache->getItem('locales');
    if (!$cacheLocales->isHit()) {
        $localeCodes = explode('|', $this->locales);

        $locales = [];
        foreach ($localeCodes as $localeCode) {
            $locales[] = [
                'code' => $localeCode,
                'name' => Intl::getLocaleBundle()
                    ->getLocaleName($localeCode, $localeCode)
            ];
        }
        $cacheLocales->set($locales);
        $this->cache->save($cacheLocales);

        return $locales;
    }

    return $cacheLocales->get();
}

D'accord... mais est-ce que cette optimisation vaut le coût et n'avons-nous pas rendu notre application plus lente ? Après avoir appliqué les modifications sur la fonction getLocales, établissez un nouveau profil Blackfire et un graphe de comparaison.

Plus précisément, intéressons-nous à la fonction que nous voulions optimiser :

Optimisations de la fonction getLocales
Comparatif des performances de la fonction getLocales

 
Félicitations, vous venez d'améliorer la performance votre application :magicien: !

En résumé

Améliorer les performances d'une application est facile du moment que nous utilisons les bons outils. Avoir la bonne information permet d'agir sur nos outils de production et de déploiement, ainsi que sur le code de notre application. La démarche est simple :

  • établir un premier graphe et agir sur les fonctions les plus coûteuses ;

  • ne pas hésiter à changer de métrique pour mieux comprendre l'impact d'une fonction ;

  • remonter dans "l'historique" du graphe pour identifier la fonction responsable du problème ;

  • adapter vos outils, le code de votre application ;

  • établir un graphe de comparaison ;

  • fêter votre succès :D !

Malgré tout, le procédé reste manuel. Comment être sûr qu'une optimisation réalisée sur une page particulière ne va pas impacter la performance d'une autre page ?

Allons-nous devoir tout analyser à nouveau et comparer un à un chaque profil :-° ?

Bien sûr que non ! Blackfire vous permet d'écrire des tests de performance, de la même façon que vous valideriez le comportement de vos applications avec PHPUnit.

On se retrouve dans la prochaine partie de ce cours pour mettre en place une démarche robuste, fiable sur la durée et automatisable :soleil:.

Exemple de certificat de réussite
Exemple de certificat de réussite