Profilage du code TypeScript : trouver les goulots d'étranglement en pratique
\nChaque développeur a déjà rencontré une situation où l'application commence à ralentir sans raison apparente. Dans le monde de TypeScript, où le code est transpilé en JavaScript, les goulots d'étranglement peuvent survenir aussi bien lors de l'exécution qu'en raison des particularités de la transpilation elle-même. Dans cet article, nous allons explorer des méthodes pratiques de profilage du code TypeScript qui vous aideront à identifier les zones problématiques et à améliorer les performances de votre application.
\n\nPourquoi le profilage TypeScript est-il important ?
\nTypeScript ajoute un typage statique, mais à l'exécution, c'est du JavaScript classique qui est exécuté. Cela signifie que les goulots d'étranglement peuvent être liés non seulement aux algorithmes, mais aussi à la manière dont TypeScript est compilé. Par exemple, une utilisation excessive de décorateurs ou de génériques complexes peut entraîner la génération de code non optimal. Le profilage aide à répondre aux questions suivantes :
\n- \n
- Quelles fonctions s'exécutent le plus longtemps ? \n
- Combien de mémoire l'application consomme-t-elle ? \n
- Où se produisent les ramassages de mémoire fréquents ? \n
- Quelles parties du code bloquent le thread principal ? \n
Sans profilage, vous risquez d'optimiser les mauvaises parties, en perdant du temps sur des micro-optimisations qui n'apporteront pas de gain significatif.
\n\nOutils pour le profilage TypeScript
\nPour le profilage du code TypeScript, nous utiliserons trois outils principaux qui couvrent la plupart des scénarios : des applications navigateur aux solutions serveur sur Node.js.
\n\n1. Chrome DevTools pour les applications navigateur
\nL'outil le plus accessible et le plus puissant. Ouvrez l'onglet Performance dans Chrome DevTools, lancez un enregistrement et effectuez l'action souhaitée dans l'application. Après l'arrêt, vous obtiendrez un flame chart — une représentation visuelle des appels de fonctions. Voici à quoi cela ressemble en pratique :
\n// Exemple de code lent en TypeScript\nfunction processData(data: number[]): number[] {\n const result: number[] = [];\n for (let i = 0; i < data.length; i++) {\n // Goulot d'étranglement potentiel : appels fréquents\n result.push(data[i] * Math.sqrt(data[i]));\n }\n return result;\n}\n\n// Utilisation\nconst largeArray = Array.from({ length: 100000 }, (_, i) => i);\nconsole.time('processData');\nprocessData(largeArray);\nconsole.timeEnd('processData'); // ~15ms\n\nDans Chrome DevTools, vous verrez que la fonction processData prend un temps significatif, et à l'intérieur, de nombreux appels à Math.sqrt. C'est une indication que vous pouvez remplacer l'itération par map ou vectoriser les calculs.
2. Node.js profiler pour les applications serveur
\nNode.js dispose d'un profileur intégré. Lancez l'application avec le flag --prof :
node --prof dist/app.js\n\nUne fois l'exécution terminée, un fichier isolate-*.log sera généré. Convertissez-le dans un format lisible :
node --prof-process isolate-*.log > processed.txt\n\nExemple d'un problème typique : des allocations fréquentes dans les boucles :
\n// Mauvais exemple : création d'objets dans une boucle\ninterface User {\n id: number;\n name: string;\n}\n\nfunction createUsers(ids: number[]): User[] {\n const users: User[] = [];\n for (const id of ids) {\n users.push({ id, name: `User_${id}` }); // Nouvelle allocation\n }\n return users;\n}\n\nLe profileur montrera que la fonction createUsers crée beaucoup d'objets temporaires, ce qui augmente la charge sur le ramasse-miettes. Optimisation : allouer la mémoire à l'avance ou utiliser un pool d'objets.
3. Benchmark.js pour les micro-benchmarks
\nLorsque vous devez comparer les performances de deux implémentations, utilisez la bibliothèque benchmark.js. Installez-la via npm :
npm install benchmark @types/benchmark\n\nExemple de comparaison de deux méthodes de filtrage d'un tableau :
\nimport Benc