メソッド呼び出しなど、コードに取り込まれた時間を知るための最も正確な方法は何ですか?
私が推測する最も簡単で最速のものはこれです:
DateTime start = DateTime.Now;
{
// Do some work
}
TimeSpan timeItTook = DateTime.Now - start;
しかし、これはどの程度正確ですか?もっと良い方法はありますか?
より良い方法はStopwatchクラスを使うことです:
using System.Diagnostics;
// ...
Stopwatch sw = new Stopwatch();
sw.Start();
// ...
sw.Stop();
Console.WriteLine("Elapsed={0}",sw.Elapsed);
他の人が言っているように、Stopwatch
はここで使うのに良いクラスです。あなたは役に立つ方法でそれを包むことができます:
public static TimeSpan Time(Action action)
{
Stopwatch stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
return stopwatch.Elapsed;
}
(Stopwatch.StartNew()
の使用に注意してください。私はこれをストップウォッチを作成してからStart()
を呼び出すほうが簡単です。)明らかにこれはデリゲートを呼び出すヒットを招きますが、大抵の場合は関係ないでしょう。あなたはそれから書くでしょう:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
利用可能な場合はStopwatchTimer,
ITimer
などの実装を使用して、CpuTimer
インターフェースを作成することもできます。
他の人が言ったように、Stopwatch
はこれのための正しいツールであるべきです。ただし、このスレッドを参照してください。 C#で小さなコードサンプルをベンチマークする、この実装を改善できますか。 。
Thomas Maierhoferがここで役に立つヒントをいくつか見ました
基本的に彼のコードはこんな感じです:
//prevent the JIT Compiler from optimizing Fkt calls away
long seed = Environment.TickCount;
//use the second Core/Processor for the test
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
//prevent "Normal" Processes from interrupting Threads
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
//prevent "Normal" Threads from interrupting this thread
Thread.CurrentThread.Priority = ThreadPriority.Highest;
//warm up
method();
var stopwatch = new Stopwatch()
for (int i = 0; i < repetitions; i++)
{
stopwatch.Reset();
stopwatch.Start();
for (int j = 0; j < iterations; j++)
method();
stopwatch.Stop();
print stopwatch.Elapsed.TotalMilliseconds;
}
もう1つの方法は、CPUがビジー状態を維持している時間の測定にProcess.TotalProcessTime
を使用することです。{コードまたはプロセスの実行中、 ここに表示 他のプロセスは測定に影響を及ぼさないのでシナリオ。それは以下のようになります。
var start = Process.GetCurrentProcess().TotalProcessorTime;
method();
var stop = Process.GetCurrentProcess().TotalProcessorTime;
print (end - begin).TotalMilliseconds;
私は使いやすい方法で両方を実行するためのヘルパークラスを書きました:
public class Clock
{
interface IStopwatch
{
bool IsRunning { get; }
TimeSpan Elapsed { get; }
void Start();
void Stop();
void Reset();
}
class TimeWatch : IStopwatch
{
Stopwatch stopwatch = new Stopwatch();
public TimeSpan Elapsed
{
get { return stopwatch.Elapsed; }
}
public bool IsRunning
{
get { return stopwatch.IsRunning; }
}
public TimeWatch()
{
if (!Stopwatch.IsHighResolution)
throw new NotSupportedException("Your hardware doesn't support high resolution counter");
//prevent the JIT Compiler from optimizing Fkt calls away
long seed = Environment.TickCount;
//use the second Core/Processor for the test
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
//prevent "Normal" Processes from interrupting Threads
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
//prevent "Normal" Threads from interrupting this thread
Thread.CurrentThread.Priority = ThreadPriority.Highest;
}
public void Start()
{
stopwatch.Start();
}
public void Stop()
{
stopwatch.Stop();
}
public void Reset()
{
stopwatch.Reset();
}
}
class CpuWatch : IStopwatch
{
TimeSpan startTime;
TimeSpan endTime;
bool isRunning;
public TimeSpan Elapsed
{
get
{
if (IsRunning)
throw new NotImplementedException("Getting elapsed span while watch is running is not implemented");
return endTime - startTime;
}
}
public bool IsRunning
{
get { return isRunning; }
}
public void Start()
{
startTime = Process.GetCurrentProcess().TotalProcessorTime;
isRunning = true;
}
public void Stop()
{
endTime = Process.GetCurrentProcess().TotalProcessorTime;
isRunning = false;
}
public void Reset()
{
startTime = TimeSpan.Zero;
endTime = TimeSpan.Zero;
}
}
public static void BenchmarkTime(Action action, int iterations = 10000)
{
Benchmark<TimeWatch>(action, iterations);
}
static void Benchmark<T>(Action action, int iterations) where T : IStopwatch, new()
{
//clean Garbage
GC.Collect();
//wait for the finalizer queue to empty
GC.WaitForPendingFinalizers();
//clean Garbage
GC.Collect();
//warm up
action();
var stopwatch = new T();
var timings = new double[5];
for (int i = 0; i < timings.Length; i++)
{
stopwatch.Reset();
stopwatch.Start();
for (int j = 0; j < iterations; j++)
action();
stopwatch.Stop();
timings[i] = stopwatch.Elapsed.TotalMilliseconds;
print timings[i];
}
print "normalized mean: " + timings.NormalizedMean().ToString();
}
public static void BenchmarkCpu(Action action, int iterations = 10000)
{
Benchmark<CpuWatch>(action, iterations);
}
}
電話するだけ
Clock.BenchmarkTime(() =>
{
//code
}, 10000000);
または
Clock.BenchmarkCpu(() =>
{
//code
}, 10000000);
Clock
の最後の部分はトリッキーな部分です。あなたが最終的なタイミングを表示したいならば、あなたが望むタイミングの種類を選ぶのはあなた次第です。読み取りタイミングの平均を与える拡張方法NormalizedMean
を書きました。{ノイズを捨てる _私は実際の平均からの各タイミングの偏差を計算し、そして次に私はより遠い値を捨てます偏差の平均から(より遅いものだけ)(絶対偏差と呼ばれます;それはよく聞かれる標準偏差ではないことに注意してください)、そして最後にの平均を返す残りの値これは、例えば、時間指定された値が{ 1, 2, 3, 2, 100 }
(ミリ秒など)であれば、100
を破棄し、{ 1, 2, 3, 2 }
の平均、つまり2
を返すことを意味します。あるいは、タイミングが{ 240, 220, 200, 220, 220, 270 }
の場合、270
を破棄し、{ 240, 220, 200, 220, 220 }
の平均値220
を返します。
public static double NormalizedMean(this ICollection<double> values)
{
if (values.Count == 0)
return double.NaN;
var deviations = values.Deviations().ToArray();
var meanDeviation = deviations.Sum(t => Math.Abs(t.Item2)) / values.Count;
return deviations.Where(t => t.Item2 > 0 || Math.Abs(t.Item2) <= meanDeviation).Average(t => t.Item1);
}
public static IEnumerable<Tuple<double, double>> Deviations(this ICollection<double> values)
{
if (values.Count == 0)
yield break;
var avg = values.Average();
foreach (var d in values)
yield return Tuple.Create(d, avg - d);
}
ストップウォッチ クラスを使う
System.Diagnostics.Stopwatchはこのタスク用に設計されています。
ストップウォッチは問題ありませんが、作業を10 ^ 6回ループしてから10 ^ 6で割ります。もっと正確になるでしょう。
私はこれを使っています:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(myUrl);
System.Diagnostics.Stopwatch timer = new Stopwatch();
timer.Start();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
statusCode = response.StatusCode.ToString();
response.Close();
timer.Stop();
私のブログから: パフォーマンステストのためのC#時間測定 (英語以外)