還在徒手揮汗寫For測效能,閃開讓BenchmarkDotNet來

還在徒手揮汗寫For測效能,閃開讓BenchmarkDotNet來

上集使用反射執行方法的7種方式我們談到寫法不同,效果不同這件事,也說到,通常我們測試程式效能會很習慣的採用 Stopwatch 模式(Stopwatch Pattern)來進行程式執行效率的比較。

Stopwatch sw = new Stopwatch();
sw.Start();
// stuff
sw.Stop();

我們把前面的 7 Ways 程式碼用 for 跑 20000 次計算時間,可以得到以下結果:

Way1 Time: 40 ms
Way2 Time: 40 ms
Way3 Time: 76 ms
Way4 Time: 67 ms
Way5 Time: 148 ms
Way6 Time: 4233 ms
Way7 Time: 2054 ms
  

但這樣對嗎?在談效率時,其實有二種意義:時間空間(文言文:時間複雜度空間複雜度),我不是來教書的,雖然 Stopwatch 寫起來不複雜,但僅能看到時間單一面向,好像只要快就是好,怪怪的。

那有無更好的選擇?

BenchmarkDotNet

今天為大家介紹 BenchmarkDotNet,BenchmarkDotNet 是一個針對 .NET 做 Benchmark 的開源函式庫。

基本特色有:

  • 支援 runtime: Full .NET Framework, .NET Core (RTM), Mono
  • 支援語言:C#, F#, and Visual Basic
  • 支援平台:Windows, Linux, MacOS
  • 報表格式:markdown, csv, html, plain text, png plots

安裝與使用 BenchmarkDotNet

使用 NuGet 安裝:Install-Package BenchmarkDotNet

接下來我們製作一個專門進行 Benchmark 的類別:

public class MethodInvokeBenchmark
{
    private readonly object _myClass = new MyClass();
    private readonly MethodInvoke _mi = new MethodInvoke();
     
    [Benchmark]
    public void Way1() => _mi.Way1_DirectMethodCall(_myClass);
    [Benchmark]
    public void Way2() => _mi.Way2_CreateLambdaCall(_myClass);
    [Benchmark]
    public void Way3() => _mi.Way3_RelectionAPIMethodInvoke(_myClass);
    [Benchmark]
    public void Way4() => _mi.Way4_Using_dynamic_Keyword(_myClass);
    [Benchmark]
    public void Way5() => _mi.Way5_CreateDelegateCall(_myClass);
    [Benchmark]
    public void Way6() => _mi.Way6_CreateExpressionCall(_myClass);
    [Benchmark]
    public void Way7() => _mi.Way7_EmitAPIDynamicMethod(_myClass);
}

就是把主控台的 Main 程式碼換個地方,透過委派指定好對應的測試方法,最後測試方法上加上 [Benchmark] 屬性,這樣就完了。

注意,在進行 Benchmark 前請先切換至 Release 模式。

Main 進行 BenchmarkRunner 呼叫我們的測試類別:

var summary = BenchmarkRunner.Run<MethodInvokeBenchmark>();

執行應用程式,就會進行 Benchmark 測試,這是我用二台不同主機進行測試的結果。

BenchmarkDotNet Result1
BenchmarkDotNet Result2

指標說明:

  • Mean: Average time of all the meaningful measurements
  • StdDev: Standard deviation of these measurements
  • Gen 0: Amount of garbage collections in generation 0
  • Allocated: Allocated memory
  • us: Microseconds

在 bin 目錄下可以找到 BenchmarkDotNet.Artifacts,基本上可以找到最少 .csv / .html / .md 三種格式的報表資訊(內容等同圖片資訊)。從 BenchmarkDotNet 得到資訊來看,有些方法快,但需要用到較多的記憶體空間,有些方法較慢,可能僅用到極小(沒用到?)記憶體空間。

現在,我們同時擁有時間與空間資訊,這樣我們就能更好的做取捨與判斷。

GitHub 完整程式碼:ReflectionMethodInvokeWays

參考資料

官方文件:http://benchmarkdotnet.org/

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。