C# 性能测试的精确时间测量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/969290/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Exact time measurement for performance testing
提问by Svish
What is the most exact way of seeing how long something, for example a method call, took in code?
查看代码中某项(例如方法调用)花费多长时间的最准确方法是什么?
The easiest and quickest I would guess is this:
我猜最简单和最快的是:
DateTime start = DateTime.Now;
{
// Do some work
}
TimeSpan timeItTook = DateTime.Now - start;
But how exact is this? Are there better ways?
但这有多准确?有更好的方法吗?
采纳答案by Philippe Leybaert
A better way is to use the Stopwatch class:
更好的方法是使用 Stopwatch 类:
using System.Diagnostics;
// ...
Stopwatch sw = new Stopwatch();
sw.Start();
// ...
sw.Stop();
Console.WriteLine("Elapsed={0}",sw.Elapsed);
回答by Dimi Takis
System.Diagnostics.Stopwatch is designed for this task.
System.Diagnostics.Stopwatch 专为此任务而设计。
回答by Alin Vasile
Yes there are some function on Windows kernel
是的,Windows 内核上有一些功能
[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);
[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceFrequency(ref long lpFrequency);
public static float CurrentSecond
{
get
{
long current = 0;
QueryPerformanceCounter(ref current);
long frequency = 0;
QueryPerformanceFrequency(ref frequency);
return (float) current / (float) frequency;
}
}
回答by Jon Skeet
As others have said, Stopwatch
is a good class to use here. You can wrap it in a helpful method:
正如其他人所说,Stopwatch
在这里使用是一个很好的类。你可以用一个有用的方法包装它:
public static TimeSpan Time(Action action)
{
Stopwatch stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
return stopwatch.Elapsed;
}
(Note the use of Stopwatch.StartNew()
. I prefer this to creating a Stopwatch and then calling Start()
in terms of simplicity.) Obviously this incurs the hit of invoking a delegate, but in the vast majority of cases that won't be relevant. You'd then write:
(注意 的使用Stopwatch.StartNew()
。我更喜欢创建一个秒表然后Start()
在简单性方面调用它。)显然这会导致调用委托的命中,但在绝大多数情况下不会相关。然后你会写:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
You could even make an ITimer
interface for this, with implementations of StopwatchTimer,
CpuTimer
etc where available.
您甚至可以ITimer
为此创建一个接口,并StopwatchTimer,
CpuTimer
在可用的情况下实现等。
回答by Mike Dunlavey
Stopwatch is fine, but loop the work 10^6 times, then divide by 10^6. You'll get a lot more precision.
秒表很好,但将工作循环 10^6 次,然后除以 10^6。你会得到更高的精度。
回答by altansezerayan
I'm using this:
我正在使用这个:
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();
回答by nawfal
As others said, Stopwatch
should be the right tool for this. There can be few improvements made to it though, see this thread specifically: Benchmarking small code samples in C#, can this implementation be improved?.
正如其他人所说,Stopwatch
应该是正确的工具。但是,可以对其进行一些改进,具体请参阅此线程:Benchmarking small code samples in C#,可以改进此实现吗?.
I have seen some useful tips by Thomas Maierhofer here
我在这里看到了Thomas Maierhofer 的一些有用提示
Basically his code looks like:
基本上他的代码看起来像:
//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;
}
Another approach is to rely on Process.TotalProcessTime
to measure how long the CPU has been kept busy running the very code/process, as shown hereThis can reflect more real scenario since no other process affects the measurement. It does something like:
另一种方法是依靠Process.TotalProcessTime
测量 CPU 一直忙于运行代码/进程的时间,如下所示这可以反映更真实的场景,因为没有其他进程影响测量。它执行以下操作:
var start = Process.GetCurrentProcess().TotalProcessorTime;
method();
var stop = Process.GetCurrentProcess().TotalProcessorTime;
print (end - begin).TotalMilliseconds;
A naked, detailed implementation of the samething can be found here.
可以在此处找到相同内容的裸露详细实现。
I wrote a helper class to perform both in an easy to use manner:
我编写了一个辅助类,以易于使用的方式执行这两种操作:
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);
}
}
Just call
打电话就行
Clock.BenchmarkTime(() =>
{
//code
}, 10000000);
or
或者
Clock.BenchmarkCpu(() =>
{
//code
}, 10000000);
The last part of the Clock
is the tricky part. If you want to display the final timing, its up to you to choose what sort of timing you want. I wrote an extension method NormalizedMean
which gives you the mean of the read timings discarding the noise.I mean I calculate the the deviation of each timing from the actual mean, and then I discard the values which was farer (only the slower ones) from the mean of deviation(called absolute deviation; note that its not the often heard standard deviation), and finally return the mean of remaining values. This means, for instance, if timed values are { 1, 2, 3, 2, 100 }
(in ms or whatever), it discards 100
, and returns the mean of { 1, 2, 3, 2 }
which is 2
. Or if timings are { 240, 220, 200, 220, 220, 270 }
, it discards 270
, and returns the mean of { 240, 220, 200, 220, 220 }
which is 220
.
的最后一部分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);
}