Fermer

juin 20, 2018

Optimisation des performances au niveau PHP avec Blackfire –


Cet article fait partie d'une série de sur la construction d'un exemple d'application – un blog de galerie multi-images – pour l'analyse comparative des performances et les optimisations. (Voir le repo ici.)


Au cours des derniers mois, nous avons introduit Blackfire et les façons dont il peut être utilisé pour détecter les goulets d'étranglement des performances des applications. Dans ce post, nous allons l'appliquer à notre projet fraîchement commencé pour essayer de trouver les points faibles et faciles à utiliser que nous pouvons choisir pour améliorer les performances de notre application.

Si vous utilisez Homestead Improved (et vous devriez l'être), Blackfire est déjà installé. Blackfire devrait seulement être installé en développement, pas en production, donc c'est bien de l'avoir seulement là.

Note: Blackfire peut être installé en production, car il ne déclenche pas vraiment pour les utilisateurs à moins ils l'initient manuellement avec l'extension Blackfire installée. Cependant, il vaut la peine de noter que la définition de déclencheurs de profil sur certaines actions ou les utilisateurs qui n'ont pas besoin de l'extension entraînera une pénalité de performance pour l'utilisateur final. Lorsque le test Blackfire est en cours, rendez les sessions de test courtes et efficaces, et évitez de le faire sous forte charge.

Bien qu'il soit utile d'être initié à Blackfire avant d'y plonger, l'application des étapes de ce post ne nécessitera aucune connaissance; nous allons commencer à partir de zéro

Setup

Les termes suivants sont des termes utiles pour évaluer les graphiques produits par Blackfire:

  • Reference Profile : Nous devons généralement exécuter notre premier profil comme profil de référence. Ce profil sera la référence de performance de notre application. Nous pouvons comparer n'importe quel profil avec la référence, pour mesurer les accomplissements de performance

  • Exclusive Time : Le temps passé sur une fonction / méthode à exécuter, sans tenir compte du temps passé pour ses appels externes. ] Temps d'intégration : temps total passé à exécuter une fonction incluant tous les appels externes

  • Hot Paths : Les chemins d'accès actifs sont les parties de notre application les plus actives pendant le profil. Ceux-ci pourraient être les parties qui ont consommé plus de mémoire ou ont pris plus de temps de CPU.

La première étape est l'enregistrement pour un compte à Blackfire . Le compte aura les jetons et les ID qui doivent être placés dans Homestead.yaml après le clonage du projet. Il y a un espace réservé pour toutes ces valeurs en bas:

 # blackfire:
# - id: foo
# jeton: barre
# client-id: foo
# client-token: barre

Après décommenter les lignes et remplacer les valeurs, nous devons installer le compagnon Chrome .

Le compagnon Chrome est utile uniquement lorsque vous avez besoin de déclencher le profilage manuellement – ce qui sera la majorité de votre utilisation cas. Il y a d'autres intégrations disponibles, dont une liste complète peut être trouvée ici

Optimisation avec Blackfire

Nous allons tester la page d'accueil: la page d'atterrissage est sans doute la partie la plus importante de tout site Web, et si cela prend trop de temps à charger, nous sommes sûrs de perdre nos visiteurs. Ils seront partis avant que Google Analytics puisse entrer pour enregistrer le rebond! Nous pourrions tester les pages sur lesquelles les utilisateurs ajoutent des images, mais les performances en lecture seule sont bien plus importantes que les performances en écriture, donc nous nous concentrerons sur les premières.

Cette version charge toutes les galeries et les trie selon l'âge.

Les tests sont simples. Nous ouvrons la page que nous voulons benchmark, cliquez sur le bouton de l'extension dans le navigateur, et sélectionnez "Profil!".

Voici le graphique résultant:

En fait, nous pouvons voir ici que le temps d'exécution inclusif à exclusif est de 100% sur l'exécution de l'AOP. Concrètement, cela signifie que toute la partie rose foncé est passée à l'intérieur de cette fonction et que cette fonction en particulier n'attend aucune autre fonction. C'est la fonction qui est attendue. D'autres appels de méthode peuvent avoir des barres rose clair beaucoup plus grandes que les PDO, mais ces parties rose clair sont une somme de toutes les petites parties rose clair des fonctions dépendantes, ce qui signifie que ces fonctions ne sont pas le problème. Les sombres doivent être traités en premier;

En outre, le passage en mode RAM révèle que, alors que l'ensemble de l'appel utilise presque 40 Mo de RAM, la grande majorité est dans le rendu Twig, ce qui est logique: il montre beaucoup de données, après

 Mode RAM

