DateTime.Now 是衡量函数性能的最佳方法吗?[已结束]

Is DateTime.Now the best way to measure a function's performance? [closed]

提问人:David Basarab 提问时间:8/27/2008 最后编辑:Peter MortensenDavid Basarab 更新时间:10/22/2020 访问量:75101

问:


想改进这个问题吗?更新问题,以便可以通过编辑这篇文章来用事实和引文来回答。

3年前关闭。

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

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

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
C# .NET 性能 日期时间 计时器

评论

1赞 dbasnett 3/7/2011
如果您需要更高的精度,请使用 Stopwatch.GetTimestamp,否则答案很好。
0赞 David Basarab 3/8/2011
@dbasnett 你能在答案中更详细地介绍一下吗?
0赞 dbasnett 3/8/2011
在上面的示例中,将 start 和 endtime 更改为 long,并将 Stopwatch.GetTimestamp 分配给它们,而不是 DateTime.Now。所用时间为 (end-start)/Stopwatch.Frequency。
0赞 Jonathan C Dickinson 10/8/2008
顺便说一句,如果您不是在寻找快速而肮脏的东西,则可以使用性能计数器。
2赞 Heretic Monkey 10/22/2020
“最好”是主观的。这个问题需要以客观的方式定义“最佳”的含义。

答:

18赞 jsight 8/27/2008 #1

秒表功能会更好(精度更高)。不过,我还建议只下载一个流行的分析器(DotTraceANTS 是我使用最多的......DotTrace 的免费试用版功能齐全,不像其他一些那样唠叨)。

673赞 Markus Olsson 8/27/2008 #2

不,不是。使用秒表(在System.Diagnostics)

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

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

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

值得一提的是,由于必须使用时区、DST 等完成的工作,通常比要慢得多。DateTime.NowDateTime.UtcNow

DateTime.UtcNow 的分辨率通常为 15 毫秒。请参阅 John Chapman 关于精度的博客文章,了解精彩的总结。DateTime.Now

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

评论

3赞 DiVan 6/23/2011
我会放置一个 PerformWork();在秒表之前“加热”。
2赞 devgeezer 4/11/2012
还必须添加建议,如果您很短,您可以重复调用它并计算该批调用的平均值。此外,对整批呼叫进行计时,而不是开始/停止通话,以避免频闪效应,从而使您的定时测量变得混乱。PerformWork()Stopwatch
1赞 Pavel Savara 7/26/2012
秒表在多核上不是线程安全的。请参阅 stackoverflow.com/questions/6664538/...stackoverflow.com/questions/1149485/...
1赞 Flappy 5/6/2014
西 南部。Elapsed毫秒;也可以
2赞 Ron 6/11/2015
@Pavel,需要明确的是,Microsoft推荐 Stopwatch 作为运行 Windows 7 和 Windows 8 的现代多核处理器的最佳解决方案(低开销和高精度)。msdn.microsoft.com/en-us/library/windows/desktop/......
14赞 rp. 8/27/2008 #3

使用 System.Diagnostics.Stopwatch 类。

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

// Do some code.

sw.Stop();

// sw.ElapsedMilliseconds = the time your "do some code" took.
4赞 Rob Cooper 8/27/2008 #4

我很少做这种性能检查(我倾向于认为“这很慢,让它更快”),所以我几乎总是这样做。

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

许多人提到使用 pinvoke 来获取性能信息。我研究的很多材料都只真正提到使用 perfmon..

编辑:

看过 StopWatch 的谈话..好!我学到了一些东西:)

这看起来像一篇好文章

95赞 mmcdole 8/27/2008 #5

如果你想要一些快速和肮脏的东西,我建议你改用秒表来获得更高的精度。

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

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

或者,如果你需要一些更复杂的东西,你可能应该考虑使用第三方分析器,如ANTS。

7赞 Adam Haile 8/27/2008 #6

@肖恩·钱伯斯

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

11赞 Andrei Rînea 8/30/2008 #7

同上秒表,它更好。

关于性能衡量,你还应该检查你的“//一些执行过程”是否是一个非常短的过程。

还要记住,“// Some Execution Process”的第一次运行可能比后续运行慢得多。

我通常通过在循环中运行 1000 次或 10000000 次来测试方法,并且获得的数据比运行一次要准确得多。

19赞 Anthony Mastrean 9/2/2008 #8

将基准测试代码推送到实用程序类/方法中非常有用。该类不需要 或 on 错误。因此,为某些操作计时的最简单代码是StopWatchDisposedStopped

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 2/18/2011
更好的粒度呢?许多事情在不到一毫秒的时间内发生。
0赞 Anthony Mastrean 2/18/2011
然后返回 Elapsed 属性,它是一个 TimeSpan。我只是向你展示模式。享受实现它的乐趣。
0赞 nawfal 4/12/2013
返回以获得更高的精度。也看到这个问题 stackoverflow.com/questions/8894425/...Elapsed.TotalMilliseconds
5赞 OwenP 10/9/2008 #9

我刚刚在 Vance Morrison 的博客上发现了一篇关于他编写的 CodeTimer 类的帖子,该类使使用更容易,并做了一些整洁的事情。StopWatch

6赞 Iain 10/25/2008 #10

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

