Wir alle bemühen uns performanten und möglichst effizienten Code zu schreiben und auch gegebenenfalls vorhandenen Legacy Code zu verbessern. Um seinen Code zu optimieren gilt die gute alte Regel – messen, messen und nochmal messen, denn nur so kann man die Engpässe vernünftig identifizieren, um danach gezielt eine Lösung zu suchen. Ich arbeite im Normalfall an dieser Stelle mit der guten alten Stopwatch, die man unter System.Diagnostics im .NET Framework findet.
Scott Hanselman hat in seinem Blog aber eine weitere, sehr elegante Möglichkeit vorgestellt, um Methodenaufrufe und Programmteile zu bewerten. Er verwendet dazu die Open Source Bibliothek BenchmarkDotNet, die man natürlich auch als NuGet Package bekommen kann. Fügt man sie einem Projekt hinzu, so kann man mittels dem Attribut Benchmark Methoden markieren, die man genauer betrachten möchte. Über den BenchmarkRunner kann man dann die Klasse mit den Attributen aufrufen.
Am besten sieht man das an einem kleinen Beispiel, das man so auch auf der GitHub Seite von BenchmarkDotNet findet. Die Idee hinter den Beispiel ist es, die Hashverfahren MD5 und SHA256 miteinander zu vergleichen. Wir erstellen also eine einfache Klasse, die ein Array mit Zufallswerten erzeugt und darauf mit dem Aufruf von zwei Methoden, die mit dem Benchmark Attribut markiert werden, sowohl den MD5 als auch den SHA256 laufen lassen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class Md5VsSha256 { private const int N = 10000; private readonly byte[] data; private readonly SHA256 sha256 = SHA256.Create(); private readonly MD5 md5 = MD5.Create(); public Md5VsSha256() { this.data = new byte[N]; new Random(42).NextBytes(this.data); } [Benchmark] public byte[] Sha256() { return this.sha256.ComputeHash(this.data); } [Benchmark] public byte[] Md5() { return this.md5.ComputeHash(this.data); } } |
In der Main Methode unserer Anwendung (oder in einem anderen Teil der Anwendung) starten wir dann mit
1 |
var summary = BenchmarkRunner.Run<Md5VsSha256>(); |
den Benchmark. Die Bibliothek führt jetzt den Benchmark auf den Methoden durch und das kann durchaus etwas dauern. Am Ende erhaltet ihr dann eine Zusammenfassung des Benchmarks, die bspw. so aussehen kann:
1 2 3 4 5 6 7 8 9 10 11 12 |
BenchmarkDotNet=v0.9.1.0 OS=Microsoft Windows NT 6.2.9200.0 Processor=Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz, ProcessorCount=8 Frequency=2241008 ticks, Resolution=446.2278 ns HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE Type=Md5VsSha256 Mode=Throughput Method | Median | StdDev | ------- |------------ |---------- | Md5 | 25.2095 us | 0.2495 us | Sha256 | 128.6298 us | 0.6977 us | |
Im bin-Verzeichnis findet ihr außerdem noch wesentlich ausführlichere Ergebnisse des Benchmarks, so unter anderem auch Readme Dateien für die gängigsten Webdienste (bspw. GitHub und StackOverflow), aber auch CSV Dateien, die ihr weiter analysieren könnt. Natürlich kann man noch wesentlich mehr mit der Bibliothek testen (bspw. die unterschiedlichen JIT Varianten, Anzahl der Prozessoren, die .NET Runtime, etc.) oder auch die Ergebnisausgabe an eigene Vorgaben anpassen.
Was verwendet ihr denn so für das Benchmarking von Methoden und Programmteilen? Auch Stopwatch? Die BenchmarkDotNet Bibliothek? Etwas anderes? Würde mich über einen Kommentar von euch freuen :-)!