概要
クライアントが「Web APIをコールし、サーバから応答を得るまでの時間」を計測した。
具体的には、HttpClientとBenchmarkDotNetを使って、処理時間の集計(平均値・最小値・最大値を算出)し、集計結果をHTMLでエクスポートとした。
計測の手順と実装を掲載する。
動作環境
クライアント
.NET Framework4.6.1
Nugetパッケージ
・BenchmarkDotNet 0.13.0
・NewtonsoftJson 13.0.1
サーバー
ASP.NET Web API
.NET Framework4.6.1
計測予定のWebAPIの一覧
今回はGET/PUT/POST/DELETEを準備する。
下記表のWeb APIをサーバ側に実装する(自動生成コードをそのまま利用する)。
HTTPメソッド | URL |
---|---|
GET | http://localhost:19691/api/values |
POST | http://localhost:19691/api/values |
PUT | http://localhost:19691/api/values/{id} |
DELETE | http://localhost:19691/api/values/{id} |
アプリケーションを作成する。
クライアントを作成する
コンソールアプリケーションでソリューションを作成して、下記手順を実行する。
1. BenchmarkDotNetを適用する。
「BenchmarkDotNet」をNugetパッケージでインストールする。
2. 計測対象のメソッドを作成する。
「BenchmarkDotNet」で計測する対象メソッド、その他オプションは「属性」で設定する。
各属性の意味はコメントで補足説明する。
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Newtonsoft.Json;
namespace BenchMark
{
[HtmlExporter]//Htmlエクスポート
public class WebApiClient
{
private static readonly HttpClient Client = new HttpClient();
private static string _jsonString;
private static StringContent _data;
[GlobalSetup]//初回セットアップ
public void GlobalSetUp()
{
_jsonString = JsonConvert.SerializeObject("TestMessage");
_data = new StringContent(_jsonString, Encoding.UTF8, mediaType: "application/json");
}
[Benchmark]//計測対象のメソッドに指定
public async Task GetAsync()
{
var response = await Client.GetAsync("http://localhost:19691/api/values");
response.EnsureSuccessStatusCode();
}
[Benchmark]
public async Task PutAsync()
{
var response = await Client.PutAsync("http://localhost:19691/api/values/5", _data);
response.EnsureSuccessStatusCode();
}
[Benchmark]
public async Task PostAsync()
{
var response = await Client.PostAsync("http://localhost:19691/api/values", _data);
response.EnsureSuccessStatusCode();
}
[Benchmark]
public async Task DeleteAsync()
{
var response = await Client.DeleteAsync("http://localhost:19691/api/values/5");
response.EnsureSuccessStatusCode();
}
}
}
3. メイン関数から計測対象をコールする。
using System.Threading.Tasks;
using BenchmarkDotNet.Running;
namespace BenchMark
{
class Program
{
static void Main(string[] args)
{
//CheckOperation().GetAwaiter().GetResult();
//計測を開始する
BenchmarkRunner.Run<WebApiClient>();
}
/// <summary>
/// ベンチマーク測定前の動作確認用
/// </summary>
static async Task CheckOperation()
{
var client = new WebApiClient();
client.GlobalSetUp();
await client.GetAsync();
await client.PutAsync();
await client.PostAsync();
await client.DeleteAsync();
}
}
}
サーバを作成する。
自動生成コード使って、Web APIを準備する。
1. Web アプリケーションでソリューションを作成
2. Web APIが自動生成されたことを確認する。
「ソリューションエクスプローラー」
→「Controllers」
ValuesControllers.csを開く。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace HttpServerBenchMark.Controllers
{
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get( int id )
{
return "value";
}
// POST api/values
public void Post( [FromBody]string value )
{
}
// PUT api/values/5
public void Put( int id, [FromBody]string value )
{
}
// DELETE api/values/5
public void Delete( int id )
{
}
}
}
パフォーマンス計測
1. サーバーを実行する。
2. クライアントを実行する。
※Releaseビルドで実行すること。
計測が完了することを待つ。
計測開始時のターミナル出力
// Validating benchmarks:
// ***** BenchmarkRunner: Start *****
// ***** Found 4 benchmark(s) in total *****
// ***** Building 1 exe(s) in Parallel: Start *****
:
:
計測終了時のターミナル出力
// ***** BenchmarkRunner: End *****
// ** Remained 0 benchmark(s) to run **
Run time: 00:01:59 (119.94 sec), executed benchmarks: 4
Global total time: 00:02:02 (122.68 sec), executed benchmarks: 4
3. 集計結果を確認する。
ターミナル出力で「Summary」を探して確認する。
結果サマリーを確認する
// * Summary *
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.985 (21H1/May2021Update)
Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
[Host] : .NET Framework 4.8 (4.8.4341.0), X86 LegacyJIT
DefaultJob : .NET Framework 4.8 (4.8.4341.0), X86 LegacyJIT
| Method | Mean | Error | StdDev | Median |
|------------ |---------:|----------:|----------:|---------:|
| GetAsync | 1.921 ms | 0.1567 ms | 0.4469 ms | 1.749 ms |
| PutAsync | 1.495 ms | 0.0319 ms | 0.0926 ms | 1.494 ms |
| PostAsync | 1.472 ms | 0.0291 ms | 0.0638 ms | 1.474 ms |
| DeleteAsync | 1.281 ms | 0.0256 ms | 0.0455 ms | 1.293 ms |
ファイルのExportを確認する。
今回はHTMLファイル出力しているため、ターミナルでExport先を確認する。
// ***** BenchmarkRunner: Finish *****
// * Export *
BenchmarkDotNet.Artifacts\results\BenchMark.WebApiClient-report.csv
BenchmarkDotNet.Artifacts\results\BenchMark.WebApiClient-report-github.md
BenchmarkDotNet.Artifacts\results\BenchMark.WebApiClient-report.html
ファイルエクスプローラで下記パスに移動する。
ソリューションのパス\bin\Release\BenchmarkDotNet.Artifacts
下記画像のファイルが存在する。
「BenchMark.WebApiClient-20210606-231210.log(※)」に、ターミナル出力と同様の内容が記録されている。
※ソリューション名-クラス名-実行日時.logって命名の様子。
ソリューションのパス\bin\Release\BenchmarkDotNet.Artifacts\resultsに移動する。
BenchmarkDotNet.Artifacts\results\BenchMark.WebApiClient-report.htmlを開く。
下図のようにサマリーと同様の内容が表示される。
logファイルとResultフォルダを控えておけば、報告資料や調査結果にそのまま利用できそう。
参考
No | リンク | 説明 |
---|---|---|
1 | 【.NET/C#】メソッドのパフォーマンスを簡単に集計するライブラリの紹介 | 過去に書いたQuiitaの記事。内容はBenchmarkDotNetの紹介と使い方に関して記載した。 |
2 | BenchmarkDotNetの公式ドキュメント | 公式ドキュメントのリンク。 |
3 | Github:本記事のソース | 本記事のソースをGithubに公開してます。 |