還在徒手揮汗寫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 測試,這是我用二台不同主機進行測試的結果。
指標說明:
- 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
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。