效能評估工具 BenchmarkDotNet

「欸這功能效能很差,每次用都速度很慢。」
這是一個很常見的「感覺」形容。每個人對於速度的快慢定義不同,有些人覺得這速度很OK,而有些人覺得效能真的太差。

在工作上我們需要盡量避免這樣的形容詞,除了不夠精確之外,還需要花費更多、額外的時間來校正雙方的度量尺,這時候就需要一個公正、客觀的數據來協助探討是否這個功能真的很慢。

今天介紹的工具就是免費、開源的 BenchmarkDotNet
這個工具能夠協助你快速完成速度的測試,不需要再像以前一樣寫滿StopWatch、手動整理跑完的數據,最後再把計時用的程式碼還原。
透過這個工具,只需要短短幾個步驟就可以取得對照的結果。

首先需要下載、安裝這個套件。

1
Install-Package BenchmarkDotNet

在本文為了我們建立一個Console專案,然後將要進行的測試內容寫在另一個新開的類別裡面。最基本的使用方式就是將測試目標函式給予 [Benchmark]Attribute即可如下。

1
2
3
4
5
6
7
8
9
10
11
public class BenchmarkSampleTestClass
{
[Benchmark]
public void TestMethod1()
{
}
[Benchmark]
public void TestMethod2()
{
}
}

在 main 方法就可以直接呼叫該class進行benchmark評測。
務必在進行benchmark時使用 Release 建置。

1
2
3
4
5
6
7
8
class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<BenchmarkSampleTestClass>();
Console.ReadLine();
}
}

接著在terminal就可以看到花費時間的比較表。

實際範例

LINQ在開發上經常會使用到,跟任何工具一樣,使用不當的時候反而會降低其效果,這邊就選一個新手常犯的錯誤:關於 Select 與 Where 的使用時機。

在這個測試類別宣告的時候,建構子會建構一個list存放10000個User物件,我們寫出兩個method分別為SelectFirstMethod與WhereFirstMethod,顧名思義就是先使用select與先使用where的差別。
目的很簡單就是找到一個條件的所有物件的ID,轉成list回傳。

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
27
public class BenchmarkSampleTestClass
{
private readonly List<User> _data = new List<User>();
public BenchmarkSampleTestClass()
{
PrepareTestObjects();
}

[Benchmark]
public List<int> SelectFirstMethod()
{
return _data.Select(x => x.Id).Where(x => x > 90).ToList();
}
[Benchmark]
public List<int> WhereFirstMethod()
{
return _data.Where(y => y.Id > 90).Select(y => y.Id).ToList();
}

private void PrepareTestObjects()
{
for (var itr = 0 ; itr <10000 ; itr++)
{
_data.Add(new User(itr));
}
}
}

設置好Release build就可以直接執行,等待結果。

1
2
3
4
5
6
7
8
9
10
11
12
|            Method |      Mean |    Error |   StdDev |
|------------------ |----------:|---------:|---------:|
| SelectFirstMethod | 178.78 us | 3.494 us | 5.837 us |
| WhereFirstMethod | 66.22 us | 1.423 us | 2.416 us |


// ***** BenchmarkRunner: End *****
// ** Remained 0 benchmark(s) to run **
Run time: 00:01:01 (61.66 sec), executed benchmarks: 2

Global total time: 00:01:07 (67.01 sec), executed benchmarks: 2

從上面的表格可以得知平均時間的快慢; 當然這個套件還有提供更多功能與資訊,這些都可以參考官方網站的內容。
有了這個工具以後會節省更多時間去爭論誰快誰慢的問題,畢竟有數據佐證最直接、客觀公正。

這些結果除了在terminal可以看到外,在專案也會建立一個folder,以專案名稱加上 .Artifacts 命名,存放結果的 .log 檔以及預設提供的 csv, html 與 md 檔。

不同執行平台

這個工具可以幫你測試在不同runtime下(NET Framework, .NET Core, Mono and CoreRT.)的表現如何,只需要在class上加上這些attribute。
[ClrJob, MonoJob, CoreJob, CoreRtJob]

1
2
[ClrJob, MonoJob, CoreJob, CoreRtJob]
public class BenchmarkSampleTestClass

測試基準

很多效能調校都會拿原本的code來進行比較,這時候只需要在benchmark後面加上baseline設定即可。

1
2
3
4
5
6
[Benchmark(Baseline = true)]
public List<int> SelectFirstMethod()
{}
[Benchmark]
public List<int> WhereFirstMethod()
{}

本篇文章完整的範例專案在這邊可以下載到。

Reference

  • 作者: MingYi Chou
  • 版權聲明: 轉載不用問,但請註明出處!本網誌均採用 BY-NC-SA 許可協議。