计算一个方法的执行时间

694

可能是重复问题:
我如何测量一个函数的运行时间?

我有一个需要花费I/O时间的方法,它会将数据从一个位置复制到另一个位置。计算执行时间的最佳和最真实的方法是什么? Thread? Timer? Stopwatch? 还有其他解决方案吗?我想要最精确的方法,并且尽可能简洁。

8个回答

1444

Stopwatch是专门为此目的设计的,是在.NET中测量时间执行的最佳方法之一。

var watch = System.Diagnostics.Stopwatch.StartNew();
// the code that you want to measure comes here
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;

在.NET中不要使用DateTime来测量时间执行。


更新:

正如评论区@series0ne所指出的那样:如果您想要对某些代码的执行进行真正精确的测量,您将需要使用操作系统内置的性能计数器。以下答案提供了一个很好的概述。


2
没错,那么这种情况怎么办呢:我有一个foreach循环,里面有一个ThreadPool.QueueUserWorkItem(delegate { CopyFiles(folder, dest); });,但是秒表在所有操作完成之前就停止了。 - Mahdi Tahsildari
17
秒表不完全准确?请记住,.NET背景噪音(例如JIT编译)会导致执行时间发生变化。因此,为了获得准确的测量结果,建议使用性能分析器。 - Matthew Layton
3
@series0ne,非常好的观点。我会更新我的答案,包括您宝贵的评论。 - Darin Dimitrov
8
最近我面试了一个非常有经验的高级开发者,他告诉我使用DateTime比使用StopWatch更加准确。他解释说,这是因为.NET中的StopWatch没有考虑CPU亲和性,所以如果你的线程从一个核心移动到另一个核心,StopWatch不会考虑执行时间跨越其他核心的情况,只会计算线程开始执行时所在核心的时间。对此,您有什么看法? - Matthew Layton
2
@series0ne,资深开发人员告诉你的有关计时器和日期时间的内容是绝对正确的。除了使用DateTime时精度不够之外。问题在于.NET中没有这样的类可以提供所有这些功能。 - Darin Dimitrov
显示剩余3条评论

94

从个人经验来看,System.Diagnostics.Stopwatch 类可用于测量方法的执行时间,但是请注意:它并不完全准确!

考虑以下示例:

Stopwatch sw;

for(int index = 0; index < 10; index++)
{
    sw = Stopwatch.StartNew();
    DoSomething();
    Console.WriteLine(sw.ElapsedMilliseconds);
}

sw.Stop();

示例结果

132ms
4ms
3ms
3ms
2ms
3ms
34ms
2ms
1ms
1ms

现在你可能会想:"为什么第一次需要132ms,而之后的时间明显更短?"

答案是, Stopwatch 在 .NET 中并不会对 "背景噪声" 活动进行补偿,例如 JITing。因此,在第一次运行方法时,.NET 首先要进行 JITing。执行此操作所需的时间将添加到执行时间中。同样,其他因素也会导致执行时间变化。

如果您真正想要绝对准确性,应该寻找 性能分析

看看下面的内容:

RedGate ANTS 性能分析器是一款商业产品,但可以产生非常精确的结果。 - 使用.NET分析性能来提升应用性能

这里有一个关于分析性能的 StackOverflow 文章:- 有哪些好的.NET分析器?

我还写了一篇关于使用 Stopwatch 进行性能分析的文章,您可能会感兴趣 - .NET 的性能分析


67
我不确定为什么这是“不准确”的例子。秒表准确地测量了第一个调用的总费用,这显然是对将要运行此代码的客户相关的内容。他们不在乎那132毫秒是被计算到方法执行还是抖动中,他们关心的是总经过时间。 - Eric Lippert
1
@EricLippert,你提出了一个很好的观点。诚然,如果代码只需要运行一次,那么JIT和其他“噪音”可能会对结果产生可信的影响。但是,OP(以及其他人)可能有其他场景。例如,代码可能在循环中执行,因此您可以计算所有迭代的平均值,或者需要最长/最短的执行时间。 - Matthew Layton
2
如果你关心循环的性能,那么就要测量循环的性能。不要简单地假设执行某个操作n次会比执行一次慢n倍。 - svick
4
首先,这段代码样本应该在循环中没有重置StopWatch时产生逐渐增加的数字输出(我进行了检查)。因此,你的输出结果与代码不匹配。其次,当我在循环中更正代码为Stop/Write/Reset/Start后,第一个测量结果与其余结果相同。我猜测你的DoSomething函数第一次运行时处理时间较长。JIT可能是原因,但Stopwatch不影响测量结果。因此评分为-1。 - ILIA BROUDNO
3
ILIA BROUDNO是完全正确的,我得到了相同的结果。@series0ne,在循环内部有一个Console.WriteLine语句,写入了到目前为止的毫秒数;由于只在循环结束后停止计时器,所以随着每次迭代时间累积,我们应该会看到类似于3、6、10、12、15...这样的结果。 - Rui Miguel Pinheiro
显示剩余4条评论

41

StopWatch类帮助你找到最佳解决方案。

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

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

此外,它还有一个名为Stopwatch.IsHighResolution的静态字段。当然,这是硬件和操作系统问题。

指示计时器是否基于高分辨率性能计数器。


21

绝对正确!我有点沮丧,因为这里有很多人建议使用秒表,但它并不完全准确。然而,正如你建议使用性能分析器,这才是正确的方法!+1 - Matthew Layton

13

根据这个微软文档

using System;
using System.Diagnostics;
using System.Threading;
class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
        Thread.Sleep(10000);
        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("RunTime " + elapsedTime);
    }
}

输出:

运行时间 00:00:09.94

7
您还可以使用这个函数,"获取自系统启动以来经过的毫秒数。"
System.Environment.TickCount

举个例子

static void method1()
{
    for (int i = 1; i <= 100; i++)
    {
        Console.WriteLine("Test1 " + i);
    }
}
static void Main(string[] args)
{
    var startTime = Environment.TickCount;
    method1();
    var endTime = Environment.TickCount;
    Console.WriteLine("RunTime " + (endTime - startTime));
}

7

StopWatch将使用高分辨率计数器

秒表通过计算底层计时器机制中的计时器滴答声来测量经过的时间。如果安装的硬件和操作系统支持高分辨率性能计数器,则Stopwatch类使用该计数器来测量经过的时间。否则,Stopwatch类使用系统计时器来测量经过的时间。使用Frequency和IsHighResolution字段来确定Stopwatch计时实现的精度和分辨率。

如果您正在测量IO,则您的数字可能会受到外部事件的影响,我不会太担心准确性(如上所述)。相反,我会进行一系列测量,并考虑这些数字的平均值和分布。


2
 using System.Diagnostics;
 class Program
 {
    static void Test1()
    {
        for (int i = 1; i <= 100; i++)
        {
            Console.WriteLine("Test1 " + i);
        }
    }
  static void Main(string[] args)
    {

        Stopwatch sw = new Stopwatch();
        sw.Start();
        Test1();
        sw.Stop();
        Console.WriteLine("Time Taken-->{0}",sw.ElapsedMilliseconds);
   }
 }

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