Chapitre 15.2 — Profiler & performance
Partie 15 : Expert — Chapitre 2/9 Version de référence : Symfony 7.4 / Blackfire.
⏱️ TL;DR
- Mesurez avant d’optimiser. L’optimisation prématurée fait perdre du temps sur des non-problèmes. On profile, on trouve le vrai goulot, on corrige, on re-mesure.
- En dev, le profiler montre tout : temps, requêtes SQL, mémoire, événements, cache. Le compteur de requêtes révèle les N+1 (Partie 6.6).
- En prod-like, Blackfire profile finement (graphe d’appels, comparaisons avant/après) sans polluer le code.
- Goulots classiques : N+1 (n°1), sérialisation lourde, trop de services instanciés, requêtes non indexées, absence de cache.
- Le composant Stopwatch mesure des sections précises ; l’OPcache preloading accélère le bootstrap en prod.
- Pour un dev Next.js : profiler ≈ les DevTools/React Profiler côté serveur ; Blackfire ≈ un profiler applicatif dédié (façon Datadog APM ciblé perf).
🎯 Objectifs
- Adopter la démarche « mesurer → corriger → re-mesurer ».
- Lire le profiler et repérer les goulots.
- Utiliser Blackfire et Stopwatch.
- Connaître les optimisations prod (OPcache preloading).
1. La règle d’or : mesurer d’abord
Le réflexe fatal est d’optimiser au jugé (« ça doit être lent ici »). Presque toujours, le vrai goulot est ailleurs. La démarche saine :
On corrige un goulot à la fois et on vérifie le gain. « Optimiser » sans mesure produit du code plus complexe pour un gain nul (voire négatif).
⚠️ Piège de l’optimisation prématurée : réécrire du code « pour la perf » avant d’avoir mesuré est une perte de temps et une source de bugs. 90 % du temps se passe souvent dans 10 % du code — trouvez ces 10 % par la mesure, pas à l’intuition. « Premature optimization is the root of all evil » (Knuth) reste vrai.
2. Le profiler (dev)
En dev, chaque page HTML affiche la barre de debug ; un clic ouvre le profiler (/_profiler). Il détaille, par requête :
- le temps total et par phase (événements du noyau, contrôleur) ;
- les requêtes SQL (nombre, durée, doublons → N+1) ;
- la mémoire consommée ;
- le cache (hits/miss), les événements, la sécurité, le Messenger.
C’est votre premier outil : avant toute optimisation, ouvrez le profiler sur la page lente et regardez où part le temps. Le plus souvent : trop de requêtes SQL (N+1) — qu’on corrige par fetch join (Partie 6.6).
3. Blackfire (profiling fin)
Le profiler dev est pratique mais impacte les mesures (il instrumente tout). Pour un profiling précis, y compris en prod-like, on utilise Blackfire : il produit un graphe d’appels (quelle fonction consomme quoi), permet de comparer deux profils (avant/après une optimisation), et de poser des assertions de perf (« cette page doit faire moins de X requêtes SQL ») — intégrables en CI.
Blackfire répond précisément à « où exactement part le temps/la mémoire ? » sur le vrai chemin d’exécution, sans deviner.
4. Les goulots classiques (et leurs corrections)
| Goulot | Symptôme (profiler) | Correction |
|---|---|---|
| N+1 | beaucoup de requêtes SQL identiques | fetch join / DTO (6.6) |
| Requête non indexée | 1 requête très lente | index (6.6) |
| Sérialisation lourde | temps dans le serializer | groupes minimaux, DTO (11.2) |
| Pas de cache | recalcul à chaque requête | composant Cache (15.1) |
| Trop de services | bootstrap lent | services lazy, revoir les dépendances |
| Rendu Twig lourd | temps dans les templates | cache de fragments, ESI |
Le N+1 reste, de loin, le problème n°1 — d’où l’insistance depuis la Partie 6.
5. Mesurer une section précise : Stopwatch
Pour mesurer un bout de code spécifique (pas toute la requête), le composant Stopwatch :
use Symfony\Component\Stopwatch\Stopwatch;
$stopwatch = new Stopwatch();
$stopwatch->start('import');
// ... code à mesurer
$event = $stopwatch->stop('import');
$this->logger->info('Import terminé', ['duration_ms' => $event->getDuration()]);Les sections mesurées apparaissent aussi dans le profiler (timeline), pour voir leur poids relatif.
6. Optimisations de production
En prod, plusieurs leviers hors code :
- OPcache : compile et cache le bytecode PHP (activé en prod). Preloading : précharge les classes du framework au démarrage du serveur → bootstrap plus rapide (
opcache.preload). - Composer :
--optimize-autoloader --classmap-authoritative(autoload figé, plus rapide). APP_ENV=prod+cache:clear/cache:warmup: config compilée, conteneur figé (Partie 5.4).- FrankenPHP worker mode (chapitre suivant) : bootstrap une fois → gain majeur.
💡 Pour un dev Next.js : la démarche « mesurer d’abord » est universelle (vous connaissez le React Profiler, Lighthouse). Blackfire ≈ un APM ciblé perf (Datadog/New Relic pour la partie profiling). Et — comme côté Next où vous surveillez le bundle et les re-renders — côté Symfony vous surveillez les requêtes SQL et le bootstrap. Le N+1 est l’équivalent backend du « re-render en boucle » : invisible en petit, dévastateur en grand.
✏️ Exercices
Exercice 1. Une page de liste est lente. Quelle est la première chose à regarder dans le profiler, et le goulot le plus probable ?
✅ Solution
Le nombre de requêtes SQL (onglet Doctrine du profiler). Le goulot le plus probable sur une liste est le N+1 : une requête pour la liste + une par ligne pour charger une relation (Partie 6.6). Correction : fetch join (->join(...)->addSelect(...)) ou projection DTO. On mesure avant/après pour confirmer le gain.
Exercice 2. Pourquoi « optimiser » sans mesurer est-il contre-productif ?
✅ Solution
Parce que le vrai goulot est presque toujours ailleurs que là où on l’imagine : on complexifie du code (source de bugs) pour un gain nul sur des non-problèmes, tout en laissant le vrai goulot en place. La perf se concentre souvent dans un petit % du code — qu’on trouve par la mesure (profiler/Blackfire), pas à l’intuition. On corrige un goulot, on re-mesure, on itère.
Exercice 3. Citez deux optimisations de production hors code.
✅ Solution
Au choix : OPcache (+ preloading des classes du framework) pour un bytecode compilé et un bootstrap rapide ; composer install --optimize-autoloader (autoload figé) ; APP_ENV=prod avec cache:warmup (config compilée) ; FrankenPHP worker mode (bootstrap unique). Ce sont des gains obtenus par la configuration/l’infra, sans toucher au code applicatif.
🧠 Quiz de révision
1. Quelle est la règle d’or de la performance ?
Mesurer avant d’optimiser : profiler → identifier le vrai goulot → corriger → re-mesurer. Éviter l’optimisation prématurée.
2. Que révèle le profiler sur une page lente ?
Le temps par phase, les requêtes SQL (dont les N+1), la mémoire, le cache, les événements — pour voir où part le temps.
3. Qu’apporte Blackfire par rapport au profiler dev ?
Un profiling fin (graphe d’appels), des comparaisons avant/après et des assertions de perf (CI), en prod-like, sans polluer le code.
4. Quel est le goulot backend n°1 ?
Le N+1 (requêtes en boucle sur une relation), corrigé par fetch join / DTO.
5. Que fait l’OPcache preloading ?
Il précharge les classes du framework au démarrage du serveur → bootstrap plus rapide en production.
Prochain chapitre : 15.3 — FrankenPHP worker mode — bootstrapper une fois, servir mille fois.