使用计时器对.NET应用程序进行性能分析

11

看起来没有免费的.NET性能分析工具可以逐行分析性能。因此,我正在考虑使用Stopwatch进行性能分析。

* 免费是指自由,即许可证包括商业应用程序。

编辑:针对那些告诉我“购买分析器”的人,我想这样做,但如果我可以花那么多钱,我会把它花在其他事情上。我试图说服老板一个分析器是值得的,但运气不太好。这个问题主要基于好奇心。我永远不会考虑用Stopwatch替代真正的分析工具。

我有一个小的测试应用程序(用C#编写),用于基于每行使用Stopwatch测量性能差异。测试代码如下:

int n = 100;
BigInteger f = 1;
for (int i = n; i > 1; i--)
{
    f *= i;
}

以下是完整代码:http://pastebin.com/AvbQmT32

我为每行代码设置了一个计时器,这是我的“分析器”。我还有一个计时器用于整个程序,这是我的“分析器分析器”。

我将程序配置为发布模式、任意CPU(在x64机器上),并禁用优化。

当我禁用分析器运行程序时,会得到类似以下的结果:

             Line             |  Ticks
------------------------------|----------
                              |
Total time:                   |       359

当我启用分析器运行它时,我会得到类似这样的结果:

             Line             |  Ticks
------------------------------|----------
                              |
int n = 100;                  |         3
BigInteger f = 1;             |        12
for (int i = n; i > 1; i--)   |       325
{                             |
    f *= i;                   |       539
}                             |
                              |
Total time:                   |      1710
Stopwatch overhead:           |       831
理想情况下,代码的耗时应该在两种情况下相等,但似乎Stopwatch的开销(overhead)会在它们自己的经过时间中出现。
现在,需要对程序的每一行进行性能分析通常没有意义,因为采用分而治之的方法通常更有效。您可以通常从对代码块进行性能分析开始,并缩小任何性能问题的范围。
此外,在大多数应用程序中,平均代码行的速度要慢得多比测试程序中的代码要慢。这意味着 Stopwatch 的开销将会更少。
然而,使用 Stopwatches 仍然会有开销,特别是如果您使用了很多个。
那么到了问题的核心:
使用 Stopwatches 进行性能分析最有效的方法是什么?如何将开销最小化?将 Stopwatch 包装在单个语句周围是否值得?
感谢您的反馈。

6
就我个人而言,我购买的性能分析器(ANTS Profiler)每一分钱都物有所值。我再也不会考虑手动分析代码性能了。 - Uwe Keim
1
抱歉打扰,但为什么不考虑使用一些System.Diagnostics类呢?我相信它包含了一些检查CPU和内存使用情况的类。 - Eon
1
Eqatec性能分析器的免费版本允许在商业项目中使用:http://www.eqatec.com/Profiler/LicenseTerms.aspx - ken2k
@ken2k: 它是否逐行进行分析性能? - Kendall Frey
7个回答

4
考虑到我们通常根据单一职责原则直觉地进行编程,不仅涉及类型,还涉及函数,我认为逐行对应用程序进行分析没有任何意义。你会更关心“单一职责”而不是每一行代码。
当然,有时候你也需要信息。但不要在整个应用程序中使用它,只在函数的某个部分或单个函数中使用。在这种情况下,StopWatch 是最好的选择。请注意,StopWatch 是 .NET 类,因此具有最小的开销。你不应该看绝对值,而应该看相对值。
希望这可以帮助你。

2

你是否尝试过查看CInject:http://codeinject.codeplex.com/documentation?它基本上是将代码注入到DLL中。开箱即用,它似乎具有性能监控功能,但它还允许您创建自己的要注入的代码。对于您正在尝试做的事情来说,似乎非常完美。


2
首先,你的结果并不令人惊讶。如果你使用商业分析工具,你会看到类似的情况:在对程序进行分析时,它的运行时间会比不进行分析时长得多。你设置的分析粒度越细,所需时间就越长。考虑到像“i>1”和“i--”这样的语句可能会被执行为单个处理器指令,因此很明显分析特定代码行的执行时间比执行该行本身要花费更长的时间。
分析增加了程序的总运行时间并不应成为问题;正如其他人提到的那样,重要的是不是程序运行的绝对时间,而是将各个部分的运行时间相互比较以找出瓶颈。但还有另一个问题。如果操作系统提供了高频计时器,则 Stopwatch 将使用其进行计时;但即使如此,它的频率也可能不够高。在我的 Windows 7 64 位 i5-2400(四核 3.10 GHz)上,高频计时器每秒滴答 3,020,556 次。听起来很多;但以这个速率,处理器可以在两次滴答之间执行一千条指令。这意味着如果你试图测量执行单个指令所需的时间,你要么会远远低估,要么会远远高估。
你最好在方法级别进行分析。即使这样,你仍然会遇到频率问题,特别是如果你有小的、良好的方法。但结果将比在行级别更可靠;一旦你确定了导致瓶颈的方法,你就可以直接检查它以确定如何优化它以提高性能。
所有这些都忽略了与性能分析一般相关的许多警告,这超出了本文的范围。确保你在该主题上进行额外的研究,以了解如何解释任何结果。例如,你的分析可能会显示程序中大部分时间都花在一个特定的方法上;但这是否意味着该方法本身存在问题,还是其他方法调用它太频繁?回答这类问题是分析真正困难的地方。

2

你不应该只看具体的数字,而应该关注相对差异。这样你才能找到问题所在,并寻找需要优化的地方来解决性能问题。

如果你正在开发需要考虑性能的代码,并且需要进行性能分析以找到问题所在,那么购买一个工具来帮助你完成这项任务将会节省大量时间并且物有所值。我建议使用Visual Studio Premium附带的分析器。


1

你有比较过没有使用性能分析器的总时间和在调试模式下使用性能分析器的时间吗?我的第一个猜测是,额外的时间出现是因为秒表的额外语句阻止了一些寄存器或循环优化。因此,额外的时间可能来自于缺少优化,而不是秒表内部的时间损失。

然而,话虽如此,即使秒表具有高分辨率,我认为您也会注意到测量像一行代码这样小的东西时会出现一些时间误差。例如,“int n = 100”在汇编中只是一行代码,因此在技术术语中,我认为它只有1个滴答声。您从运行到运行中注意到的数字变化有多大?如果您得到的变化是平均值的显着百分比,那么该值实际上并没有给您太多信息。


优化已禁用,因此不会成为问题。在这两种情况下,我有时会看到约平均值的1/3的变化。 - Kendall Frey
禁用优化后,切换到调试模式可能不会揭示问题,但仍有可能减速是由于秒表调用的额外开销引起的。秒表调用在内部使用多个变量,这些变量可能会强制您的主代码执行诸如在秒表调用之后重新加载寄存器等操作。 - Sogger
哦,关于1/3的变化,这取决于你试图为什么确定数字,但如此大的变化意味着该数字仅适用于数量级比较(就其他回答所说而言,仅适用于相对比较)。想象一下一个身高6英尺的人使用1/3的误差范围进行测量,“哦,你的身高在4到8英尺之间。”挺重要的。 - Sogger
然而,性能分析通常涉及“数量级比较”。多花1秒并没有什么意义,但是多花两倍的时间就有了。 - Kendall Frey
1
我完全同意。就性能分析而言,这几乎总是如此,除了一些需要满足特定时间要求的实时应用程序。我对变化的评论只是试图回答你的问题:“在一个语句周围包装一个秒表是否值得?”答案是如果你的误差范围非常小,那么是的。如果不是,那就取决于你想用这个值做什么。有时候,这个值甚至不够准确,无法进行相对比较。 - Sogger

0

0
也许你应该考虑以方法调用时间为基准,而不是单个代码行。毕竟,在调用代码中,方法调用通常只占据一行。另外一个有趣的方法是使用日志库记录方法的进入和退出,例如这样(使用log4net作为首选的记录器):
public string MyMethod(string arg)
{
    string result = null;

    if (logger.IsDebugEnabled) logger.Debug("->MyMethod(" + arg + ")");

    // do stuff
    result = "Hello world!";

    if (logger.IsDebugEnabled) logger.Debug("<-MyMethod(" + arg + ") = " + result);

    return result;
}

这将创建一个日志文件,非常有用,可以查看应用程序正在执行的操作,并且每行写入日志都有时间戳,因此您可以看到特定方法是否需要很长时间。使用log4net,您可以轻松更改配置以关闭日志记录,因此当您不需要它时,您不会付出任何性能损失。


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