Profilage du code JavaScript : trouver les goulots d'étranglement
\n\nLorsque votre projet Node.js commence à ralentir, la première pensée est « il faut tout réécrire en Rust ». Mais ne vous précipitez pas ! Le plus souvent, le problème se cache dans quelques lignes de code qui fonctionnent de manière inefficace. Dans cet article, nous allons voir comment trouver ces « goulots d'étranglement » à l'aide d'outils de profilage simples. Vous apprendrez à mesurer les performances, à construire un flamegraph et à comprendre où va le CPU.
\n\nPourquoi profiler le code ?
\n\nImaginez que votre serveur traite 100 requêtes par seconde, alors qu'il en faut 500. Vous pouvez optimiser tout et n'importe quoi, mais cela prendra des semaines. Ou vous pouvez trouver une seule fonction qui s'exécute 80 % du temps et la corriger en une heure. Le profilage est exactement ce qui montre où le temps est dépensé.
\n\nExaminons les principaux scénarios où le profilage est nécessaire :
\n- \n
- Latence élevée des réponses API \n
- Fuites mémoire (memory leaks) \n
- Charge CPU élevée sans raison apparente \n
- Traitement lent de grands tableaux de données \n
Outil 1 : Profileur intégré Node.js (--prof)
\n\nLa méthode la plus simple est de lancer Node.js avec le drapeau --prof. Il collecte des statistiques d'exécution et les enregistre dans un fichier isolate-*.log.
Créons un script de test qui contient un goulot d'étranglement :
\n\n// slow.js\nfunction heavyComputation() {\n let sum = 0;\n for (let i = 0; i < 1e7; i++) {\n sum += Math.sqrt(i) * Math.sin(i);\n }\n return sum;\n}\n\nfunction fastFunction() {\n return 42;\n}\n\nconsole.time('total');\nheavyComputation();\nfastFunction();\nconsole.timeEnd('total');\n\n\nLançons le profilage :
\nnode --prof slow.js\n\nAprès l'exécution, un fichier isolate-0x...-v8.log apparaît. Pour le lire, utilisons l'utilitaire node --prof-process :
node --prof-process isolate-0x...-v8.log > profile.txt\n\nOuvrons profile.txt et nous verrons un tableau avec des pourcentages de temps :
[Summary]:\n ticks total nonlib name\n 785 78.5% 85.3% JavaScript\n 215 21.5% 14.7% C++\n ...\n[Bottom up (heavy) profile]:\n ticks parent name\n 500 50.0% LazyCompile: *heavyComputation slow.js:2:17\n 100 10.0% Builtin: MathSqrt\n 50 5.0% Builtin: MathSin\n\n\nNous voyons que 50 % du temps est consacré à la fonction heavyComputation. Maintenant, nous savons quoi optimiser en premier.
Outil 2 : Chrome DevTools (inspecteur)
\n\nPour une analyse plus visuelle, connectons Chrome DevTools. Lançons Node.js avec le drapeau --inspect :
node --inspect slow.js\n\nOuvrons Chrome et allons à l'adresse chrome://inspect. Cliquons sur « Open dedicated DevTools for Node ». Onglet « Profiler » → « Start » → lancez le script → « Stop ».
Vous verrez un flamegraph — un graphique où la largeur du bloc montre le temps d'exécution. Plus le bloc est large, plus la fonction est « chaude ». Notre heavyComputation sera un énorme rectangle.
Conseil : Utilisez le filtre « Focus selected » sur une fonction pour voir ses appels internes. Par exemple, vous remarquerez que Math.sqrt est appelé 10 millions de fois — il serait peut-être judicieux de calculer les racines à l'avance.
Outil 3 : perf_hooks — mesures précises dans le code
\n\nParfois, il est nécessaire de mesurer une partie spécifique du code sans lancer d'outils externes. Pour cela, utilisons le module intégré perf_hooks.
Exemple de mesure du temps d'exécution d'une fonction :
\n\nconst { performance } = require('perf_hooks');\n\nfunction processData(items) {\n let result = [];\n for (let item of items) {\n // Simulation d'un traitement complexe\n for (let j = 0; j < 1000; j++) {\n result.push(item * j);\n }\n