调试与发布性能


132

我遇到了以下段落:

“在Visual Studio中编译代码时,IDE中的“调试与发布”设置几乎对性能没有影响……生成的代码几乎相同。C#编译器实际上并没有做任何优化。C#编译器仅吐出IL…,而在运行时由JITer进行所有优化。JITer确实具有调试/发布模式,这对性能产生了巨大的影响。但这并不能阻止您运行项目的“调试”或“发布”配置,也无法阻止是否连接了调试器。”

来源在这里,播客在这里

有人可以指导我阅读可以实际证明这一点的Microsoft文章吗?

谷歌搜索“ C#调试与发布性能 ”通常会返回结果,说“ 调试具有很大的性能优势 ”,“ 发布已优化 ”和“ 不将调试部署到生产环境 ”。



在Win7-x86上使用.Net4时,我编写了一个受CPU限制的程序,该程序在发布时的运行速度比在主循环中没有assert / etc的调试速度快2倍。
Bengie

另外,如果您关心内存的使用,可能会有很大的差异。我已经看到了一种情况,在调试模式下编译的多线程Windows服务每个线程使用700MB,而在Release版本中每个线程使用50MB。在典型的使用情况下,调试版本会很快用完内存。
o。nate 2014年

@Bengie-您是否已验证是否将调试器附加到发行版,其运行速度仍快2倍?请注意,上面的引言说JIT优化受调试器是否附加的影响。
制造商史蒂夫

Answers:


99

部分正确。在调试模式下,编译器将为所有变量发出调试符号,并按原样编译代码。在发布模式下,包括一些优化:

  • 未使用的变量根本不会编译
  • 如果某些循环变量被证明是不变的,则它们会被编译器带出循环
  • 不包含根据#debug指令编写的代码,等等。

其余的取决于JIT。

优化的完整列表在这里Eric Lippert提供


10
并且不要忘记Debug.Asserts!在DEBUG构建中,如果失败,它们将停止线程并弹出一个消息框。在发行时,它们根本不会编译。这适用于所有具有[ConditionalAttribute]的方法。
伊万·兹拉塔诺夫

13
C#编译器不执行尾部调用优化;抖动。如果你想的时候优化开关在C#编译器做什么准确的列表,请参阅blogs.msdn.com/ericlippert/archive/2009/06/11/...
埃里克利珀

63

没有文章可以“证明”有关性能问题的任何内容。证明更改对性能的影响的方法是尝试两种方法,并在实际但受控的条件下进行测试。

您是在问有关性能的问题,所以很显然您关心性能。如果您关心性能,那么正确的做法是设置一些性能目标,然后为自己编写一个测试套件,以针对这些目标跟踪您的进度。一旦有了这样的测试套件,就可以轻松地使用它来为自己测试语句的真实性或虚假性,例如“调试构建速度较慢”。

而且,您将能够获得有意义的结果。“慢”是没有意义的,因为不清楚是慢一微秒还是慢二十分钟。“在现实情况下慢10%”更有意义。

花时间将您的时间花在在线研究此问题上,以构建可回答该问题的设备。这样,您将获得更加准确的结果。您在网上阅读的所有内容仅是对可能发生的猜测。原因是您收集到的事实,而不是其他人对程序的行为方式的猜测。


2
我认为您可以关心性能,但仍然希望使用“调试”。例如,如果您大部分时间都在等待依赖关系,那么我认为以调试模式进行构建不会产生太大的变化,但是您还有在堆栈跟踪中获取行号的额外好处,这可能有助于更快地修复错误并更快乐的用户。关键是您必须权衡利弊,一般的“在调试中运行较慢,但仅在受CPU限制的情况下”这样的语句足以帮助您做出决定。
Josh Mouch

11

我无法评论性能,但是“不要将调试部署到生产中”的建议仍然成立,因为调试代码通常在大型产品中做很多不同的事情。一方面,您可能使调试开关处于活动状态,另一方面,可能会有其他冗余完整性检查和调试输出,这些输出不属于生产代码。


