精确的时间测量用于性能测试

这个问题在这里已经有了答案:

  • 如何在.NET中测量代码性能? 17个答案

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

    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

    上一篇: Exact time measurement for performance testing

    下一篇: Performing a Stress Test on Web Application?