Dans le diagramme, les chemins chauds ont des bordures épaisses et indiquent généralement des goulots d'étranglement. Les nœuds intensifs peuvent faire partie du chemin chaud, mais aussi être complètement à l'extérieur. Les noeuds intensifs sont des noeuds dans lesquels on passe beaucoup de temps et qui peuvent tout aussi bien indiquer des problèmes.

En regardant les méthodes les plus problématiques et en cliquant sur les noeuds pertinents, on peut identifier que PDOExecute est le plus goulot d'étranglement problématique, tandis que unserialize utilise le plus de RAM par rapport aux autres méthodes. Si nous appliquons un travail de détective et suivons le flot de méthodes qui s'appellent l'une l'autre, nous remarquons que ces deux problèmes sont causés par le fait que nous chargeons l'ensemble des galeries sur la page d'accueil. PDOExecute prend une éternité de mémoire et de temps de mur pour les trouver et les trier, et Doctrine prend des âges et des cycles CPU infinis pour les transformer en entités restituables avec unserialize pour les boucler dans une brindille modèle. La solution semble simple – ajouter la pagination à la page d'accueil!

En ajoutant une constante PER_PAGE dans le HomeController et en le mettant à quelque chose comme 12 et puis en utilisant cette constante de pagination dans la procédure de récupération, nous bloquons le premier appel aux 12 nouvelles galeries:

 $ galleries = $ this-> em-> getRepository (Gallery :: class) -> findBy ([]['createdAt' => 'DESC']self :: PER_PAGE);

Nous allons déclencher une charge paresseuse lorsque l'utilisateur atteindra la fin de la page lors du défilement, nous devons donc ajouter quelques JS à la vue d'accueil:

 {% block javascripts%}
    {{ parent() }}

     {% endblock%}

Puisque les annotations sont utilisées pour les routes, il est facile d'ajouter simplement une nouvelle méthode au HomeController pour charger paresseusement nos galeries quand elles sont déclenchées:

 / **
 * @Route ("/ galleries-lazy-load", nom = "home.lazy-load")
 * /
fonction publique homeGalleriesLazyLoadAction (Demande $ request)
{
    $ page = $ request-> get ('page', null);
    if (vide ($ page)) {
        retourne une nouvelle réponse JsonResponse ([
            'success' => false,
            'msg'     => 'Page param is required',
        ]);
    }

    $ offset = ($ page - 1) * self :: PER_PAGE;
    $ galleries = $ this-> em-> getRepository (Galerie :: classe) -> findBy ([]['createdAt' => 'DESC']12, $ offset);

    $ view = $ this-> twig-> render ('partials / home-galleries-lazy-load.html.twig', [
        'galleries' => $galleries,
    ]);

    retourne une nouvelle réponse JsonResponse ([
        'success' => true,
        'data'    => $view,
    ]);
}

Comparaison

Comparons maintenant notre application mise à niveau avec la version précédente en relançant le profileur.

Effectivement, notre site utilise 10 fois moins de mémoire et se charge beaucoup plus rapidement – peut-être pas en temps CPU, comme indiqué par le chronomètre dans le graphique, mais en impression. Le rechargement est maintenant presque instantané

Le graphique nous montre maintenant que DebugClass est l'appel de méthode le plus gourmand en ressources

 DebugClass l'appel de méthode le plus gourmand en ressources

Cela arrive parce qu'on est en mode de développement, et ce chargeur de classe est généralement beaucoup plus lent que celui de production, car il ne cache pas les classes. Cela est nécessaire pour que les modifications effectuées dans le code puissent être immédiatement testées sans avoir à effacer le cache APC ou tout autre cache utilisé.

Si nous passons au mode prod juste pour les besoins de ce test, nous verrons une différence notable:

Conclusion

La ​​vitesse de notre application est maintenant époustouflante – un minuscule 58ms pour charger la page, et aucun chargeur de classe en vue. Rappelez-vous, tout cela se passe dans une machine virtuelle avec des milliers d'entrées de données factices. Nous pouvons nous sentir très optimistes quant à l'état de production de notre application à ce stade: il y a peu ou pas d'optimisations sur la page d'accueil; Tout autre élément peut être classé comme une micro-optimisation.

Relancer régulièrement ces tests de performance est important pour le cycle de développement de toute application, et les intégrer dans le pipeline de test d'une application, comme un flux CD / CI, peut être extrêmement utile. productif. Nous verrons cette option un peu plus tard, mais il est important de noter que l'abonnement premium de Blackfire offre réellement cette chose intégrée. Check it out !

En ce moment, il est important que nous ayons Blackfire installé et disponible, et qu'il peut nous être très utile pour trouver les goulets d'étranglement et en identifier de nouveaux à mesure que nous ajoutons plus de fonctionnalités dans le mix. Bienvenue dans le monde des tests de performance continue!






Source link