Désarticulons Wordpress 2.8

Attention, cet article a été posté en 2009. Il est possible que les informations mentionnées ne soient plus d'actualité, ou que mon opinion ait évolué. Merci d'en tenir compte lors de votre lecture.

Petit, je souhaitais devenir chirurgien. Ce rêve m’a laissé quelques séquelles, j’adore opérer à cœur ouvert : prendre un programme, le découper en morceau, et comprendre ses forces et ses faiblesses en partant de l’intérieur. Aujourd’hui, j’ai décidé de m’attaquer à la nouvelle monture de Wordpress, dans le but d’optimiser le machin.

Première étape, l’installation. Je ne détaille pas le processus, vous le connaissez probablement sur le bout des doigts. Dix minutes top chrono, le temps de dézipper la bête, le tout est installé. Le scalpel à droite, la morphine à gauche, l’opération peut commencer.

Après un premier état des lieux rapide, je me dirige directement vers le centre du cerveau : les requêtes SQL. Une ligne de code ajoutée par-ci, une autre par là, elles défilent sur mon écran au fil de ma navigation. Pour information, j’ai juste trifouillé la méthode query() du fichier /wp-includes/wp-db.php pour ajouter un simple :

echo $query;

Première constatation, ces requêtes sont nombreuses. Au moins une dizaine par page, en moyenne. Et encore, je n’ose pas vous parler de l’interface d’administration. De plus, nombre d’entre elles sont redondantes, plusieurs pourraient être fusionnées, d’autres supprimées. Je pense notamment à celles permettant de charger les options de configuration de votre blog : ces options changent tous les 36 du mois, les mettre en cache me semble inévitable, pourquoi ne le font-ils pas ? Idem pour les liens ou les catégories, placés dans la sidebar.

Pour m’aider dans l’analyse de ces requêtes, j’approfondis le bout de code donné plus haut (ne fuyez pas, je vous l’explique juste après, en bon français) :

echo $query;
if ( substr($query, 0, 7) == 'SELECT ' )
{
$r = mysql_query('EXPLAIN '.$query);
echo '<pre>';
while ( $d = mysql_fetch_assoc($r) )
{
var_dump($d);
}
echo '</pre>';
}

Ce bout de code permet d’exploiter la puissance du gestionnaire de base de données : pour chaque requête, je lui demande de m’expliquer comment il va la traiter. Cela permet de voir en un coup d’œil si la requête est optimisée, et si des améliorations peuvent être apportées à ce niveau.

Les connaisseurs noteront que je n’utilise pas l’objet MySqL proposé par Wordpress : je vais au plus simple, pour limiter au maximum les risques d’erreur, qui pourraient provoquer des jugements trop hâtifs.

Je suis plutôt content à ce niveau, toutes les requêtes sont relativement optimisées. Revers de la médaille, cela veut également dire que j’allègerai relativement peu la bête en cherchant de ce côté. Flûte.

Prochaine opération, les timers. Modifier le code en plaçant des témoins à différents endroits stratégiques permet de repérer facilement les goulots d’engorgement, pour cibler les zones à optimiser en priorité. Pour cela, on démarre le chrono au tout début du fichier :

define("TIMER_START", microtime(true));

puis on affiche le temps écoulés depuis le début à plusieurs reprises :

echo microtime(true) – TIMER_START;

Bingo, deux goulots repérés. Sur un total de 430 millisecondes en moyenne pour charger la page (c’est énorme : à titre de comparaison, Blogonet affiche la Home en moins de 30 millisecondes, sur la même bécane) :

  • 100 ms sont utilisées par le chargement d’une 15ène de fichiers, définissant des centaines de fonctions, sur plusieurs dizaines de milliers de lignes. Peu de solutions à proposer à ce niveau (ou du moins, pas au niveau du code en lui-même, il faudra plutôt toucher à configuration du serveur, en mettant en place un cache OP-Code).
  • 200 ms environ sont utilisées par le moteur de langage : après avoir analysé la chose, la solution est très puissante, mais également très très lourde. Pour corriger le tir, un fichier de cache peut être mis en place sur l’objet ainsi généré.

Résultat, en appliquant les quelques conseils de l’article, j’arrive à franchir la barre des 180 ms, en moyenne. Le tout avec moitié-moins de requêtes. C’est encore beaucoup, j’en suis conscient, mais ça permet déjà de diviser les ressources consommées par deux (et donc de prendre un serveur deux fois moins cher ?).

