Accueil > Développement > Profiler PHP

Profiler PHP

jauge-thumb4561403Le but de ce billet est simplement de montrer qu’il est facile de mettre en place un profiler et que l’intérêt peut être énorme.

Pour rappel, un profiler est un outil dont le but est d’optimiser une application. Que ce soit pour la faire exécuter plus rapidement ou pour diminuer la mémoire consommée. Habitué au profiling java (via OptimizeIt, JProbe, JProfiler …), ce n’est que récemment que je me suis intéressé au profiling PHP (j’entends déjà les mauvaises langues siffler « ben oui, php, c’est rapide et léger, pas besoin de profiler ! »).

Profiling CPU

La rapidité est un facteur intéressant aussi bien pour un script exécuté par Apache que pour un script exécuté en ligne de commande.

Avant d’expliquer comment installer le profiler, d’abord un simple exemple d’utilisation. Sur un CMS (Drupal), nous avons remarqué que de simples requêtes HTTP faisait grimper le CPU d’Apache à 100% pendant quelques secondes. Une simple coup de profiling permet de trouver très rapidement le coupable:

profiler-speed

L’outil ci-dessus montre que la requête HTTP (index.php) a duré 2.8 secondes, et sur ces 2.8 secondes, la fonction ‘theme’ a pris 2.4 secondes. Après, il suffit de suivre le fil en double-cliquant sur les lignes jusqu’à trouver une fonction suspecte:

prof-speed-b

Et là on trouve le coupable: la fonction JSMin:minify, qui a elle seule, prend 2.2 secondes. Cette fonction sert à minifier à la volée les scripts JS que nous avons mis en place dans Drupal. Dans ce cas concret, l’optimisation est simple: nous minifions nous-meme les scripts, et la requête HTTP passe de 2.8 secondes à 0.6 seconde !

Remarque: en mesurant le temps d’exécution de la fonction, on mesure également le temps d’exécution des requêtes SQL, ce qui permet éventuellement de trouver des requêtes à optimiser.

Cet exemple permet de montrer qu’il est très facile de trouver le problème. Après, ce n’est pas pour autant que l’on trouve la solution, mais c’est déjà très intéressant dans bon nombre de cas.

Installation de l’outil:

L’extension php xdebug permet de tracer les performances de chaque instruction exécutée. Pour l’installer, c’est très simple:

Ensuite, il faut configurer XDebug. Cela se fait simplement en éditant le fichier php.ini:

zend_extension=/path/to/xdebug.so_or_dll
xdebug.profiler_enable = On
xdebug.profiler_output_dir = /whatever/folder
xdebug.profiler_output_name = "cachegrind.out.%u"

Maintenant, tout appel à php engendrera des fichiers de logs dans le répertoire défini ci-dessus. Attention, ces fichiers peuvent être très volumineux !

Pour les dépouiller, il faut utiliser KCachegrind (KDE) ou WinCacheGrind (Windows). Ce dernier est basique mais fournit l’essentiel (les snapshots ci-dessus ont été pris avec WinCacheGrind). KCacheGrind est plus complet, le site de XDebug explique les bases de son utilisation. Il est intéressant de remarquer que l’on peut très bien avoir des logs créés par un serveur Debian et ensuite analysés sur un poste Windows.

Profiling mémoire

On a rarement recours au profiling mémoire pour des scripts exécutés sur un serveur HTTP. La durée de la requête étant en général au plus de quelques secondes, la mémoire n’a pas le temps d’atteindre des sommets, et si c’est le cas, on a vite tendance à augmenter le paramètre memory_limit dans le php.ini sans trop se poser de questions !!!

Par contre, pour des scripts exécutés en ligne de commande, qui peuvent durer plusieurs minutes, cela peut devenir problématique. Notre besoin était d’importer des contenus externes (dans Drupal). Notre script bouclait donc sur les 4.000 contenus à importer. Or, à chaque nouvelle itération, la mémoire augmentait de 100ko. Il y avait donc une fuite mémoire qu’il était nécessaire de colmater.

Malheureusement, je n’ai pas trouvé d’outil adéquat pour du profiling mémoire. Les outils java cités plus haut permettaient de manière très efficace de repérer quels étaient les données qui étaient encore en mémoire à tort, et par qui elles avaient été allouées. Mais je n’ai pas trouvé l’équivalent en php.

XDebug fournit des informations mémoires en le configurant ainsi:

xdebug.show_mem_delta = On
xdebug.trace_output_dir = /whatever/folder
xdebug.trace_output_name="trace.%u"

Mais je n’ai pas trouvé d’outil capable de dépouiller les fichiers de log (les analyser manuellement n’est vraiment pas productif), et j’ai l’impression que ces fichiers ne contiennent pas toutes les informations utiles (notamment la liste des objets encore alloués, par qui).

Donc sur ce coup là, je m’en suis tiré avec du profiling cpu, en prenant pour hypothèse que la fonction qui prend le plus de CPU serait la fonction qui prend le plus de mémoire ! Et par une immense chance, cela a fonctionné (le vilain petit canard était le module pathauto de Drupal) !

Mais si vous connaissez des outils, je suis preneur !

Tristan Développement , , , ,

  1. EricS
    17/03/2009 à 21:26 | #1

    Je préférais les recettes de cuisine ou disons je comprenais un peu plus.

  1. Pas encore de trackbacks