我必须承认,通常我不会在我的程序中的 Debug 和 Release 配置之间进行切换,而且我通常会选择 Debug 配置,即使程序实际部署在客户位置也是如此。
据我所知,如果您不手动更改,这些配置之间的唯一区别是 Debug 定义了 DEBUG
常量,并且 Release 勾选了优化代码。
所以我的问题实际上是双重的:
这两种配置之间是否存在很大的性能差异。是否有任何特定类型的代码会在这里导致性能上的巨大差异,或者它实际上并不那么重要?是否有任何类型的代码可以在 Debug 配置下正常运行而在 Release 配置下可能会失败,或者您是否可以确定在 Debug 配置下测试并正常工作的代码在 Release 配置下也可以正常工作。
C# 编译器本身不会在 Release 构建中大量更改发出的 IL。值得注意的是,它不再发出允许您在花括号上设置断点的 NOP 操作码。最重要的是 JIT 编译器中内置的优化器。我知道它进行了以下优化:
方法内联。方法调用被注入方法的代码所取代。这是一个很大的问题,它使属性访问器基本上是免费的。
CPU 寄存器分配。局部变量和方法参数可以保持存储在 CPU 寄存器中,而不会(或不经常)存储回堆栈帧。这是一个很大的问题,值得注意的是使调试优化代码变得如此困难。并赋予 volatile 关键字一个含义。
数组索引检查消除。使用数组时的一项重要优化(所有 .NET 集合类在内部都使用数组)。当 JIT 编译器可以验证循环永远不会越界索引数组时,它将消除索引检查。大的一个。
循环展开。通过在主体中重复代码多达 4 次并减少循环来改进具有小主体的循环。降低分支成本并改进处理器的超标量执行选项。
死代码消除。像 if (false) { /.../ } 这样的语句被完全消除了。这可能是由于不断的折叠和内联而发生的。其他情况是 JIT 编译器可以确定代码没有可能的副作用。这种优化使分析代码变得如此棘手。
代码提升。循环内不受循环影响的代码可以移出循环。 C 编译器的优化器将花费更多时间来寻找提升的机会。然而,由于需要进行数据流分析,这是一项昂贵的优化,而且抖动无法承受时间,因此只能提升明显的案例。迫使 .NET 程序员编写更好的源代码并提升自己。
常见的子表达式消除。 x = y + 4; z = y + 4;变成z = x;在 dest[ix+1] = src[ix+1]; 这样的语句中很常见为便于阅读而编写,没有引入辅助变量。无需牺牲可读性。
不断折叠。 x = 1 + 2;变为 x = 3;这个简单的示例被编译器提前捕获,但发生在 JIT 时间,此时其他优化使之成为可能。
复制传播。 x = 一个; y = x;变成 y = a;这有助于寄存器分配器做出更好的决定。在 x86 抖动中这是一个大问题,因为它几乎没有可以使用的寄存器。让它选择正确的对性能至关重要。
这些非常重要的优化可以产生很大的不同,例如,当您分析应用程序的调试版本并将其与发布版本进行比较时。只有当代码在您的关键路径上时,这才真正重要,您编写的 5% 到 10% 的代码实际上会影响程序的性能。 JIT 优化器不够聪明,无法预先知道什么是关键的,它只能对所有代码应用“将其转为 11”拨号。
这些优化对程序执行时间的有效结果通常会受到在其他地方运行的代码的影响。读取文件、执行 dbase 查询等。使 JIT 优化器所做的工作完全不可见。不过没关系:)
JIT 优化器是非常可靠的代码,主要是因为它已经过数百万次的测试。在您的程序的 Release 构建版本中出现问题的情况极为罕见。然而它确实发生了。 x64 和 x86 抖动都存在结构问题。 x86 抖动在浮点一致性方面存在问题,当浮点计算的中间值以 80 位精度保存在 FPU 寄存器中而不是在刷新到内存时被截断时,会产生细微的不同结果。
是的,存在许多性能差异,这些差异确实适用于您的代码。 Debug 做的性能优化很少,release 模式做的很多;只有依赖 DEBUG 常量的代码在发布版本中可能会以不同的方式执行。除此之外,您应该看不到任何问题。
依赖于 DEBUG
常量的框架代码示例是 Debug.Assert()
方法,它定义了属性 [Conditional("DEBUG)"]
。这意味着它还依赖于 DEBUG
常量,并且它不包含在发布版本中。
DEBUG
下运行速度慢 40%:AppDomain.CurrentDomain.GetAssemblies().Sum(p => p.GetTypes().Sum(p1 => p1.GetProperties().Length))
。
asp.net
上并使用调试而不是发布,则可能会在您的页面上添加一些脚本,例如:具有大约 7k 行的 MicrosoftAjax.debug.js
。
这在很大程度上取决于您的应用程序的性质。如果您的应用程序是 UI 密集型的,您可能不会注意到任何区别,因为连接到现代计算机的最慢组件是用户。如果您使用一些 UI 动画,您可能需要测试在 DEBUG 构建中运行时是否可以察觉到任何明显的延迟。
但是,如果您有许多计算量大的计算,那么您会注意到差异(可能高达@Pieter 提到的 40%,尽管这取决于计算的性质)。
这基本上是一种设计权衡。如果您在 DEBUG 构建下发布,那么如果用户遇到问题,您可以获得更有意义的回溯,并且可以进行更灵活的诊断。通过在 DEBUG 构建中发布,您还可以避免优化器产生晦涩的 Heisenbugs。
我的经验是,中型或大型应用程序在发布版本中的响应速度明显更快。试试你的应用程序,看看感觉如何。
发布版本可能会咬你的一件事是,调试版本代码有时可以抑制竞争条件和其他与线程相关的错误。优化的代码会导致指令重新排序,更快的执行会加剧某些竞争条件。
您永远不应该将 .NET Debug 版本发布到生产环境中。它可能包含丑陋的代码来支持编辑并继续或谁知道还有什么。据我所知,这种情况只发生在 VB 而不是 C#(注意:原始帖子被标记为 C#),但它仍然应该有理由暂停微软认为他们被允许做的事情调试版本。事实上,在 .NET 4.0 之前,VB 代码泄漏的内存与您为支持编辑并继续而构造的事件的对象实例的数量成正比。 (虽然据报道这是每个 https://connect.microsoft.com/VisualStudio/feedback/details/481671/vb-classes-with-events-are-not-garbage-collected-when-debugging 修复的,但生成的代码看起来很糟糕,创建 WeakReference
对象并将它们添加到静态列表中同时持有一个锁)我当然不想要任何生产环境中的这种调试支持!
我会这样说
很大程度上取决于您的实施。通常,差异不是那么大。我做了很多测量,经常看不出有什么不同。如果您使用非托管代码、大量巨大的数组和类似的东西,性能差异会稍大一些,但不是一个不同的世界(如在 C++ 中)。通常在发布代码中显示更少的错误(更高的容差),因此开关应该可以正常工作。
根据我的经验,发布模式带来的最糟糕的事情是晦涩难懂的“发布错误”。由于 IL(中间语言)在 Release 模式下进行了优化,因此存在在 Debug 模式下不会出现的错误的可能性。还有其他关于此问题的 SO 问题:Common reasons for bugs in release version not present in debug mode
这种情况在我身上发生过一两次,一个简单的控制台应用程序在调试模式下运行得非常好,但在完全相同的输入下,在发布模式下会出错。这些错误极难调试(讽刺的是,根据发布模式的定义)。
**Debug Mode:**
Developer use debug mode for debugging the web application on live/local server. Debug mode allow developers to break the execution of program using interrupt 3 and step through the code. Debug mode has below features:
1) Less optimized code
2) Some additional instructions are added to enable the developer to set a breakpoint on every source code line.
3) More memory is used by the source code at runtime.
4) Scripts & images downloaded by webresource.axd are not cached.
5) It has big size, and runs slower.
**Release Mode:**
Developer use release mode for final deployment of source code on live server. Release mode dlls contain optimized code and it is for customers. Release mode has below features:
1) More optimized code
2) Some additional instructions are removed and developer can’t set a breakpoint on every source code line.
3) Less memory is used by the source code at runtime.
4) Scripts & images downloaded by webresource.axd are cached.
5) It has small size, and runs fast.
我知道我的回答已经很晚了,而且我的回答并不完全符合您的要求,但是,我认为可以使用一些可靠而简单的示例会很好。无论如何,这段代码导致了 Debug 和 Release 之间的巨大差异。代码是在 Visual Studio 2019 上用 C++ 编写的。代码是这样的:
#include <iostream>
using namespace std;
unsigned long long fibonacci(int n)
{
return n < 2 ? n : (fibonacci(n - 1) + fibonacci(n - 2));
}
int main()
{
int x = 47;
cout << "Calculating..." << endl;
cout << "fib(" << x << ") = " << fibonacci(x) << endl;
}
编辑:计算斐波那契数列的性能差异
Debug Release
C++ x86 C++ x64 C++ x86 C++ x64 C# Debug C# Release
Time (mSeconds) 99384.9 27799.1 11066.0 11321.5 95233.7 24566.0
Time (Seconds) 99.4 27.8 11.1 11.3 95.2 24.6
LinkedList<T>
没有,即使它不经常使用。volatile
关键字不适用于存储在堆栈帧中的局部变量。来自 msdn.microsoft.com/en-us/library/x13ttww7.aspx 的文档:“volatile 关键字只能应用于类或结构的字段。不能将局部变量声明为 volatile。”Debug
和Release
构建之间的真正区别在于“优化代码”复选框,该复选框通常在Release
上打开,但在Debug
上关闭。这只是为了确保读者不会开始认为两种构建配置之间存在“神奇”的、不可见的差异,超出了 Visual Studio 中项目属性页上的内容。