37

我想测量一段代码的执行情况,我想知道最好的方法是什么?

选项1:

DateTime StartTime = DateTime.Now;

//Code

TimeSpan ts = DateTime.Now.Subtract(StartTime);
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
        ts.Hours, ts.Minutes, ts.Seconds,
        ts.Milliseconds / 10);
Console.WriteLine(elapsedTime, "RunTime");

选项 2:使用 System.Diagnostics;

    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();

    //Code

    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;

    // Format and display the TimeSpan value.
    string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
        ts.Hours, ts.Minutes, ts.Seconds,
        ts.Milliseconds / 10);
    Console.WriteLine(elapsedTime, "RunTime");

这不仅仅是为了进行基准测试,它实际上是应用程序的一部分。函数执行的时间是相关数据。然而,它不需要是原子的或超精确的。

哪个选项更适合生产代码,或者其他人使用不同的东西,也许更好?

4

8 回答 8

28

该类Stopwatch专门设计用于测量经过的时间,并且可以(如果在您的硬件上可用)使用底层高频硬件计时器提供良好的粒度/准确性。所以这似乎是最好的选择。

IsHighResolution属性可用于确定高分辨率计时是否可用。根据文档,此类为“最佳可用”Win32 API 提供了一个包装器,以实现准确的计时:

具体来说,可以使用Frequency字段和 GetTimestamp方法来代替非托管 Win32 API QueryPerformanceFrequencyQueryPerformanceCounter.

这些 Win32 API [here] 和链接的 MSDN docs 2中有详细的背景信息。

高分辨率定时器

计数器是编程中用来指代递增变量的通用术语。一些系统包括一个高分辨率性能计数器,可提供高分辨率的经过时间。

如果系统上存在高分辨率性能计数器,您可以使用QueryPerformanceFrequency 函数来表示频率,以每秒计数为单位。计数的值取决于处理器。例如,在某些处理器上,计数可能是处理器时钟的循环速率。

QueryPerformanceCounter函数检索高分辨率性能计数器的当前值。通过在一段代码的开头和结尾调用此函数,应用程序实质上将计数器用作高分辨率计时器。例如,假设 QueryPerformanceFrequency指示高分辨率性能计数器的频率为每秒 50,000 个计数。如果应用程序在要计时的代码部分之前和之后立即调用 QueryPerformanceCounter ,则计数器值可能分别为 1500 个计数和 3500 个计数。这些值表示代码执行时经过了 0.04 秒(2000 次计数)。

于 2010-10-11T02:49:51.310 回答
13

这不仅StopWatch更准确,而且DateTime.Now在某些情况下会给出不正确的结果。

例如,考虑在夏令时切换期间会发生什么——使用DateTime.Now实际上可以给出否定的答案!

于 2010-10-11T02:58:42.617 回答
6

我一般StopWatch用于这种情况。

MSDN页面:

跑表

提供一组可用于准确测量经过时间的方法和属性。

在下面的文章中,我使用它来比较 LINQ 与 PLINQ 的执行时间:

使用 Visual Studio 2010 的并行 LINQ (PLINQ)

于 2010-10-11T02:47:43.947 回答
5

两者都不会损害性能,因为您说这并不重要。秒表似乎更合适 - 您只是从时间中减去时间,而不是从另一个日期中减去一个日期。日期的东西需要更多的内存和 CPU 时间来处理。如果您计划在多个地方重用它,还有一些方法可以使代码更清晰。using想到了超载。我会搜索一个例子。好的,从以下位置窃取的代码:

http://stevesmithblog.com/blog/great-uses-of-using-statement-in-c/

public class ConsoleAutoStopWatch : IDisposable
{
    private readonly Stopwatch _stopWatch;

    public ConsoleAutoStopWatch()
    {
        _stopWatch = new Stopwatch();
        _stopWatch.Start();
    }

    public void Dispose()
    {
        _stopWatch.Stop();
        TimeSpan ts = _stopWatch.Elapsed;

        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                                           ts.Hours, ts.Minutes, ts.Seconds,
                                           ts.Milliseconds / 10);
        Console.WriteLine(elapsedTime, "RunTime");
    }
}

private static void UsingStopWatchUsage()
{
    Console.WriteLine("ConsoleAutoStopWatch Used: ");
    using (new ConsoleAutoStopWatch())
    {
        Thread.Sleep(3000);
    }
}
于 2010-10-11T02:46:37.353 回答
1

两者都可能很好地满足您的需求,但我会说 use StopWatch。为什么?因为它适用于您正在执行的任务。

你有一个类是为返回当前日期/时间而构建的,它恰好可以用于计时,你有一个专门为计时而设计的类。

在这种情况下,只有在您需要毫秒精度时才真正存在差异(在这种情况下StopWatch更准确),但作为一般原则,如果存在专门用于您正在寻找的任务的工具,那么使用它会更好。

于 2010-10-11T02:58:37.683 回答
1

我有一个小班来临时做这种事情。它使用秒表类 - c# 微性能测试

例如。

var tester = new PerformanceTester(() => SomeMethod());
tester.MeasureExecTime(1000);
Console.Writeline(string.Format("Executed in {0} milliseconds", tester.AverageTime.TotalMilliseconds));
于 2010-10-11T06:12:33.753 回答
0

使用下面的代码

DateTime dExecutionTime;
dExecutionTime = DateTime.Now;
      TimeSpan span = DateTime.Now.Subtract(dExecutionTime);
                  lblExecutinTime.Text = "total time taken " +   Math.Round(span.TotalMinutes,2) + " minutes .   >>---> " + DateTime.Now.ToShortTimeString();
于 2012-03-16T16:33:29.380 回答
0

如果您需要登录到其他地方,我将 Hamish 的回答简化并使其更通用:

public class AutoStopWatch : Stopwatch, IDisposable {

    public AutoStopWatch() {
        Start();
    }

    public virtual void Dispose() {
        Stop();
    }
}

 public class AutoStopWatchConsole : AutoStopWatch {

    private readonly string prefix;

    public AutoStopWatchConsole(string prefix = "") {
        this.prefix = prefix;
    }

    public override void Dispose() {
        base.Dispose();

        string format = Elapsed.Days > 0 ? "{0} days " : "";
        format += "{1:00}:{2:00}:{3:00}.{4:00}";

        Console.WriteLine(prefix + " " + format.Format(Elapsed.Days, Elapsed.Hours, Elapsed.Minutes, Elapsed.Seconds, Elapsed.Milliseconds / 10));
    }
}

private static void Usage() {

    Console.WriteLine("AutoStopWatch Used: ");
    using (var sw = new AutoStopWatch()) {
        Thread.Sleep(3000);

        Console.WriteLine(sw.Elapsed.ToString("h'h 'm'm 's's'"));
    }

    Console.WriteLine("AutoStopWatchConsole Used: ");
    using (var sw = new AutoStopWatchConsole()) {
        Thread.Sleep(3000);
    }

}
于 2013-10-12T00:05:32.163 回答