Votre application C# ralentit ? Il est temps de regarder sous le capot
Vous avez écrit un code C# propre et élégant, mais il fonctionne plus lentement que souhaité. Cela vous dit quelque chose ? Dans le développement moderne, la performance C# devient souvent un facteur décisif : les utilisateurs ne pardonnent pas les longs chargements, et les applications serveur doivent supporter des milliers de requêtes par seconde. Bonne nouvelle : .NET fournit des outils puissants pour le diagnostic .NET et le profilage C#. Mauvaise nouvelle : sans comprendre le processus, vous risquez d'optimiser ce qui ne doit pas l'être.
Dans cet article, nous allons voir comment profiler du code C# correctement, trouver les véritables goulots d'étranglement C# et appliquer des techniques éprouvées d'optimisation de code C#. Vous découvrirez pourquoi les intuitions sont souvent trompeuses et comment utiliser BenchmarkDotNet et le traçage ETW pour obtenir des données objectives. C'est parti !
Pourquoi « à l'œil » ne fonctionne pas : mythes sur l'optimisation
Avant de vous lancer dans l'optimisation de code C#, dissipons les idées reçues. De nombreux développeurs pensent savoir où leur programme ralentit. En pratique, le profilage C# montre que 90 % du temps est passé dans 10 % du code, et ces « points chauds » se trouvent souvent dans des endroits inattendus.
Erreurs typiques :
- Optimisation prématurée — vous passez des heures à accélérer des parties qui s'exécutent une fois par jour.
- Confiance en l'intuition — « Cette requête LINQ ralentit forcément » (alors qu'en réalité le problème vient de la sérialisation).
- Ignorer le GC — le ramasse-miettes peut « consommer » jusqu'à 30 % du temps processeur si vous gérez mal la mémoire.
Conclusion : la performance C# doit être mesurée, pas devinée. Pour cela, il existe des outils professionnels.
Outils pour le profilage C#
Dans l'écosystème .NET, il existe plusieurs voies pour le diagnostic .NET. Examinons les principales.
BenchmarkDotNet — votre meilleur ami
BenchmarkDotNet est la référence pour le micro-benchmarking. Il exécute automatiquement le code plusieurs fois, préchauffe le JIT, prend en compte le bruit et fournit des résultats statistiquement significatifs. Utilisez-le pour comparer deux implémentations d'une même méthode.
Exemple : comparons une boucle for classique et foreach pour un tableau :
using BenchmarkDotNet.Attributes;using BenchmarkDotNet.Running;
[MemoryDiagnoser] // mesure l'allocation mémoirepublic class LoopBenchmarks{ private int[] _data;
[GlobalSetup] public void Setup() { _data = Enumerable.Range(0, 1000).ToArray(); }
[Benchmark] public int ForLoop() { int sum = 0; for (int i = 0; i < _data.Length; i++) sum += _data[i]; return sum; }
[Benchmark] public int ForeachLoop() { int sum = 0; foreach (var item in _data) sum += item; return sum; }}
class Program{ static void Main() => BenchmarkRunner.Run<LoopBenchmarks>();}En exécutant ce code, vous verrez que la performance C# pour foreach sur les tableaux est identique à for — le JIT optimise les deux variantes de la même manière. Mais pour List<T>, foreach peut être plus lent à cause des vérifications de limites.
Profileurs Visual Studio et dotMemory
Pour un profilage C# complet au niveau de l'application, utilisez le profileur intégré de Visual Studio (disponible dans la version Enterprise) ou le dotMemory gratuit de JetBrains. Ils montreront :
- Quelles méthodes consomment le plus de CPU.
- Combien de mémoire est allouée et g