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


474

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

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

DateTime startTime = DateTime.Now;

// Some execution process

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

顺便说一句,如果您不想要什么,可以使用快速而肮脏的性能计数器。
乔纳森·C·迪金森

1
如果需要更高的精度,请使用Stopwatch.GetTimestamp,否则答案很好。
dbasnett 2011年

@dbasnett您能否在答案中更详细地介绍?
David Basarab

在上面的示例中,将start和endtime更改为long,然后将Stopwatch.GetTimestamp分配给它们,而不是DateTime.Now。花费的时间是(结束开始)/Stopwatch.Frequency。
dbasnett 2011年

Answers:


649

不,这不对。使用秒表(在中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来检查Stopwatch是否使用硬件来实现高精度。


3
我要放置一个PerformWork(); 在秒表之前“加热”。
DiVan 2011年

2
还必须添加建议,如果您PerformWork()的电话很短,则可以重复调用它并计算一批呼叫的平均值。此外,请对整个呼叫进行计时,而不要开始/停止您的通话,Stopwatch以避免产生频闪效应,这种频闪效应会使您的计时测量变得混乱。
devgeezer 2012年


1
sw。经过的毫秒;也可以
Flappy

2
@Pavel,要明确,秒表是由微软推荐为在运行Windows 7和Windows 8的现代多核处理器的最佳解决方案(低开销,精度高)msdn.microsoft.com/en-us/library/windows/桌面/…
罗恩(Ron)

91

如果您想要快速又脏的东西,建议您改用秒表,以获得更高的精度。

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

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

另外,如果您需要一些更高级的工具,则可能应该考虑使用诸如ANTS之类的第三方分析器。


56

这篇文章说,首先你需要比较三个选择,StopwatchDateTime.NowDateTime.UtcNow

它还显示在某些情况下(不存在性能计数器时),秒表正在使用DateTime.UtcNow +一些额外的处理。因此,在这种情况下,显然DateTime.UtcNow是最好的选择(因为其他人使用它+一些处理)

但是,事实证明,该计数器几乎总是存在-请参见有关高分辨率性能计数器及其与.NET Stopwatch相关的解释?

这是一个性能图。请注意,与替代产品相比,UtcNow具有较低的性能成本:

在此处输入图片说明

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

Stopwatch更好的一件事是它提供了更高分辨率的时间测量。另一个是其更面向对象的性质。但是,围绕创建OO包装器UtcNow并不难。


第一个链接似乎已断开。
Peter Mortensen

1
是的坏了..时间机器可以显示它,我猜。顺便说一句,为什么您要编辑“三个”,我相信这里并不需要。
Valentin Kuzub

18

将基准测试代码放入实用程序类/方法中很有用。本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
更好的粒度呢?许多事情在不到一毫秒的时间内发生。
亨里克

然后返回Elapsed属性,它是一个TimeSpan。我只是向您展示模式。祝您实施愉快。
Anthony Mastrean 2011年

回报Elapsed.TotalMilliseconds更高的精度。看到这个问题太stackoverflow.com/questions/8894425/...
nawfal

16

秒表功能会更好(精度更高)。我也建议只下载其中一个最受欢迎的分析器(DotTraceANTS是我使用最多的分析器... DotTrace的免费试用版功能齐全,不像其他一些那样na。)


13

使用System.Diagnostics.Stopwatch类。

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

// Do some code.

sw.Stop();

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

11

同上秒表,那就更好了。

关于性能评估,您还应该检查“ //某些执行过程”是否是很短的过程。

还请记住,“ //某些执行过程”的第一次运行可能比随后的运行慢。

我通常通过在一个循环中运行1000次或1000000次来测试一种方法,与运行一次相比,我得到的数据要准确得多。


9

这些都是衡量时间的好方法,但这只是发现瓶颈的一种非常间接的方法。

在线程中查找瓶颈的最直接方法是使其运行,并且在执行任何使您等待的事情时,请使用暂停键或Break键将其暂停。这样做几次。如果瓶颈花费了X%的时间,则X%是您在每个快照的动作中捕获它的概率。

这是有关其运作方式和运作方式的更完整说明


7

@ 肖恩·钱伯斯

仅供参考,.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的设计器工具框中找到它


6

这是正确的方法:

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来获取准确的性能计数器


6

Visual Studio Team System具有一些有助于解决此问题的功能。本质上,您可以编写单元测试并将其混合在不同的场景中,以作为压力测试或负载测试的一部分针对您的软件运行。这可能有助于确定对应用程序性能影响最大的代码区域。

Microsoft的Patterns and Practices组在Visual Studio Team System性能测试指南中提供了一些指南


5

我刚刚在Vance Morrison的博客中找到有关他编写的CodeTimer类的文章,该类使使用StopWatch变得更容易,并且在侧面做了一些整洁的事情。


5

这还不够专业:

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

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

一个更可靠的版本是:

PerformWork();

int repeat = 1000;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repeat; i++)
{
   PerformWork();
}

sw.Stop();

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

在我的真实代码中,我将添加GC.Collect调用以将托管堆更改为已知状态,并添加Sleep调用,以便可以在ETW配置文件中轻松分隔不同的代码间隔。


4

我很少进行这种性能检查(我倾向于只是认为“这很慢,要使其更快”),所以我几乎总是这样做。

谷歌确实揭示了很多用于性能检查的资源/文章。

许多人提到使用pinvoke获取性能信息。我学习的许多材料只提到使用perfmon。

编辑:

看过秒表的话题。我学到了一些东西:)

看起来不错


4

我在程序中使用的方法是使用StopWatch类,如下所示。

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


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.