15

I am trying to optimize my application for for it to perform well right after it is started. At the moment, its distribution contains 304 binaries (including external dependencies) totaling 57 megabytes. It is a WPF application doing mostly database access, without any significant calculations.

I discovered that the Debug configuration offers way better (~5 times gain) times for most operations, as they are performed for the first time during the lifetime of the application's process. For example, opening a specific screen within the app takes 0.3 seconds for NGENed Debug, 0.5 seconds for JITted Debug, 1.5 seconds for NGENed Release and 2.5 seconds for JITted Release.

I understand that the gap in JIT compilation time is caused by the JIT compiler applying more aggressive optimizations for the Release binaries. From what I can tell, Debug and Release configurations differ by the /p:DebugType and /p:Optimize switches passed to the C# compiler, but I see the same performance gap even if I build the application with /p:Configuration=Release /p:DebugType=full /p:Optimize=false – that is, the same image debug options as in /p:Configuration=Debug.

I confirm that the options were applied by looking at the DebuggableAttribute applied to the resulting assembly. Observing the NGEN output, I see <debug> added to the names of some assemblies being compiled – how does NGEN distinguish between debug and non-debug assemblies? The operation being tested uses dynamic code generation – what level of optimization is applied to dynamic code?

Note: I am using the 32-bit framework due to external dependencies. Should I expect different results on x64?

Note: I also do not use conditional compilation. So the compiled source is the same for both configurations.

4

3 回答 3

2

如果,如您所说,要加载 304 个程序集,那么这可能是您的应用程序运行缓慢的原因。这似乎是要加载的程序集数量非常多。

每次 CLR 从另一个程序集获取尚未加载到 AppDomain 中的代码时,它都必须从磁盘加载它。

您可能会考虑使用 ILMerge 来合并其中的一些程序集。这将减少从磁盘加载程序集的延迟(您需要一个更大的磁盘预先命中)。

它可能需要一些实验,因为并非所有东西都喜欢被合并(特别是那些使用反射,并且依赖于程序集文件名永远不会改变的东西)。它也可能导致非常大的程序集。

于 2012-05-07T05:13:09.347 回答
1

你是在调试器('F5')下运行它还是没有调试器('ctrl+F5')?如果是前者,请确保工具 -> 选项 -> 调试 -> “在模块加载时抑制 JIT 优化”未选中

于 2012-05-06T20:23:46.717 回答
1

好的,这里有几个问题。

据我所知,调试和发布配置因传递给 C# 编译器的 /p:DebugType 和 /p:Optimize 开关而异,但即使我使用 /p:Configuration=Release / 构建应用程序,我也看到相同的性能差距p:DebugType=full /p:Optimize=false – 即与 /p:Configuration=Debug 中相同的图像调试选项。

尽管勾选框相同,但更改为发布模式也会导致某些内部代码路径被删除,例如Debug.Assert()(在 Microsoft 内部代码中大量使用)。所以这些不会在运行时评估,这会导致一些性能提升。DebugType=full生成与其编译的代码匹配的 PDB,因此本身不会影响性能。如果部署了 PDB,异常处理代码将使用 PDB 针对已编译代码提供更有用的堆栈跟踪。发布模式还会在内部触发一些内存改进,因为调试版本用于附加调试器。

NGEN 是一种用于“潜在”优化应用程序的工具。它优化代码以针对您所在的计算机运行。但它也有缺点,因为 JIT 编译器可以更改内存中代码的布局,而 NGEN 本质上更静态。

至于 32 位 (x86) 依赖项,您的应用程序现在将以 x86 模式运行。如果依赖项同时具有 x86 和 x64 版本,并且如果您的应用程序在“任何 CPU”编译模式下编译,则 JIT 编译器将在两者之间自动切换。NGEN 只会为当前计算机生成特定版本。所以如果你做了 NGEN 然后分发,它只适用于你编译的特定架构。

如果您没有使用条件编译功能,那么从 Debug 切换到 Release 并不重要。但是您会在 Release 中看到性能优势。

对于 NGEN,我建议您进行广泛的测试,以了解与 2 相比的优势。它并不总能带来更好的性能。

于 2012-04-21T11:49:01.207 回答