我正在尝试优化我的应用程序,以便它在启动后立即运行良好。目前,它的发行版包含304个二进制文件(包括外部依赖项),总计57兆字节。它是一个主要进行数据库访问的WPF应用程序,没有任何重要的计算。
我发现调试配置为大多数操作提供了更好的(~5倍增益)时间,因为它们是在应用程序进程的生命周期中第一次执行。例如,在应用程序中打开特定屏幕,NGENed Debug需要0.3秒,JITted Debug需要0.5秒,NGENed Release需要1.5秒,JITted Release需要2.5秒。
据我所知,JIT编译时间的差距是由JIT编译器对Release二进制文件应用更积极的优化引起的。据我所知,Debug和Release配置因传递给C#编译器的/p:DebugType
和/p:Optimize
开关而不同,但即使我使用/p:Configuration=Release /p:DebugType=full /p:Optimize=false
构建应用程序,我也会看到相同的性能差距 - 即与/p:Configuration=Debug
中的图像调试选项相同。
我确认通过查看应用于生成的程序集的DebuggableAttribute
来应用这些选项。观察NGEN输出,我看到<debug>
添加到正在编译的某些程序集的名称中 - NGEN如何区分调试和非调试程序集?正在测试的操作使用动态代码生成 - 动态代码应用了什么级别的优化?
注意:由于外部依赖性,我使用的是32位框架。我应该期待x64上有不同的结果吗?
注意:我也不使用条件编译。因此,两种配置的编译源都是相同的。
答案 0 :(得分:2)
如果正如您所说,您要加载304个程序集,那么这可能是导致您的应用程序运行缓慢的原因。 这似乎是要加载的极大数量的组件。
每次CLR从另一个尚未加载到AppDomain中的程序集中获取代码时,都必须从磁盘加载它。
您可以考虑使用ILMerge合并其中一些程序集。这将减少从磁盘加载程序集的延迟(您需要预先安装一个更大的磁盘)。
这可能需要一些实验,因为并非所有东西都喜欢被合并(特别是那些使用Reflection,并且依赖于程序集文件名永远不会改变)。它也可能导致非常大的组件。
答案 1 :(得分:1)
好的,这里有几个问题。
据我所知,Debug和Release配置有所不同 / p:DebugType和/ p:优化传递给C#编译器的开关,但是我 即使我使用构建应用程序,也会看到相同的性能差距 / p:Configuration = Release / p:DebugType = full / p:Optimize = false - 那个 是,与/ p中相同的映像调试选项:Configuration = Debug。
虽然勾选框相同,但更改为“释放”模式也会导致删除某些内部代码路径,例如Debug.Assert()
(在Microsoft内部代码中大量使用)。因此,这些不会在运行时进行评估,从而导致一些性能提升。 DebugType=full
生成与其编译的代码匹配的PDB,因此本身不会影响性能。如果部署了PDB,则异常处理代码将使用PDB针对编译的代码提供更有用的堆栈跟踪。发布模式还在内部触发一些内存改进,因为Debug版本用于附加调试器。
NGEN是一种用于“潜在地”优化应用程序的工具。它优化了代码,使其专门针对您所在的计算机。但它可能有缺点,因为JIT编译器可以更改内存中代码的布局,而NGEN本质上更加静态。
对于32位(x86)依赖项,您的应用程序现在将以x86模式运行。如果依赖项同时具有x86和x64版本,并且如果您的应用程序在“任何CPU”编译模式下编译,则JIT编译器将在2之间自动切换.NGEN将仅生成当前计算机的特定版本。因此,如果你做NGEN然后分发,它只适用于你编译的特定架构。
如果您没有使用条件编译功能,那么从Debug切换到Release并不重要。但是你会在Release中看到性能优势。
对于NGEN,我建议你进行广泛的测试,看看它的好处。它并不总能带来更好的性能。
答案 2 :(得分:1)
你是在调试器('F5')下运行它还是没有调试器('ctrl + F5')?如果是前者,请确保工具 - &gt;选项 - &gt;调试 - &gt; “取消选中模块加载时的JIT优化”