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, Stopwatchis 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 ITimerinterface for this, with implementations of StopwatchTimer,CpuTimeretc 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, Stopwatchshould 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.TotalProcessTimeto 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 Clockis 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 NormalizedMeanwhich 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);
}

