性能测试的准确时间测量

时间:2009-06-09 10:37:33

标签: c# .net performance testing

查看某些内容(例如方法调用)接受代码的时间最长的方法是什么?

我猜的最容易和最快的是:

DateTime start = DateTime.Now;
{
    // Do some work
}
TimeSpan timeItTook = DateTime.Now - start;

但这有多精确?还有更好的方法吗?

8 个答案:

答案 0 :(得分:529)

更好的方法是使用秒表类:

using System.Diagnostics;
// ...

Stopwatch sw = new Stopwatch();

sw.Start();

// ...

sw.Stop();

Console.WriteLine("Elapsed={0}",sw.Elapsed);

答案 1 :(得分:159)

正如其他人所说,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
});

您甚至可以为此设置ITimer界面,StopwatchTimer, CpuTimer等实施可用。

答案 2 :(得分:73)

正如其他人所说,Stopwatch应该是正确的工具。尽管如此,对它的改进却很少,具体见这个帖子:Benchmarking small code samples in C#, can this implementation be improved?

我在Thomas Maierhofer here

看到了一些有用的提示

基本上他的代码如下:

//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保持忙碌的时间运行代码/进程as shown here这可以反映更真实的情况,因为没有其他过程会影响测量。它的确如下:

 var start = Process.GetCurrentProcess().TotalProcessorTime;
 method();
 var stop = Process.GetCurrentProcess().TotalProcessorTime;
 print (end - begin).TotalMilliseconds;

samething can be found here.

的详细实施

我编写了一个帮助程序类,以易于使用的方式执行:

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 }的{​​{1}}的平均值。

220

答案 3 :(得分:13)

使用Stopwatch

答案 4 :(得分:12)

System.Diagnostics.Stopwatch专为此任务而设计。

答案 5 :(得分:5)

秒表很好,但循环工作10 ^ 6次,然后除以10 ^ 6。 你会得到更多精确度。

答案 6 :(得分:3)

我正在使用它:

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# Time Measurement For Performance Testing(不是英文)

答案 7 :(得分:-12)

是的,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;
    }
}