C# 性能测试的精确时间测量

C# 性能测试的精确时间测量,c#,.net,performance,testing,C#,.net,Performance,Testing,查看某个东西(例如方法调用)在代码中花费了多长时间的最准确方法是什么 我想最简单、最快捷的方法是: DateTime start = DateTime.Now; { // Do some work } TimeSpan timeItTook = DateTime.Now - start; 但这有多准确?有更好的方法吗?使用秒表类更好的方法是使用秒表类: using System.Diagnostics; // ... Stopwatch sw = new Stopwatch();

查看某个东西(例如方法调用)在代码中花费了多长时间的最准确方法是什么

我想最简单、最快捷的方法是:

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

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

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

using System.Diagnostics;
// ...

Stopwatch sw = new Stopwatch();

sw.Start();

// ...

sw.Stop();

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

System.Diagnostics.Stopwatch是为完成此任务而设计的。

正如其他人所说,
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
接口,在可用的情况下实现
Stopwatch计时器、
CpuTimer
等功能。

秒表可以,但可以循环工作10^6次,然后除以10^6。 您将获得更高的精度。

我使用的是:

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();

正如其他人所说,
Stopwatch
应该是正确的工具。不过,可以对其进行一些改进,具体请参见以下线程:

我已经看到了一些有用的提示

基本上,他的代码如下所示:

//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;
}
一个赤裸裸的、详细的

我编写了一个帮助器类,以易于使用的方式执行这两个操作:

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);
    }
}

时钟的最后一部分是棘手的部分。如果要显示最终计时,则由您选择所需的计时类型。我编写了一个扩展方法
NormalizedMean
,它给出了消除噪声的读取时间的平均值。我的意思是,我计算每个时间点与实际平均值的偏差,然后丢弃偏离平均值(称为绝对偏差;注意,这不是经常听到的标准偏差)较远的值(只有较慢的值),最后返回剩余值的平均值。这意味着,例如,如果计时值为
{1,2,3,2,100}
(毫秒或其他单位),它将丢弃
100
,并返回
{1,2,3,2}
的平均值,即
2
。或者,如果计时是
{240,220,200,220,220,270}
,它将丢弃
270
,并返回
{240,220,200,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);
}
publicstaticdoublenormalizedmean(此ICollection值)
{
如果(values.Count==0)
返回double.NaN;
变量偏差=值。偏差();
var meanDeviation=偏差.Sum(t=>Math.Abs(t.Item2))/values.Count;
返回偏差,其中(t=>t.Item2>0 | | Math.Abs(t.Item2)t.Item1);
}
公共静态IEnumerable偏差(此ICollection值)
{
如果(values.Count==0)
屈服断裂;
var avg=值。平均值();
foreach(值中的变量d)
产生返回元组.Create(d,avg-d);
}


您不会因为不知道.NET类是如何工作的而押注于它们吗?这是否意味着您也害怕使用String类?无论如何,Stopwatch类的文档明确表示它正在使用QueryPerformanceCounter()Win32 API函数。String类与此无关。如果.NET中存在秒表,我怎么知道它比QueryPerformanceCounter好?哪一个是可以存在的最佳选项@pixel3cs:因为你在评论中受到批评而对正确答案投否决票不是很好mature@pixel3cs但是您有时间阅读Kernel32API吗?如果您需要知道特定机器上秒表计时的分辨率,那么您可以使用Stopwatch.Frequency属性。另外,Stopwatch.StartNew()也可以静态方法是一种在一条线上创建和启动秒表的便捷方法。投票人:我们很高兴知道出了什么问题!猜一猜,因为你几乎在同一时间回答了相同的问题,而且描述较少。(我没有投你赞成票或反对票)看看上面关于这个问题的评论,你就会明白为什么了。说得好,但在那10^6次上仍然需要一些时间:)把秒表放在整个事情上。我认为这很清楚。@JonSkeet在循环中调用此实用程序(即根据第二个代码示例)时,调用
action()
似乎会增加第一次迭代的成本。你能解释一下这里发生了什么吗?如果可以的话,在评论中解释一下?!非常感谢..@ppejovic:这可能是JIT编译的成本,也可能是初始化lambda表达式使用的类的成本。“增加的成本”到底有多大?@ppejovic:如果你在调试,那么你应该完全忽略所有性能结果。但是不管你是否在调试,JIT编译都会发生,只是进行了不同的优化。@NAKRO:那是因为你说的“工作”只是“开始一项新任务”。这真的不需要很长时间。所以,是的,它确实给出了正确的结果,但是你没有测量你真正想要测量的东西。如果你想测量任务完成所需的时间,那么你需要等待它完成。@NAKRO:可以,但你需要确保所涉及的“操作”启动所有任务并等待它们完成。关于控制环境和忽略峰值的详细信息!谢谢。在原始示例中,
long seed=Environment.TickCount被用作被测试算法的输入,并防止在编译时对其进行评估。此处未使用该种子。有关
values.developments()
method..的情况如何?(也许我可以自己做,但如果有专家的输入就好了)介意分享一下可变计时器的类型以及如何读取经过的时间吗?
Clock.BenchmarkTime(() =>
{
    //code

}, 10000000);
Clock.BenchmarkCpu(() =>
{
    //code

}, 10000000);
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);
}