DateTime.Now は関数のパフォーマンスを測定する最良の方法ですか?
-
09-06-2019 - |
質問
ボトルネックを見つけて、時間をできるだけ正確に測定する必要があります。
次のコード スニペットはパフォーマンスを測定する最良の方法ですか?
DateTime startTime = DateTime.Now;
// Some execution process
DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
解決
いいえ、ちがいます。使用 ストップウォッチ (で System.Diagnostics
)
Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();
Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);
ストップウォッチは高精度タイマーの存在を自動的にチェックします。
言及する価値があるのは、 DateTime.Now
多くの場合、よりもかなり遅いです DateTime.UtcNow
タイムゾーンを調整する必要があるため、 夏時間 など。
DateTime.UtcNow の解像度は通常 15 ミリ秒です。見る ジョン・チャップマンのブログ投稿 について DateTime.Now
優れた概要を正確に表現します。
興味深い雑学:ストップウォッチが元に戻ります DateTime.UtcNow
ハードウェアが高周波カウンターをサポートしていない場合。静的フィールドを調べることで、ストップウォッチが高精度を達成するためにハードウェアを使用しているかどうかを確認できます。 ストップウォッチ.IsHighResolution.
他のヒント
素早く簡単に操作したい場合は、精度を高めるために代わりにストップウォッチを使用することをお勧めします。
Stopwatch sw = new Stopwatch();
sw.Start();
// Do Work
sw.Stop();
Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds);
あるいは、もう少し洗練されたものが必要な場合は、おそらく次のようなサードパーティ プロファイラーの使用を検討する必要があります。 アリ.
この記事 まず最初に 3 つの選択肢を比較する必要があると述べています。 Stopwatch
, DateTime.Now
そして DateTime.UtcNow
.
また、場合によっては (パフォーマンス カウンターが存在しない場合)、Stopwatch が DateTime.UtcNow と追加の処理を使用していることも示しています。そのため、その場合は DateTime.UtcNow が最良のオプションであることは明らかです (他の人がそれを使用し、何らかの処理を行っているため)
ただし、結局のところ、カウンターはほとんど常に存在します - を参照してください。 高解像度パフォーマンスカウンターと.NETストップウォッチに関連するその存在についての説明?.
こちらが性能グラフです。UtcNow が代替手段と比較してパフォーマンス コストがいかに低いかに注目してください。
X 軸はサンプル データ サイズ、Y 軸は例の相対時間です。
一つのこと Stopwatch
より優れている点は、より高い分解能時間測定を提供することです。もう 1 つは、より OO 的な性質を持っていることです。ただし、OO ラッパーを作成すると、 UtcNow
難しいことはできません。
ベンチマーク コードをユーティリティ クラス/メソッドにプッシュすると便利です。の StopWatch
クラスは必要ありません Disposed
または Stopped
エラーで。したがって、最も単純なコードは、 時間 いくつかの アクション は
public partial class With
{
public static long Benchmark(Action action)
{
var stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
サンプル呼び出しコード
public void Execute(Action action)
{
var time = With.Benchmark(action);
log.DebugFormat(“Did action in {0} ms.”, time);
}
拡張メソッドのバージョンは次のとおりです
public static class Extensions
{
public static long Benchmark(this Action action)
{
return With.Benchmark(action);
}
}
そしてサンプル呼び出しコード
public void Execute(Action action)
{
var time = action.Benchmark()
log.DebugFormat(“Did action in {0} ms.”, time);
}
System.Diagnostics.Stopwatch クラスを使用します。
Stopwatch sw = new Stopwatch();
sw.Start();
// Do some code.
sw.Stop();
// sw.ElapsedMilliseconds = the time your "do some code" took.
ストップウォッチも同様で、はるかに優れています。
パフォーマンスの測定に関しては、「// 一部の実行プロセス」が非常に短いプロセスかどうかも確認する必要があります。
また、「// 一部の実行プロセス」の最初の実行は、それ以降の実行よりもはるかに遅い可能性があることにも留意してください。
通常、メソッドをループ内で 1000 回または 1000000 回実行してテストしますが、1 回実行するよりもはるかに正確なデータが得られます。
これらはすべて時間を測定するための優れた方法ですが、ボトルネックを見つけるための非常に間接的な方法にすぎません。
スレッドのボトルネックを見つける最も直接的な方法は、スレッドを実行し、待機させるような処理を実行している間に、一時停止キーまたはブレーク キーを使用してスレッドを停止することです。これを数回繰り返します。ボトルネックに時間が X% かかる場合、X% は各スナップショットの動作中にボトルネックを発見する確率です。
参考までに、.NET Timer クラスは診断用ではなく、次のように事前に設定された間隔でイベントを生成します ( MSDN):
System.Timers.Timer aTimer;
public static void Main()
{
// Create a timer with a ten second interval.
aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 2 seconds (2000 milliseconds).
aTimer.Interval = 2000;
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program.");
Console.ReadLine();
}
// Specify what you want to happen when the Elapsed event is
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
したがって、これは実際に何かにかかった時間を知るのには役立ちません。単に一定の時間が経過したことを知るだけです。
タイマーは System.Windows.Forms... のコントロールとしても公開されます。VS05/VS08 のデザイナー ツール ボックスにあります。
これが正しい方法です:
using System;
using System.Diagnostics;
class Program
{
public static void Main()
{
Stopwatch stopWatch = Stopwatch.StartNew();
// some other code
stopWatch.Stop();
// this not correct to get full timer resolution
Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds);
// Correct way to get accurate high precision timing
Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds);
}
}
詳細については、こちらをご覧ください 正確なパフォーマンス カウンターを取得するには、DataTime の代わりに Stopwatch を使用します。.
Visual Studio チーム システム には、この問題の解決に役立つ機能がいくつかあります。基本的に、単体テストを作成し、それらをさまざまなシナリオで組み合わせて、ストレス テストまたは負荷テストの一部としてソフトウェアに対して実行できます。これは、アプリケーションのパフォーマンスに最も影響を与えるコード領域を特定するのに役立つ場合があります。
Microsoft のパターンとプラクティス グループには、次のようなガイダンスがあります。 Visual Studio チーム システムのパフォーマンス テスト ガイダンス.
ヴァンス・モリソンのブログで次のような投稿を見つけました。 CodeTimer クラス 彼はそれを使うと書いた StopWatch
より簡単で、サイドでいくつかの優れた処理を実行します。
私はこの種のパフォーマンス チェックをほとんど行ったことがないので (「これは遅い、もっと速くしろ」と考える傾向があります)、ほぼ常にこれを使用しています。
Google でパフォーマンスをチェックするためのリソースや記事がたくさん見つかります。
パフォーマンス情報を取得するために pinvoke を使用すると多くの人が言及しています。私が勉強している資料の多くは、perfmon の使用についてのみ言及しています。
編集:
StopWatch の講演を見ました。ニース!私は何かを学びました:)
私のプログラム内での使用方法は、ここに示すように StopWatch クラスを使用することです。
Stopwatch sw = new Stopwatch();
sw.Start();
// Critical lines of code
long elapsedMs = sw.Elapsed.TotalMilliseconds;
これは十分に専門的ではありません:
Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();
Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);
より信頼性の高いバージョンは次のとおりです。
PerformWork();
int repeat = 1000;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repeat; i++)
{
PerformWork();
}
sw.Stop();
Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds / repeat);
実際のコードでは、マネージド ヒープを既知の状態に変更するための GC.Collect 呼び出しを追加し、ETW プロファイルでコードの異なる間隔を簡単に分離できるように Sleep 呼び出しを追加します。