30 octobre 2008

Optimiser une application PHP avec Xdebug.

Cet article tente de décrire la méthode utilisée pour optimiser le code de Pxxo à partir des données générées par Xdebug.

On considère ici que Xdebug est déjà installé.

Activer le profiler Xdebug

Xdebug propose en standard un profiler de code pour PHP. Celui permet de tracer et chronométrer tous les appels de méthodes et de fonctions php natives ou non. Pour activer cette fonctionnalité, il suffit de modifier la configuration de Xdebug. Pour cela, on doit modifier le fichier php.ini ou le fichier xdebug.ini.

Exemple sous Linux Debian avec le fichier xdebug.ini

zend_extension=/usr/lib/php5/20060613+lfs/xdebug.so

xdebug.profiler_append = 1
xdebug.profiler_enable  = 1
xdebug.profiler_output_dir = /data/thouveni/tmp
xdebug.profiler_output_name = cachegrind.out.%s

Écrire un script de test

Le profiler Xdebug génère énormément d'information. Vouloir analyser une application complète me semble inapproprié tant le volume de données produites risque d'être important (plusieurs Giga). Il est donc préférable d'isoler certaines fonctionnalités pour les tester unitairement.

On utilise Xdebug pour analyser une portion de code particulière plutôt qu'une application complète.

Il est pratique de créer un script dédié à l'exécution de la portion de code que l'on a isolée. Idéalement, ce script pourra également rejouer en boucle la partie à évaluer pour mieux faire ressortir les différences entre chaque appel.

Dans le cas de Pxxo, le script dédié est le suivant :

require_once 'Pxxo/Widget/Fake.php';

$p = array();
$p['ResourcePath'] = rtrim(dirname(__FILE__), DIRECTORY_SEPARATOR).'/rsc';   
$p['ResourceURL']  = rtrim(dirname($_SERVER['PHP_SELF']), DIRECTORY_SEPARATOR).'/rsc';
$p['Lang']         = 'fr';
$p['CacheMode']    = true;
$p['CachePath']    = sys_get_temp_dir();

for($z = 0; $z < 200; $z++) {
    $o = new Pxxo_Widget_Fake($p);
    $o->main();
    $o->get();
}

Lancer l'évaluation

Une fois le profiler activé dans la configuration de Xdebug, il suffit de relancer son serveur pour générer des fichiers de log.

Personnellement, j'active le profiler uniquement le temps d'exécuter le script de test. Cela évite de produire inutilement de la log et garantit que la log ne contiendra que les traces de l'appel à mon script.

Bien sûr, il vaut mieux activer le profiler sur un serveur de test et non sur un serveur de production. Encore une fois, trop de requête risque de produire trop de log.

Visualiser les résultats

Xdebug produit des logs au format cachegrind. Il existe à ma connaissance 2 outils libres pour analyser ce type de fichier, l'un sous Windows WinCacheGrind et l'autre sous Linux KCachegrind.

Pour avoir utiliser les deux, je conseille l'outil Linux. Car l'outil Windows n'accepte pas les gros fichiers de log et il n'implémente pas toutes les fonctionnalités proposées par KCachegrind, notamment la génération de carte graphique.

KCachegrind propose une interface en deux parties. La première affiche la liste des appels tracés :

http://www.touv.fr/IMG/png/kcachegring.pxxo.V2.1.png

La seconde propose une vue détaillée de chaque appel. La vue la plus simple à exploiter est celle présentée dans l'onglet "Callee Map". On peut rapidement et visuellement identifier quels sont les sous-appels qui prennent le plus de temps.

http://www.touv.fr/IMG/png/kcachegring.pxxo.V2.2.png

Analyser les résultats

Les graphiques produits par KCachegrind sont peut-être jolis mais il est quand même difficile de les analyser. Voici une façon parmi d'autres de procéder.

Le but est de chercher les appels les plus couteux en temps d'exécution afin de voir si on peut améliorer les choses. Pour cela, il y a plusieurs techniques :

Technique 1.

  • Trier par ordre décroissant la colonne "Self." dans la partie gauche de KCachegrind, de manière à voir tout de suite les appels qui prennent le plus de temps.
  • Repérer ceux qui sont appelés le plus souvent.
  • Repérer ceux qui ont la "Map Callee" avec le moins de couleurs ! Plus sérieusement, il s'agit d'identifier les appels dont le temps d'exécution propre est 4 à 5 fois plus importants que pour les sous-appels.

Technique 2.

Cette technique n'est qu'une variante de la technique précédente.

  • Trier par ordre décroissant la colonne "Called" dans la partie gauche de KCachegrind, de manière à voir tout de suite les appels qui sont le plus souvent appelés.
  • Repérer ceux qui ont le temps d'exécution propre les plus importants.
  • Repérer ceux qui ont la "Map Callee" avec le moins de couleurs !

Exemple avec Pxxo

Si on applique la technique n°1 sur le script de test de Pxxo, on peut remarquer la fonction addCacheID :

http://www.touv.fr/IMG/png/kcachegring.pxxo.V2.a.png

http://www.touv.fr/IMG/png/kcachegring.pxxo.V2.b.png

Cette méthode prend du temps, elle est beaucoup appelée et c'est majoritairement elle qui consomme le plus par rapport à ses sous-appels.

Il convient donc de regarder le code de cette méthode afin de voir :

  • si le code ne peut être amélioré
  • si cette méthode est appelée à bon escient

Dans le cas Pxxo, cette méthode n'est pas appelée à bon escient, Son but est de deviner le type des paramètres qu'on lui envoie afin d'en faire des chaines de caractères qu'elle stockera dans un tableau interne.

Clairement, si dans un programme, on manipule déjà une chaine de caractères, il semble inutile de vouloir deviner son type. Il vaut mieux créer une nouvelle méthode qui se chargera directement de stocker cette chaine dans le tableau interne.

Résultats

http://www.touv.fr/IMG/png/kcachegring.pxxo.V2.c.png

Avant Après
Self. Called Self. Called
addCacheID 5,99 1200 3,84 600
addRawCacheID 0 0 0.06 600
Total 5,99 1200 3,90 1200

Soit un gain de 2,09. Bien sûr, cela n'est pas suffisant, il faut réitérer la technique n°1. Et cette fois-ci, on remarque la méthode loadParamaters.

http://www.touv.fr/IMG/png/kcachegring.pxxo.V3.a.png

http://www.touv.fr/IMG/png/kcachegring.pxxo.V3.b.png

Comme la méthode précédente, elle consomme beaucoup, sans être relativement beaucoup appelée.

Contrairement au cas précédent, cette méthode est appelée à bon escient. C'est donc son code interne qui est mauvais. Il faut donc la réécrire différemment.

Résultats

http://www.touv.fr/IMG/png/kcachegring.pxxo.V4.a.png

http://www.touv.fr/IMG/png/kcachegring.pxxo.V4.b.png

Avant Après
9,30 2,41

Soit un gain de 6,89. Et on peut continuer comme cela autant de fois que l'on souhaite...

Conclusion

Xdebug est un bon outil pour ceux qui souhaitent améliorer finement leur code PHP. Par contre, si le but ultime de l'optimisation est l'amélioration des performances d'une application PHP alors avant d'utiliser Xdebug, il convient de regarder ailleurs. Car si Xdebug permet d'optimiser son code PHP, les gains obtenus sont dans la plupart des cas minimes et ils ne règleront probablement pas les gros problèmes de lenteur.

Aucun commentaire:

Enregistrer un commentaire