我在这个问题上同意您的意见,但这不能回答主要问题
sagie

5
@sagie:是的,我知道这一点,但我认为这一点仍然值得提出。
康拉德·鲁道夫

6

来自msdn social

它没有很好的记录,这就是我所知道的。编译器发出System.Diagnostics.DebuggableAttribute的实例。在调试版本中,IsJitOptimizerEnabled属性为True,在发行版本中为False。您可以使用ildasm.exe在程序集清单中看到此属性

JIT编译器使用此属性来禁用优化,这会使调试变得困难。像循环不变的提升那样移动代码的代码。在某些情况下,这可能会在性能上产生很大差异。虽然不是通常。

将断点映射到执行地址是调试器的工作。它使用.pdb文件和JIT编译器生成的信息,该编译器提供了IL指令到代码地址的映射。如果要编写自己的调试器,则可以使用ICorDebugCode :: GetILToNativeMapping()。

基本上,由于禁用了JIT编译器优化,因此调试部署将变慢。


3

您所读的内容非常有效。由于JIT优化,发行版通常更精简,不包括调试代码(#IF DEBUG或[Conditional(“ DEBUG”)]),最小的调试符号加载,并且通常不考虑使用较小的组件,这将减少加载时间。由于加载了更广泛的PDB和符号,因此在VS中运行代码时,性能差异更加明显,但如果独立运行,性能差异可能会不太明显。某些代码将比其他代码更好地进行优化,并且与其他语言一样,它们将使用相同的优化启发式方法。

Scott在这里对内联方法优化有很好的解释

请参阅本文,其中简要说明了为什么它在ASP.NET环境中的调试和发布设置不同。


3

关于性能以及是否连接调试器,您应该注意的一件事使我们感到惊讶。

我们有一段代码,涉及许多紧密的循环,似乎要花很多时间进行调试,但可以很好地运行。换句话说,没有客户或客户遇到问题,但是当我们进行调试时,它似乎像糖蜜一样运转。

罪魁祸首是Debug.WriteLine紧密循环中的一个,它吐出了数千条日志消息,这些消息是前一段时间在调试会话中留下的。看来,当连接调试器并侦听此类输出时,会涉及使程序运行缓慢的开销。对于这个特定的代码,它的运行时间约为0.2-0.3秒,而连接调试器则为30+秒。

不过,简单的解决方案就是删除不再需要的调试消息。


2

msdn网站...

发布与调试配置

在您仍在处理项目时,通常将使用调试配置来构建应用程序,因为此配置使您可以查看变量的值并控制调试器中的执行。您还可以在发布配置中创建和测试内部版本,以确保没有引入仅在一种类型的内部版本或其他类型的内部版本上显露的错误。在.NET Framework编程中,此类错误很少见,但可能会发生。

当您准备将应用程序分发给最终用户时,请创建一个发行版本,该版本要小得多,并且通常比相应的调试配置具有更好的性能。您可以在项目设计器的“生成”窗格或“生成”工具栏中设置生成配置。有关更多信息,请参见构建配置。


1

在很大程度上,这取决于您的应用程序是否受计算限制,并且像Lasse的示例一样,这并不总是那么容易分辨。如果我对它的工作有任何疑问,我将其暂停几次并检查堆栈。如果发生了我真正不需要的其他事情,那将立即发现它。


1

我最近遇到了性能问题。产品完整列表花费了太多时间,大约80秒。我调整了数据库,改善了查询,没有任何区别。我决定创建一个TestProject,发现在4秒钟内执行了相同的过程。然后我意识到项目处于Debug模式,而测试项目处于Release模式。我将主项目切换为发布模式,产品完整列表仅用4秒钟即可显示所有结果。

简介:调试模式比运行模式要慢得多,因为它保留了调试信息。您应该始终以发布模式进行部署。如果包含.PDB文件,您仍然可以拥有调试信息。这样,您可以记录带有行号的错误,例如。


“运行模式”是指“发布”吗?
罗恩·克莱因

对,就是这样。发行版没有所有调试开销。
Francisco Goldenstein 2012年

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.