Améliorer son code PHP Jeu de données d'exemple

Une mini-base de données est nécessaire avec une table et quelques champs. Ce script SQL devrait convenir.


 CREATE TABLE `membres` (   `num` smallint(5) NOT NULL AUTO_INCREMENT,
`nom` varchar(50) NOT NULL,
`password` varchar(32) NOT NULL,
`date_naissance` date DEFAULT NULL,
PRIMARY KEY (`num`),
KEY `ville` (`ville`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `membres` (`num`, `nom`, `password`, `date_naissance`) VALUES
(1, 'Cyril', 'AgoraticSecret', NULL),
(2, 'Guillaume', 'AgoraPHPSecret', '1984-02-08', 'Marseille'),
(3, 'Julien', 'ZFPowerS', '1977-08-07', 'paris');


Pour le profileur, interrogez sa page via wget ou Curl en envoyant un paramètre GET appelé XDEBUG_PROFILE.


Après exécution de la page testée, un fichier devrait être présent dans le dossier /tmp. Par défaut, son nom dépendra du PID exécuté (ceci se personnalise), par exemple : cachegrind.out.7316. Ouvrez-le avec KcacheGrind (figure 8.1).


visualisation dans kcachegrind
Visualisation dans KCacheGrind © Eyrolles


La partie de gauche représente les appels de fonctions. Vous pouvez les exprimer en microsecondes, ou en pourcentages. Les temps indiqués ne sont pas ceux que vous obtiendrez dans la réalité, ne serait-ce qu'à cause de l'impact de Xdebug.


La valeur Incl est le temps qu'a mis la fonction à s'exécuter (tous appels confondus), ainsi que tous les éventuels enfants qu'elle a appelés. Self, pour sa part, donne le temps des instructions de la fonction, sans compter le temps passé à exécuter ses éventuels enfants. Enfin, Called indique combien de fois elle a été appelée.

Sur la droite, dans Call graph, vous pouvez apercevoir les enchaînements de fonctions et les temps d'exécution associés. Ici, vous pouvez cliquer-droit pour régler la profondeur des nœuds : Min Node Cost, Min Call Cost, Caller Depth et Callee Depth.

Un seul coup d'œil permet de se rendre compte que la connexion à la base de données (la création de l'objet PDO) a pris 41% du temps total. C'est donc sur ce point qu'il faut agir si l'on veut optimiser le temps de réponse de ce script.

Dans le cas où notre serveur de base de données est sur la même machine que PHP, une amélioration possible serait de s'y connecter en utilisant des sockets Unix (moins gourmandes que les sockets TCP, car la pile TCP/IP du noyau n'est pas utilisée). Le changement est minime et ne concerne que le constructeur de PDO.


 Utilisation de connexions avec socket Unix


$p = new PDO('mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=profiling',
X 'julien', 'password');



Relançons une action de profiling après ce changement : la fonction PDO::__construct() est passée de 746 à 590 ms. Au total, nous avons gagné 14% de temps de traitement : le profiling permet d'agir sur les goulots d'étranglement.

Une prochaine étape d'optimisation pourrait être de totalement court-circuiter la base de données en récupérant les informations une première fois, puis en les stockant en cache (mémoire, fichier...) pour les resservir par la suite. La page serait alors quasiment statique et l'amélioration des performances optimale.