Bien entendu, ça ne remplacera jamais un système de cache évolué. D’ailleurs, un jour, je regarderai ce qu’ils ont dans le ventre, eux aussi. Il y a peut être moyen d’optimiser encore.

crédit image

Vos réflexions

Pas mal les optimisations. Effectivement un moteur de caching de op-code est LA solution à mettre ne place en premier lieu de mon point de vue. En utilises-tu un en particulier ?

Sinon pour carrément soulager le serveur le bon vieux WP-SuperCache, mais je n'aime pas trop car des fois on crée des décalages avec les commentaires...

A bientôt
L'optimisation est impressionnante. Surtout que tu as juste modifié quelques lignes de code ! (bon après, bien que ça soit juste quelques corrections mineurs au final, j'imagine que l'idenfication des problèmes et la recherche de solution a dû te prendre de nombreuses heures...).

Cela dit, couplé avec un bon cache, ça peut vraiment être performant. Car oui, il ne faut pas se leurer, à chaque modification (ajout de commentaire, correction mineur...), l'ensemble du cache doit être mis à jour... ce qui entraine forcément de lourds calculs ! Et si tous ces calculs sont nettement allégés, c'est que du bonheur.

Merci beaucoup pour cette expertise (gratuite, qui plus est !) en tout cas !
@Lionel : Niveau cache-OPCode, j'utilise APC. C'est assez fiable, assez fin, assez stable en général. Même si je pense que certaines bidouilles pourraient le rendre encore plus performant (peut être qu'un jour, je mettrais la main dans le cambouis, mais je n'en ai pas le temps actuellement).

Concernant WP-Super-Cache, il doit avoir moyen de corriger ces problèmes avec les commentaires. Les seuls problèmes que je vois actuellement sont les différences qu'il peut y avoir entre deux personnes (par exemple, les formulaires pré-remplis, ou les même les commentaires en attente de validation visibles par leur auteur uniquement, etc...). Bref, c'est loin d'être évident.
@0x90 : Oui, je ne cache pas que la recherche a pris un peu de temps. Et encore, le code de wordpress est relativement bien structuré, il est assez simple à avaler malgré le nombre de fichiers. Cependant, je pense que des efforts peuvent encore être fait, pour encore diviser le temps par deux. Mais ça demandera plus de temps.

C'est pour ça que j'aurais aimé avoir l'avis de d'autres connaisseurs, avoir d'autres expertises, pour optimiser la chose au mieux.
@Jukien: penses-tu un jour faire un article pour expliquer en détail ce que tu as changé ?
comme @Jukien ou lionel je serais assez content de voir les détails de ce que tu as pu optimisé.
@Lionel : Oui, s'il y a beaucoup de demandes, je prendrai le temps de mettre tous les changements au propre. Actuellement, c'est beaucoup de bidouilles, beaucoup de retouches, et les seules notes que j'ai prises sont consignées en vrac sur un calepin.

Cependant, j'aurais aimé avoir d'autres avis, d'autres expertises, de personnes ayant plongé dans le code, ayant cherché à optimiser la bête. Cela permettrait sûrement d'être encore plus efficace, ou de voir les problèmes (ou cas limites) que peuvent entrainer certaines solutions. Donc si vous avez plongé dans la marmite, ou si vous connaissez des personnes qui l'ont fait, n'hésitez pas à relayer :)
Article super intéressant, qui ouvre de nombreuses portes. D'ailleurs, tu devrais peut être soumettre tes découvertes à la team de développement, cela pourrait servir.
Article intéressant mais je reste sur ma faim et regrette de ne pas avoir d'infos plus concrètes sur le détail des modifications que tu as effectué...
@Antoine : Oui, mais comme dit précédemment, cet article s'adresse essentiellement à des connaisseurs, des personnes qui s'intéressent au coeur de Wordpress et qui ont pris le temps d'étudier son code. Le but était avant tout d'échanger dans ce sens, pour trouver les meilleures solutions, afin de proposer un tutorial clair et concis par la suite.

J'ai eu une demi-douzaine de contacts par email, mais j'aurais autant aimé que ces échanges se fassent ici-même, à travers les commentaires, afin qu'un maximum de personnes puissent s'y impliquer. Non ?

Alors, quelles sont vos méthodes pour optimiser la bête ?