DateTime.Now是衡量函数性能的最佳方式吗?

493

我需要找到瓶颈,并尽可能准确地测量时间。

以下代码片段是否是衡量性能的最佳方式?

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);

顺便说一下,如果您不想要快速而肮脏的东西,可以使用性能计数器。 - Jonathan C Dickinson
1
如果您需要更高的精度,请使用Stopwatch.GetTimestamp,否则答案是正确的。 - dbasnett
@dbasnett,您能否在回答中提供更多详细信息? - David Basarab
参见:反对使用DateTime.Now的理由 - Matt Johnson-Pint
2
“Best” 是主观的。这个问题需要以客观的方式定义“最好”的含义。 - Heretic Monkey
显示剩余2条评论
16个回答

673
不,不是这样。使用Stopwatch(在System.Diagnostics中)。
Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

秒表会自动检查高精度计时器的存在。

值得一提的是,由于需要处理时区DST等问题,DateTime.Now通常比DateTime.UtcNow慢得多。

DateTime.UtcNow通常具有15毫秒的分辨率。请参阅John Chapman的博客文章,了解DateTime.Now精度的概述。

有趣的小知识:如果您的硬件不支持高频计数器,则秒表会退回到DateTime.UtcNow。您可以查看静态字段Stopwatch.IsHighResolution,以查看秒表是否使用硬件实现高精度。


3
在“启动计时器”之前,我会先加上一个PerformWork()函数。 - DiVan
2
必须添加建议,如果您的 PerformWork() 很短,那么您可以重复调用它并计算一批调用的平均值。此外,为了避免闪烁效应影响计时测量结果,建议对整个调用批次进行计时,而不是启动/停止 Stopwatch - devgeezer
1
秒表在多核上不是线程安全的。请参见https://dev59.com/w2w15IYBdhLWcg3wWqf-和https://dev59.com/f0jSa4cB1Zd3GeqPF34K。 - Pavel Savara
1
sw.ElapsedMilliseconds; 可以。 - Flappy
2
@Pavel,明确一点,微软推荐使用Stopwatch作为现代多核处理器上运行Windows 7和Windows 8的最佳解决方案(低开销和高精度)。https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx - Ron
显示剩余3条评论

95

如果您想要快速获得较高精度的结果,我建议使用 Stopwatch。

Stopwatch sw = new Stopwatch();
sw.Start();
// Do Work
sw.Stop();

Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds);

如果你需要更加高级的工具,可以考虑使用第三方分析器,例如ANTS


60

这篇文章指出首先需要比较三种替代方案,分别是 StopwatchDateTime.NowDateTime.UtcNow

文章还展示了在某些情况下(当性能计数器不存在时),Stopwatch 会使用 DateTime.UtcNow + 一些额外处理。因此,在这种情况下,显然 DateTime.UtcNow 是最佳选择(因为其他选项都使用它 + 一些处理)。

然而,事实证明,计数器几乎总是存在的 - 参见与.NET Stopwatch有关的高分辨率性能计数器及其存在的说明?

这是一个性能图表。请注意 UtcNow 相对于其他替代方案具有多低的性能成本:

Enter image description here

X轴是样本数据大小,Y轴是示例的相对时间。

Stopwatch 更擅长提供更高分辨率的时间测量。另一个是它更面向对象的特性。不过,创建一个围绕 UtcNow 的面向对象的包装器并不难。


2
第一个链接似乎已经失效了。 - Peter Mortensen
1
变得破损了,是的...时间机器可以显示出来,我猜。顺便问一下,你为什么要编辑“the three”,我认为这里不需要“the”。 - Valentin Kuzub
注意给未来读者:这些数字似乎不再准确,因为Tono Nam进行了另一项测试(稍作调整,因为原始测试在撰写时存在缺陷),显示Stopwatch是获胜者,尽管优势微弱:https://dotnetfiddle.net/dSg3bN - undefined

19

将您的基准测试代码推入实用程序类/方法非常有用。 StopWatch 类不需要在出现错误时DisposedStopped。 因此,计时一些操作的最简单代码是:

public partial class With
{
    public static long Benchmark(Action action)
    {
        var stopwatch = Stopwatch.StartNew();
        action();
        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }
}

示例调用代码

public void Execute(Action action)
{
    var time = With.Benchmark(action);
    log.DebugFormat(“Did action in {0} ms.”, time);
}

以下是扩展方法版本

public static class Extensions
{
    public static long Benchmark(this Action action)
    {
        return With.Benchmark(action);
    }
}

并提供示例调用代码

public void Execute(Action action)
{
    var time = action.Benchmark()
    log.DebugFormat(“Did action in {0} ms.”, time);
}

1
更好的粒度怎么样?许多事情发生在不到一毫秒的时间内。 - Henrik
返回Elapsed属性,它是一个TimeSpan。我只是向您展示模式。享受实现它的乐趣。 - Anthony Mastrean
返回Elapsed.TotalMilliseconds以获得更高的精度。也可以参考这个问题:https://dev59.com/eWox5IYBdhLWcg3w6odg - nawfal

18

秒表功能可以更好(更高的精度)。不过我建议您只需下载其中一款流行的分析工具(我最常用的是DotTraceANTS... DotTrace的免费试用版是完全功能齐备的,没有像其他一些工具那样烦人的提示)。


14

使用System.Diagnostics.Stopwatch类。

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

// Do some code.

sw.Stop();

// sw.ElapsedMilliseconds = the time your "do some code" took.

11

Ditto Stopwatch非常优秀。

关于性能测试,你也应该检查一下你的“//某个执行过程”是否非常短。

还要记住,“//某个执行过程”的第一次运行可能比后续运行慢得多。

通常我会在循环中运行方法1000次或者1000000次,这样可以获得比仅运行一次更准确的数据。


9
这些都是衡量时间的好方法,但这只是一种非常间接的找到瓶颈的方式。在线程中找到瓶颈最直接的方法是让它运行,在它执行让你等待的操作时,使用暂停或断点键将其停止。多次执行此操作。如果您的瓶颈占用了X%的时间,那么在每个快照中捕获它的概率就是X%。这里有一个更完整的解释,说明了如何以及为什么它起作用

7

这是正确的方式:

using System;
using System.Diagnostics;

class Program
{
    public static void Main()
    {
        Stopwatch stopWatch = Stopwatch.StartNew();

            // some other code

        stopWatch.Stop();

        // this not correct to get full timer resolution
        Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds);

        // Correct way to get accurate high precision timing
        Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds);
    }
}

如果您想了解更多信息,请查看使用秒表替代DataTime以获得准确的性能计数器


7

@Sean Chambers

顺便提一下,.NET Timer类不是用于诊断的,它会在预设的时间间隔内生成事件,就像这样(来自MSDN):

System.Timers.Timer aTimer;
public static void Main()
{
    // Create a timer with a ten second interval.
    aTimer = new System.Timers.Timer(10000);

    // Hook up the Elapsed event for the timer.
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

    // Set the Interval to 2 seconds (2000 milliseconds).
    aTimer.Interval = 2000;
    aTimer.Enabled = true;

    Console.WriteLine("Press the Enter key to exit the program.");
    Console.ReadLine();
}

// Specify what you want to happen when the Elapsed event is 
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}

所以这并不能帮助你知道某件事情花费了多长时间,只是告诉你经过了一定的时间。

计时器也作为一个控件暴露在System.Windows.Forms中...你可以在VS05/VS08的设计器工具箱中找到它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接