Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9541

【.NET/C#】メソッドのパフォーマンスを簡単に集計するライブラリの紹介

$
0
0
概要 BenchmarkDotNetの紹介と使い方を掲載する。 BenchmarkDotNetはメソッドのパフォーマンスを簡単に計測できるライブラリです。 統計的な情報をレポートしてくれるため、関数のパフォーマンスを調査する業務で役立ちそう。 BenchmarkDotNetとは? BenchmarkDotNetは、メソッドをベンチマークに変換し、そのパフォーマンスを追跡し、 再現性のある測定実験を共有するのに役立ちます。 上記はGitHubの記載内容の機械翻訳 サポート対象は下記。.NET系なら大体使えそう。 本記事ではC#で実装する。 Projects: classic and modern with PackageReferences Runtimes: Full .NET Framework (4.6+), .NET Core (2.0+), Mono, CoreRT OS: Windows, Linux, MacOS Languages: C#, F#, VB 本記事では試しませんが、エクスポート機能も充実している。 手順 下記ドキュメントのOverViewに従って、パフォーマンス計測を行う。 BenchmarkDotNetの公式ドキュメント 1.NuGetパッケージをインストール Visual Studioを立ち上げ、「コンソールアプリケーション」を選択する。 下記のNuGetパッケージを追加する。 NuGetリンク 2.計測対象のメソッドを含むクラスを作成する 扱う題材(計測対象) stringとStringBuilderの文字列結合の処理時間を比較する。 期待結果は「StringBuilderの方がパフォーマンスが優れる」こと。 ※下記サイトで既に検証済み 文字列処理を高速に行う StringBuilderがパフォーマンスが優れる理由 下記の違いにより、扱う文字の数が増えるとStringBuilderの方がパフォーマンスに優れることが多い。 ・string型は編集不可なため、文字列操作の度に新たなインスタンスを作る。 ・StringBuilderは編集可能であるため、文字列操作で新たなインスタンスを作らない。 stringとStringBuilderの違い 計測対象のクラスを定義する 計測対象のメソッド含むクラスを定義する。 対象メソッドには[Benchmark]属性を指定する。 using BenchmarkDotNet.Attributes; using System.Text; public class StringConcatMesurement { private int NumberOfItems = 200000; [Benchmark] public string WithStringBuilder() { var sb = new StringBuilder(); for (int i = 0; i < NumberOfItems; i++) { sb.Append("1"); } return sb.ToString(); } [Benchmark] public string WithStringType() { string s = string.Empty; for (int i = 0; i < NumberOfItems; i++) { s += "1"; } return s; } } メイン関数からコールする using BenchmarkDotNet.Running; class Program { static void Main(string[] args) { var summary = BenchmarkRunner.Run<StringConcatMesurement>(); } } これで準備完了!!実行する前に一点だけ注意事項。 [リリースビルド]で実行すること。 [デバックビルド]の場合は、下記のように実行時エラーになる。 ちなみに、このままの状態で実行すると私の環境で10分以上掛かった。 ベンチマーク計測に正確さが必要ない場合、[ShortRunJob]属性をつけて下さい(後述の項「計測時間の短縮をする方法」を参照)。 計測開始時 計測終了時 計測結果を確認する 下記の結果が表示される。 ・実行環境 ・統計情報の表(関数名、平均値、エラー、標準偏差) ・統計情報のラベルの説明 // * Summary * BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.867 (2004/?/20H1) Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=3.1.402 [Host] : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT DefaultJob : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT | Method | Mean | Error | StdDev | |------------------ |---------------:|--------------:|--------------:| | WithStringBuilder | 895.1 us | 15.62 us | 21.90 us | | WithStringType | 7,806,015.2 us | 155,282.10 us | 347,310.67 us | // * Hints * Outliers StringConcatMesurement.WithStringBuilder: Default -> 4 outliers were removed (978.11 us..1.02 ms) StringConcatMesurement.WithStringType: Default -> 4 outliers were removed (9.74 s..10.10 s) // * Legends * Mean : Arithmetic mean of all measurements Error : Half of 99.9% confidence interval StdDev : Standard deviation of all measurements 1 us : 1 Microsecond (0.000001 sec) 予想通りStringBuilderの方がパフォーマンスが優れる。 平均値で比較すると、約8721倍StringBuilderが速い。 標準偏差を見ても、stringに比べると計測結果のバラツキが小さいことが分かる。 計測結果サマリをカスタマイズする サマリーは下記の詳細結果のうち、属性でどれを出すかを指定する。 デフォルトだと、平均値、エラー、標準偏差を表示する。 試しに最小値、最大値を追加する。 // * Detailed results * StringConcatMesurement.WithStringBuilder: DefaultJob Runtime = .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT; GC = Concurrent Workstation Mean = 830.978 us, StdErr = 3.726 us (0.45%), N = 15, StdDev = 14.432 us Min = 809.898 us, Q1 = 821.067 us, Median = 826.252 us, Q3 = 844.369 us, Max = 855.115 us IQR = 23.303 us, LowerFence = 786.113 us, UpperFence = 879.323 us ConfidenceInterval = [815.549 us; 846.406 us] (CI 99.9%), Margin = 15.428 us (1.86% of Mean) Skewness = 0.28, Kurtosis = 1.64, MValue = 2 -------------------- Histogram -------------------- [802.218 us ; 834.536 us) | @@@@@@@@@@ [834.536 us ; 857.584 us) | @@@@@ --------------------------------------------------- StringConcatMesurement.WithStringType: DefaultJob Runtime = .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT; GC = Concurrent Workstation Mean = 7.206 s, StdErr = 0.051 s (0.70%), N = 95, StdDev = 0.493 s Min = 6.459 s, Q1 = 6.804 s, Median = 7.101 s, Q3 = 7.443 s, Max = 8.521 s IQR = 0.639 s, LowerFence = 5.846 s, UpperFence = 8.402 s ConfidenceInterval = [7.034 s; 7.378 s] (CI 99.9%), Margin = 0.172 s (2.38% of Mean) Skewness = 0.84, Kurtosis = 3.15, MValue = 2.93 -------------------- Histogram -------------------- [6.317 s ; 6.608 s) | @@@@ [6.608 s ; 6.891 s) | @@@@@@@@@@@@@@@@@@@@@@@@ [6.891 s ; 7.243 s) | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ [7.243 s ; 7.605 s) | @@@@@@@@@@@@@@@@@@@ [7.605 s ; 7.892 s) | @@@@@@@ [7.892 s ; 8.269 s) | @@@@@@@ [8.269 s ; 8.663 s) | @@@ --------------------------------------------------- サマリーのカスタマイズ実装 [MinColumn,MaxColumn]//←新規追加 public class StringConcatMesurement { private int NumberOfItems = 200000; [Benchmark] public string WithStringBuilder() { //略 } [Benchmark] public string WithStringType() { //略 } } カスタマイズ後のサマリー 最小値、最大値の項目が増えた。 | Method | Mean | Error | StdDev | Min | Max | |------------------ |---------------:|--------------:|--------------:|---------------:|---------------:| | WithStringBuilder | 831.0 us | 15.43 us | 14.43 us | 809.9 us | 855.1 us | | WithStringType | 7,205,897.0 us | 171,677.49 us | 492,574.91 us | 6,458,541.9 us | 8,520,846.9 us | 計測時間の短縮をする方法 関数のパフォーマンスを改善する場面などでは、正確性よりも試行回数が重視されると思う。 何も制限なしで使うと、文字列の結合の評価だけで約10分かかった。 とても待ってられない。 下記[ShortRunJob]属性指定することで、関数の試行回数(N=3)で固定されて計測時間を短縮できる。 [ShortRunJob]属性指定 [ShortRunJob]//←これ [MinColumn, MaxColumn] public class StringConcatMesurement { //(略) } [ShortRunJob]属性指定前の計測時間 Global total time: 00:10:46 (646.11 sec), executed benchmarks: 2 [ShortRunJob]属性指定後の計測時間 Global total time: 00:00:57 (57.23 sec), executed benchmarks: 2 参考にしたサイト BenchmarkDotNetの公式ドキュメント How to benchmark C# code using BenchmarkDotNet @neueccさんが書いたベンチマークの測り方 文字列処理を高速に行う stringとStringBuilderの違い

Viewing all articles
Browse latest Browse all 9541

Trending Articles