精确的时间测量用于性能测试
这个问题在这里已经有了答案:
更好的方法是使用秒表类:
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()
的用法,我更喜欢创建一个Stopwatch,然后以简单的方式调用Start()
。显然这招致了调用委托的打击,但在绝大多数情况下,没关系。 你会写:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
你甚至可以ITimer
创建一个ITimer
接口,并且可以使用StopwatchTimer,
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;
}
另一种方法是依靠Process.TotalProcessTime
来测量CPU一直忙于运行代码/进程的时间,如下所示,这可以反映更真实的场景,因为没有其他进程影响测量。 它做类似的事情:
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);
}
链接地址: http://www.djcxy.com/p/52531.html