Benchmark dotnet en toute simplicité

Le benchmark ou test de performance permet – en informatique – de comparer les performances de plusieurs solutions. La méthode la plus simple consiste à utiliser la méthode Stopwatch() mais elle se révèle rapidement limitée. Découvrons BenchmarkDotNet, un framework de benchmark open-source pour dotnet, que j’ai utilisé sur le projet LeetMe. “The quick brown fox jumps over the lazy dog” s’écrit en leet “_T#3 qu!(k 8r0w~ f0* jump5 0v3r 7#3 142y d09_“. Nous allons voir comment déterminer la méthode qui l’écrit le plus rapidement.

LeetMe remplace des caractères par d’autres en se basant sur un dictionnaire. L’aspect algorithmie (for ascendant, for descendant, foreach) ainsi que l’aspect technique (un tableau, un dictionnaire, une liste) sont amusants mais il faut savoir ce qui fonctionne le mieux. Pour le déterminer, j’ai implémenté chaque possibilité dans une classe distincte.

ArrayInline : tableaux déclarés à la volée dans un tableau

ArrayDefined : tableaux instanciés puis ajoutés dans un tableau

DicoCharArray : tableaux instanciés puis ajoutés dans un dictionnaire

DicoCharList : listes instanciés puis ajoutées dans un dictionnaire

L’objectif est de comparer la rapidité d’exécution et la consommation mémoire de plusieurs méthodes qui réalisent la même tache.

Le code suivant permet de tester et comparer les 4 méthodes décorée par [Benchmark] avec Benchmarkdotnet. Chaque méthode est appelée plusieurs fois afin de pouvoir construire des statistiques correctes sur la moyenne et l’écart de temps d’exécution et sur la consommation de mémoire [MemoryDiagnoser].

 

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;

    [CoreJob]
    [RPlotExporter, RankColumn]
    [MemoryDiagnoser]
    public class BenchLeet
    {
        private readonly string sentence;

        private readonly LeetMe.LeetMeArrayInline LeetMeInstance1 = new LeetMe.LeetMeArrayInline();
        private readonly LeetMe.LeetMeArrayDefined LeetMeForInstance2 = new LeetMe.LeetMeArrayDefined();
        private readonly LeetMe.LeetMeDicoCharArray LeetMeInstance3 = new LeetMe.LeetMeDicoCharArray();
        private readonly LeetMe.LeetMeDicoCharList LeetMeForInstance4 = new LeetMe.LeetMeDicoCharList();

        public BenchLeet()
        {
            // a random text
            sentence = LoremNET.Lorem.Sentence(5, 10);
        }

        [Benchmark]
        public string NoobArrayInline()
        {
            return LeetMeUpInstance1.Translate(sentence, LeetMe.LeetLevel.Noob);
        }

        [Benchmark]
        public string NoobArrayDefined()
        {
            return LeetMeUpInstance2.Translate(sentence, LeetMe.LeetLevel.Noob);
        }

        [Benchmark]
        public string NoobDicoCharArray()
        {
            return LeetMeUpForDescDeInstance3.Translate(sentence, LeetMe.LeetLevel.Noob);
        }

        [Benchmark]
        public string NoobDicoCharList()
        {
            return LeetMeUpForDescInstance4.Translate(sentence, LeetMe.LeetLevel.Noob);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run();
            Console.ReadLine();
        }
    }

Lancer en mode Release sinon vous aurez un message d’erreur.

Après un certain temps et beaucoup de ligne dans la fenêtre d’exécution

// * Summary *

BenchmarkDotNet=v0.11.1, OS=Windows 10.0.14393.2363 (1607/AnniversaryUpdate/Redstone1), VM=VMware
Intel Xeon CPU E5-2650 v4 2.20GHz, 1 CPU, 2 logical and 2 physical cores
Frequency=2148435 Hz, Resolution=465.4551 ns, Timer=TSC
.NET Core SDK=2.1.400
  [Host] : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT  [AttachedDebugger]
  Core   : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT

Job=Core  Runtime=Core

            Method |     Mean |     Error |    StdDev | Rank |  Gen 0 | Allocated |
------------------ |---------:|----------:|----------:|-----:|-------:|----------:|
   NoobArrayInline | 5.093 us | 0.1071 us | 0.1603 us |    2 | 1.0834 |   6.77 KB |
  NoobArrayDefined | 3.982 us | 0.0631 us | 0.0527 us |    1 | 0.5493 |   3.45 KB |
 NoobDicoCharArray | 5.048 us | 0.0996 us | 0.2329 us |    2 | 0.4807 |   3.05 KB |
  NoobDicoCharList | 6.497 us | 0.1276 us | 0.1614 us |    3 | 0.9613 |   6.01 KB |

La présentation de BenchmarkDotNet est maintenant finie. J’espère vous avoir présenter un outil intéressant à l’aide d’un exemple ludique.

Références :

BenchmarkDotNet

LeetMe