Profilage du code TypeScript : trouver les goulots d'étranglement en pratique
Lorsque votre projet TypeScript commence à ralentir et que les utilisateurs se plaignent de latences, il est temps de passer au profilage. Le profilage n'est pas simplement "regarder ce qui est lent", mais une approche systématique pour identifier les goulots d'étranglement dans les performances du code. Dans cet article, nous allons explorer les méthodes pratiques de profilage du code TypeScript avec des exemples concrets et des outils.
Pourquoi profiler le code TypeScript ?
TypeScript est compilé en JavaScript, et tous les problèmes de performance inhérents à JS se retrouvent dans TS. Cependant, TypeScript ajoute ses propres nuances : la typification, les décorateurs, les génériques peuvent créer des surcoûts inattendus. Le profilage aide à répondre aux questions :
- Quelle fonction s'exécute le plus longtemps ?
- Où se produit une fuite de mémoire ?
- Pourquoi l'animation ralentit-elle à 60fps ?
- Quel algorithme nécessite une optimisation ?
Outils pour le profilage TypeScript
Chrome DevTools (pour le code navigateur)
L'outil le plus puissant pour le frontend. Ouvrez l'onglet Performance, cliquez sur Record, effectuez une action et arrêtez l'enregistrement. Vous verrez un flamegraph — une visualisation des appels de fonctions et de leur temps d'exécution.
// Exemple de code lent à profilerfunction heavyCalculation(iterations: number): number { let result = 0; for (let i = 0; i < iterations; i++) { result += Math.sqrt(i) * Math.sin(i); } return result;}
// Lancez dans la console du navigateur, puis profilezheavyCalculation(1000000);Node.js — profileur intégré
Pour le code serveur, utilisez le flag --inspect et Chrome DevTools, ou le --prof intégré pour générer des logs.
// server.tsimport express from 'express';
const app = express();
app.get('/heavy', (req, res) => { const data = processData(10000); res.json({ result: data });});
function processData(size: number): number[] { const arr: number[] = []; for (let i = 0; i < size; i++) { arr.push(i * Math.random()); } return arr.sort((a, b) => a - b);}
app.listen(3000);// Lancement : node --prof dist/server.jsBenchmark.js pour des mesures précises
Lorsque vous devez comparer deux implémentations, utilisez la bibliothèque benchmark.
import Benchmark from 'benchmark';
const suite = new Benchmark.Suite();
// Variante lentefunction slowSearch(arr: number[], target: number): boolean { for (let i = 0; i < arr.length; i++) { if (arr[i] === target) return true; } return false;}
// Variante rapide avec Setfunction fastSearch(arr: number[], target: number): boolean { const set = new Set(arr); return set.has(target);}
const testArray = Array.from({ length: 10000 }, (_, i) => i);
suite .add('slowSearch', () => slowSearch(testArray, 9999)) .add('fastSearch', () => fastSearch(testArray, 9999)) .on('cycle', (event: any) => console.log(String(event.target))) .on('complete', function(this: any) { console.log('Fastest is ' + this.filter('fastest').map('name')); }) .run({ async: true });Exemples pratiques de recherche de goulots d'étranglement
Exemple 1 : Fuite de mémoire via les fermetures
Un problème typique dans les applications React — les fermetures qui retiennent des références à de gros objets.
// Code problématiquefunction createHeavyComponent() { const hugeData = new Array(1000000).fill('data'); return { getData: () => hugeData, // la fermeture retient hugeData updateData: (newData: string) => { // mutation, mais la référence reste hugeData.push(newData); } };}
const component = createHeavyComponent();// hugeData ne sera pas récupéré par le GC tant que component existe<