Microsoft 的模式和实践组在 Visual Studio Team System 性能测试指南中提供了一些指导。

9赞 Mike Dunlavey 1/28/2009 #11

这些都是测量时间的好方法,但这只是找到瓶颈的一种非常间接的方法。

在线程中找到 bottneck 的最直接方法是让它运行,当它正在做任何让你等待的事情时,用暂停或中断键停止它。这样做几次。如果您的瓶颈花费了 X% 的时间,则 X% 是您在每个快照的操作中捕获它的概率。

以下是对它如何以及为什么工作的更完整的解释

7赞 jiya jain 3/7/2011 #12

这是正确的方法:

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

60赞 Valentin Kuzub 8/9/2011 #13

本文说,首先您需要比较三个备选方案,、 和 。StopwatchDateTime.NowDateTime.UtcNow

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

但是,事实证明,计数器几乎始终存在 - 请参阅有关高分辨率性能计数器及其与 .NET 秒表相关的存在的解释?

下面是一个性能图。请注意,与替代品相比,UtcNow 的性能成本有多低:

Enter image description here

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

更好的一点是它提供了更高分辨率的时间测量。另一个是它更OO的性质。但是,创建一个 OO 包装器并不难。StopwatchUtcNow

评论

2赞 Peter Mortensen 3/23/2013
第一个链接似乎已断开。
1赞 Valentin Kuzub 3/23/2013
变得破碎了,是的。我猜时间机器可以显示它。顺便说一句,你为什么要编辑“三个”,我相信这里不需要。
0赞 Visual Vincent 9/15/2023
给未来读者的注意:这些数字似乎不再准确,因为Tono Nam的另一项测试(稍作调整,因为原始测试在撰写本文时存在缺陷)显示是赢家,尽管略有优势:dotnetfiddle.net/dSg3bNStopwatch
5赞 Blackvault 7/18/2014 #14

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

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


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
4赞 Bye StackOverflow 9/15/2014 #15

这还不够专业:

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。收集调用以将托管堆更改为已知状态,并添加睡眠调用,以便在 ETW 配置文件中轻松分离不同的代码间隔。

-1赞 Tono Nam 10/22/2020 #16

由于我不太关心精度,所以我最终比较了它们。我在网络上捕获了大量数据包,我想放置接收每个数据包的时间。下面是测试 500 万次迭代的代码

    int iterations = 5000000;

    // Test using datetime.now
    {
        var date = DateTime.UtcNow.AddHours(DateTime.UtcNow.Second);

        var now = DateTime.UtcNow;

        for (int i = 0; i < iterations; i++)
        {
            if (date == DateTime.Now)
                Console.WriteLine("it is!");
        }
        Console.WriteLine($"Done executing {iterations} iterations using datetime.now. It took {(DateTime.UtcNow - now).TotalSeconds} seconds");
    }

    // Test using datetime.utcnow
    {
        var date = DateTime.UtcNow.AddHours(DateTime.UtcNow.Second);

        var now = DateTime.UtcNow;

        for (int i = 0; i < iterations; i++)
        {
            if (date == DateTime.UtcNow)
                Console.WriteLine("it is!");
        }
        Console.WriteLine($"Done executing {iterations} iterations using datetime.utcnow. It took {(DateTime.UtcNow - now).TotalSeconds} seconds");
    }

    // Test using stopwatch
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();

        var now = DateTime.UtcNow;

        for (int i = 0; i < iterations; i++)
        {
            if (sw.ElapsedTicks == DateTime.Now.Ticks)
                Console.WriteLine("it is!");
        }
        Console.WriteLine($"Done executing {iterations} iterations using stopwatch. It took {(DateTime.UtcNow - now).TotalSeconds} seconds");
    }

输出为:

Done executing 5000000 iterations using datetime.now. It took 0.8685502 seconds 
Done executing 5000000 iterations using datetime.utcnow. It took 0.1074324 seconds 
Done executing 5000000 iterations using stopwatch. It took 0.9625021 seconds

因此,总而言之,如果您不太关心精度,DateTime.UtcNow 是最快的。这也支持了这个问题 https://stackoverflow.com/a/6986472/637142 答案。

评论

0赞 Visual Vincent 9/15/2023
这是一个很好的测试。不幸的是,在目前的状态下,它是有缺陷的,这使得它看起来比实际执行需要更多的时间。在秒表测试中,您正在执行一个额外的日期时间操作 (),该操作不仅必须从系统时钟获取当前时间,而且在此之前还要将 datetime 对象转换为当前时区,因为您使用的是 .因此,您实际上是在测试执行时间,而不是单独测试。Stopwatchsw.ElapsedTicks == DateTime.Now.TicksDateTime.NowDateTime.UtcNowDateTime.Now + StopwatchStopwach
0赞 Visual Vincent 9/15/2023
我投了反对票是为了提高对潜在错误信息的认识,但如果这种情况得到纠正,我很乐意投赞成票。为了使测试更正确,您应该将 before 的值存储在循环之前,这实际上在我的计算机上的 .NET 6 和 .NET Fiddle 上运行时速度最快: dotnetfiddle.net/dSg3bNDateTime.(Utc)Now.TicksStopwatch