Accueil Nos publications Blog Code benchmark en C#

Code benchmark en C#

Code benchmark en C#

Sommaire

  1. Qu’est-ce qu’un benchmark ?
  2. Exemple de benchmark
  3. Création d’un benchmark
  4. Résultat du benchmark
  5. Conclusion

Introduction

Avez-vous déjà rencontré une situation où vous aviez une tâche à accomplir et deux implémentations différentes en tête et vous ne saviez pas laquelle serait la plus rapide ?

Avez-vous déjà hésité à mettre à jour l’algorithme d’un collègue et vous vous êtes posé la même question ?

Dans cet article, nous allons voir comment effectuer des benchmarks sur notre code et obtenir toutes ces mesures pour analyser et optimiser notre code.

Qu’est ce qu’un benchmark ?

Un benchmark est une analyse d’un programme présentant un ensemble de mesures traduisant le temps d’exécution et les ressources consommées par le code pendant l’exécution.

Dans cet article, nous couvrirons l’un des packages les plus utilisés dans cette catégorie : Benchmark.NET

Benchmark.NET

Benchmark.NET est une excellente bibliothèque .NET qui fournit un ensemble de métriques sur notre code, telles que l’utilisation de la mémoire, le temps d’exécution, l’utilisation du garbage collector Tout ce que vous avez à faire est d’installer le package NuGet et de créer votre classe de benchmark.

Déroulement d’un benchmark

Comme nous le savons tous, il y a toujours un coût de démarrage lorsqu’on lance une application C#.

Si nous exécutons notre benchmark au moment T0 de l’exécution, ce temps de démarrage peut affecter notre benchmark, et nous n’aurons pas de mesures précises. Pour éviter cela, Benchmark.NET a ajouté une étape supplémentaire de “warm up” pour pouvoir faire un benchmark précis et éviter les effets de bord.

Code benchmark en C#

Exemple de benchmark

Créer un nouveau projet

Créons une application console simple en .NET 7 pour tester notre code. Si vous n’avez pas .NET 7 installé sur votre machine, vous pouvez utiliser une autre version.

Rider est ici utilisé comme principal IDE. La même chose s’applique à Visual Studio.

  • Ouvrez Rider (ou Visual Studio) et cliquez sur “Créer un nouveau projet” ou “Nouveau projet”,
  • Sélectionnez “Application console .NET” parmi les modèles de projet disponibles,
  • Choisissez un nom et un emplacement pour votre projet, puis cliquez sur “Créer”.

Vous devriez maintenant avoir un nouveau projet console prêt à être utilisé.

Code benchmark en C#

Imaginons que nous construisons un compteur et que nous devons afficher une valeur de ce compteur dans la console. Notre résultat final sera :

Counter : 1
Counter : 2
Counter : 3
….
Counter : 10,000

Pour ce benchmark, nous allons essayer de comparer deux méthodes différentes de construction de chaîne de caractères : interpolation de chaîne (String interpolation) vs. StringBuilder.

Création d’un benchmark

Code benchmark en C#

Pour exécuter ce benchmark, vous devez appeler le BenchmarkRunner dans votre fichier Program.cs

Code benchmark en C#

Une dernière chose, la solution doit s’exécuter en mode release et sans débogage pour avoir des résultats précis. En mode débogage, l’IDE ajoute des metadatas sur notre code pour nous donner la possibilité de faire du pas à pas ou de mettre des points d’arrêt. Toutes ces informations peuvent fausser notre benchmark. Si vous essayez d’exécuter votre benchmark en mode débogage, vous aurez un message d’erreur.

Résultat du benchmark

Lorsque vous appuyez sur la flèche verte ou Ctrl + F5 pour exécuter votre application, vous aurez 3 étapes principales affichées dans votre console.

Warm-up :

Code benchmark en C#

Exécution :

Code benchmark en C#

Résumé :

Code benchmark en C#

Ce que nous obtenons de ce benchmark est le temps d’exécution de notre algorithme. Si nous essayons de lire ce tableau :

Le code d’interpolation de chaîne prend 316 455 microsecondes pour s’exécuter (colonne Mean). Par contre, le StringBuilder est beaucoup plus rapide avec 390 microsecondes.

Époustouflant, non ?

Dans cet exemple, nous parlons de temps d’exécution mesuré en microsecondes (0,000001 sec).

Cela peut sembler un détail pas important pour le moment, mais imaginez que vous ayez un énorme flux de données dans votre application qui analyse et crée des chaînes pour des écrans LED, vous aurez certainement des secondes au lieu de microsecondes.

Surtout si votre application est hébergée sur le cloud, temps + utilisation de la mémoire = argent.

Voyons comment nous pouvons obtenir plus d’informations sur l’utilisation de la mémoire.

Analyse utilisation mémoire

C’est assez simple, Benchmark Dotnet embarque plusieurs attributs que vous pouvez mettre dans votre classe ou dans vos méthodes pour façonner votre benchmark et avoir le résultat souhaité à la fin.

Ajouter l’attribut “MemoryDiagnoser”

Code benchmark en C#

Résultat

Code benchmark en C#

Nous avons maintenant 4 colonnes supplémentaires dans notre résultat :

Allocated:  mémoire allouée par opération unique

Gen0, Gen1, Gen2 : ces colonnes représentent le nombre de fois où le garbage collector a essayé de nettoyer les objets inutilisés dans la mémoire (appeler aussi collection generations). Pour avoir une vision claire de ce qui se passe ici, vous devez comprendre comment GC (garbage collector) fonctionne.

Gen0 est la première génération d’exécution du GC. Dans cette exécution, le GC essaiera de libérer toutes les allocations de mémoire inutilisées. S’il détecte que nous avons des objets qui ont en cours d’utilisation ou qu’ils peuvent potentiellement être désalloués ultérieurement, il les marquera comme Gen1. Ces objets seront désalloués dans la deuxième génération d’exécution.

Dans le tableau récapitulatif, nous avons combien de fois le GC s’est exécuté pour 1000 opérations.

Analyse des résultats

Passons en revue notre code et vérifions ce qu’il se passe. L’interpolation de chaîne prend plus de temps et consomme plus de mémoire en raison de l’immuabilité de la classe String.

Un objet immuable est un objet dont la valeur ne peut pas être modifiée après sa création, donc si nous voulons mettre à jour sa valeur, nous devons créer un nouvel objet avec la nouvelle valeur, et c’est ce que nous faisons dans notre code.

Chaque fois que nous mettons à jour notre objet résultat, nous créons un nouvel objet dans les coulisses et nous le stockons dans la mémoire.

Code benchmark en C#

Pour cette raison, il est recommandé d’utiliser le StringBuilder pour les applications qui gère un grand flux de strings.

Conclusion

En conclusion, le benchmarking est un outil indispensable pour tout développeur qui souhaite optimiser son code et améliorer ses performances. En utilisant un package tel que Benchmark.NET, les développeurs peuvent facilement mesurer le temps d’exécution de leur code, l’utilisation de la mémoire et d’autres mesures importantes.

Dans cet article, nous avons expliqué comment créer un benchmark à l’aide de Benchmark.NET, comment préparer l’application et comment analyser les résultats. Nous avons également examiné un exemple simple comparant les méthodes d’interpolation de chaîne et de générateur de chaîne, et nous avons conclu que le StringBuilder est une meilleure option pour gérer de nombreuses manipulations de chaîne. Grâce à l’analyse comparative, les développeurs peuvent prendre des décisions éclairées concernant leur code et créer des applications plus rapides et plus efficaces.

Vous souhaitez en savoir plus ? Contactez-nous !