.NET Core 效能分析: xUnit.Performance 簡介

solenovex發表於2018-09-08

 xunit-performance 是xUnit的一個擴充套件, 使用它可以對.NET Core專案進行效能測試。

官網:https://github.com/Microsoft/xunit-performance

 

xUnit大家可能都用過,它是用來做單元測試的,它可以很快給開發人員功能是否OK的反饋。

和xUnit一樣,xUnit-Performance可以很快給出效能上的反饋。

 

準備和安裝xUnit-Performance

為了講解,我們需要準備一個需要被測試的專案和一個測試專案。

我使用Visual Studio 2017建立專案之後總有一些問題,不過後來我是用dotnet cli和VSCode就沒有什麼問題了。

 

建立專案的順序如下:

1. 首先使用dotnet cli建立一個classlib型別的被測試專案,它的目標框架是.NET Standard 2.0:

 

這個專案裡只有一個類,也就是要被測試的類:

這個類有三個方法,分別是使用foreach,for和Linq擴充套件方法的Sum對集合迴圈並求和。

 

2. 使用dotnet cli建立一個console專案(如果使用VS2017的話直接建類庫就可以,因為VS2017內建Test Runner),這個是測試專案,它的版本只能是2.0(可能是因為我電腦sdk的版本較老):

另外還需要引用被測試專案。

 

3.然後,按照官方文件安裝兩個庫。 

xUnit-Performance目前還處於Beta階段,這兩個庫需要按照官網的指示進行安裝:

最新版的xunit.performance.api.dll, 這裡用到的是MyGet:  https://dotnet.myget.org/feed/dotnet-core/package/nuget/xunit.performance.api#.

 

然後是最新版的 Microsoft.Diagnostics.Tracing.TraceEvent, 這個使用Nuge: https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.TraceEvent

 

OK,現在依賴庫都裝好了。

 

編寫效能測試

效能測試和單元測試略有不同, 效能測試是跑很多次, 然後取平均值. 同時也要考慮到記憶體等其它因素的影響.

在效能測試裡就不需要測試功能的正確性了, 但是程式在壓力下可能會產生不同的結果, 尤其是多執行緒的情況. 這時你就需要寫壓力測試了.

而對於效能測試, 我們只考慮速度.

 

由於我是用的是dotnet cli和VSCode,所以測試專案我選用的是控制檯專案,它的Main方法需要這樣寫:

如果您能成功的使用VS2017建立測試專案,那麼就不需要Main方法了,建立一個類庫專案即可,直接使用VS2017的Test Runner即可。

 

效能測試程式碼

下面我們編寫效能測試方法。

首先在測試專案建立一個類,然後做一些準備工作:

這裡我準備了一個List<KeyValuePair<int, double>>,它有100000條資料,是隨機生成的。

 

然後是測試方法,在這裡我們使用[Benchmark]替代了xUnit單元測試中的[Fact]:

xUnit.Performance的測試會跑很多次,結果是取平均值的

這裡我們迴圈遍歷Benchmark.Iterations,它有一個預設值,我這裡預設是跑了1000次迴圈。

再迴圈裡,首先您可以做一些準備工作。然後使用iteration.StartMeasurement()來開始進行測量。

只有iteration.StartMeasurement()後邊的部分才會被測量,在大括號裡面寫被測試相關的程式碼就可以了。

 

然後在命令列輸入執行測試:

 

測試結果如下:

提供了控制檯輸出,xml,csv,md輸出(在專案資料夾裡)。

從控制檯可以看到該測試的迴圈跑了1000次,平均結果是0.963毫秒

 

下面是csv結果的截圖:

 

下面是md結果檔案的截圖:

 

下面是xml結果檔案的截圖,它裡面有詳細資料:

 

內部迴圈

xUnit.Performance還可以新增一個內部迴圈屬性 InnerIterationCount。先看程式碼,新增以下方法:

[Benchmark(InnerIterationCount = 10_000)],這裡的InnerIterationCount是內部迴圈遍歷的次數。

在StartMeasurement()之後,進行內部迴圈。

這樣的話,外層迴圈的次數可能會很少,而且第一次外層迴圈是熱身,不包括在測試結果中

而內部迴圈適合於執行比較快速的程式碼(微秒級)。

 

有時確實需要這樣兩層迴圈,做一些熱身工作或者需要完成不同級別的準備工作。

 

然後我們來跑測試

在結果裡看到外層迴圈有2次的記錄,但是它實際跑了3次,第一次算作熱身,不做統計。

它的時間是內層迴圈的總和,除以10000之後,和第一個方法的結果差不太多。

 

我可以在方法中列印輸出迴圈次數:

其結果如下:

可以看到確實是跑了3次,但統計了2次。

 

然後我再新增另外兩個測試方法,分別測試另外兩個方法:

 

執行測試:

可以看到現在這4個測試方法的結果。

看來針對List來說foreach要比linq和for迴圈快。

注意foreach測試的外層迴圈跑了2次,而for和linq的測試迴圈只跑了1次,可能是因為花費時間太久了吧?這個我不太確定

 

StopWatch

可以看到測試命令的引數 stopwatch,它應該是來自System.Diagnostics名稱空間下的StopWatch類。

它有Start()和Stop()方法和一些其它屬性用來統計逝去的時間。

StopWatch類是跨平臺的,但是在其它系統上,它只能統計時間;而在Windows上,它還可以使用核心ETW events和CPU效能計數來給您更多的資料,具體請查閱相關資料。

 

結語

該庫還有很多功能和命令的引數,具體請參考文件:https://github.com/Microsoft/xunit-performance

但是要注意,它仍然是beta狀態,只能在MyGet而不是Nuget獲取。

相關文章