15

我注意到 WPF 应用程序启动有时很慢。有人知道原因是元素初始化还是 DLL 加载还是其他原因?

4

5 回答 5

21

下面的文字摘自这篇关于改善 WPF 应用程序启动时间的 MSDN 文章(编辑:现在合并到WPF 应用程序启动时间

应用程序启动时间

WPF 应用程序启动所需的时间量可能会有很大差异。本主题介绍了用于减少 Windows Presentation Foundation (WPF) 应用程序的感知和实际启动时间的各种技术。

了解冷启动和热启动

冷启动发生在您的应用程序在系统重新启动后第一次启动时,或者当您启动应用程序时,将其关闭,然后在很长一段时间后再次启动它。当应用程序启动时,如果 Windows 内存管理器的备用列表中不存在所需的页面(代码、静态数据、注册表等),则会发生页面错误。需要磁盘访问才能将页面放入内存。

当主要公共语言运行时 (CLR) 组件的大部分页面已经加载到内存中时,就会发生热启动,这样可以节省昂贵的磁盘访问时间。这就是托管应用程序在第二次运行时启动速度更快的原因。

实现启动画面

如果在启动应用程序和显示第一个 UI 之间存在明显的、不可避免的延迟,请使用闪屏优化感知的启动时间。这种方法几乎在用户启动应用程序后立即显示图像。当应用程序准备好显示它的第一个 UI 时,启动屏幕会消失。从 .NET Framework 3.5 SP1 开始,您可以使用SplashScreen类来实现启动画面。有关详细信息,请参阅如何:将启动画面添加到 WPF 应用程序

您还可以使用本机 Win32 图形来实现自己的启动画面。在调用Run方法之前显示您的实现。

分析启动代码

确定冷启动缓慢的原因。磁盘 I/O 可能是原因,但情况并非总是如此。一般来说,您应该尽量减少对外部资源的使用,例如网络、Web 服务或磁盘。

在测试之前,请确认没有其他正在运行的应用程序或服务使用托管代码或 WPF 代码。

重新启动后立即启动 WPF 应用程序,并确定显示所需的时间。如果您的应用程序的所有后续启动(热启动)都快得多,那么您的冷启动问题很可能是由 I/O 引起的。

如果您的应用程序的冷启动问题与 I/O 无关,则可能是您的应用程序执行了一些冗长的初始化或计算,等待某个事件完成,或者在启动时需要大量 JIT 编译。以下部分更详细地描述了其中一些情况。

优化模块加载

使用 Process Explorer (Procexp.exe) 和 Tlist.exe 等工具来确定应用程序加载的模块。命令 Tlist <pid> 显示进程加载的所有模块。

例如,如果您没有连接到 Web 并且您看到 System.Web.dll 已加载,那么您的应用程序中有一个模块引用了此程序集。检查以确保引用是必要的。

如果您的应用程序有多个模块,请将它们合并到一个模块中。这种方法需要较少的 CLR 程序集加载开销。更少的程序集也意味着 CLR 维护的状态更少。

延迟初始化操作

考虑将初始化代码推迟到呈现主应用程序窗口之后。

请注意,初始化可能在类构造函数内部执行,如果初始化代码引用其他类,可能会导致执行许多类构造函数的级联效应。

避免应用程序配置

考虑避免应用程序配置。例如,如果应用程序具有简单的配置要求并且具有严格的启动时间目标,则注册表项或简单的 INI 文件可能是更快的启动替代方案。

利用 GAC

如果程序集未安装在全局程序集缓存 (GAC) 中,则强名称程序集的哈希验证和 Ngen 图像验证会导致延迟(如果计算机上有该程序集的本机映像)。GAC 中安装的所有程序集都会跳过强名称验证。有关详细信息,请参阅Gacutil.exe(全局程序集缓存工具)

使用 Ngen.exe

考虑在您的应用程序上使用本机图像生成器 (Ngen.exe)。使用 Ngen.exe 意味着用 CPU 消耗换取更多磁盘访问,因为 Ngen.exe 生成的本机映像可能比 MSIL 映像大。

为了缩短热启动时间,您应该始终在您的应用程序上使用 Ngen.exe,因为这避免了应用程序代码的 JIT 编译的 CPU 成本。

在某些冷启动情况下,使用 Ngen.exe 也很有帮助。这是因为不需要加载 JIT 编译器 (mscorjit.dll)。

同时拥有 Ngen 和 JIT 模块可能会产生最坏的影响。这是因为必须加载 mscorjit.dll,并且当 JIT 编译器处理您的代码时,当 JIT 编译器读取程序集的元数据时,必须访问 Ngen 图像中的许多页面。

Ngen 和 ClickOnce

您计划部署应用程序的方式也会影响加载时间。ClickOnce 应用程序部署不支持 Ngen。如果您决定将 Ngen.exe 用于您的应用程序,则必须使用另一种部署机制,例如 Windows Installer。

有关详细信息,请参阅Ngen.exe(本机图像生成器)

变基和 DLL 地址冲突

如果您使用 Ngen.exe,请注意在内存中加载本机映像时可能会发生变基。如果由于该地址范围已分配而未在其首选基地址加载 DLL,则 Windows 加载程序将在另一个地址加载它,这可能是一项耗时的操作。

您可以使用虚拟地址转储 (Vadump.exe) 工具检查是否存在所有页面都是私有的模块。如果是这种情况,则模块可能已重新定位到不同的地址。因此,它的页面不能被共享。

有关如何设置基地址的详细信息,请参阅Ngen.exe(本机图像生成器)

优化验证码

Authenticode 验证增加了启动时间。Authenticode 签名的程序集必须通过证书颁发机构 (CA) 进行验证。此验证可能很耗时,因为它可能需要多次连接到网络才能下载当前的证书吊销列表。它还确保在通往受信任根的路径上有完整的有效证书链。在加载程序集时,这可能会导致几秒钟的延迟。

考虑在客户端计算机上安装 CA 证书,或尽可能避免使用 Authenticode。如果您知道您的应用程序不需要发布者证据,则您无需支付签名验证的费用。

从 .NET Framework 3.5 开始,有一个配置选项允许绕过 Authenticode 验证。为此,请将以下设置添加到 app.exe.config 文件:

<configuration>
 <runtime>
    <generatePublisherEvidence enabled="false"/>
  </runtime>
</configuration>

在 Windows Vista 上比较性能

Windows Vista 中的内存管理器有一项称为 SuperFetch 的技术。SuperFetch 分析一段时间内的内存使用模式,以确定特定用户的最佳内存内容。它不断工作以始终保持该内容。

这种方法与 Windows XP 中使用的预取技术不同,后者将数据预加载到内存中而不分析使用模式。随着时间的推移,如果用户在 Windows Vista 上频繁使用您的 WPF 应用程序,您的应用程序的冷启动时间可能会有所改善。

高效使用 AppDomain

如果可能,将程序集加载到与域无关的代码区域,以确保在应用程序中创建的所有 AppDomain 中使用本机映像(如果存在)。

为了获得最佳性能,请通过减少跨域调用来强制执行有效的跨域通信。如果可能,请使用不带参数或带有原始类型参数的调用。

使用 NeutralResourcesLanguage 属性

使用NeutralResourcesLanguageAttributeResourceManager指定中性区域性。这种方法避免了不成功的程序集查找。

使用 BinaryFormatter 类进行序列化

如果必须使用序列化,请使用BinaryFormatter类而不是XmlSerializer类。BinaryFormatter类在mscorlib.dll 程序集中的基类库 (BCL) 中实现。XmlSerializer在System.Xml.dll 程序集中实现,它可能是要加载的附加 DLL。

如果必须使用XmlSerializer类,则通过预先生成序列化程序集可以获得更好的性能。

配置 ClickOnce 以在启动后检查更新

如果您的应用程序使用 ClickOnce,请通过将 ClickOnce 配置为在应用程序启动后检查部署站点的更新来避免启动时的网络访问。

如果您使用 XAML 浏览器应用程序 (XBAP) 模型,请记住即使 XBAP 已经在 ClickOnce 缓存中,ClickOnce 也会检查部署站点的更新。有关详细信息,请参阅ClickOnce 安全性和部署

将 PresentationFontCache 服务配置为自动启动

重新启动后运行的第一个 WPF 应用程序是 PresentationFontCache 服务。该服务缓存系统字体,改进字体访问,并提高整体性能。启动服务会产生开销,在某些受控环境中,可以考虑将服务配置为在系统重新启动时自动启动。

以编程方式设置数据绑定

不要使用 XAML 以声明方式为主窗口设置DataContext ,而是考虑在OnActivated方法中以编程方式设置它。

于 2009-05-15T14:02:36.307 回答
4

我见过的关于修复 WPF 启动性能的最有用的建议是在另一个问题中给出的:在每个框架文件夹中运行“ngen update”。

微软似乎无法让他们的 ngen 缓存保持最新,这导致您的应用程序几乎每次启动时都要重新编译一半的 .NET 框架。

很难相信,但似乎是真的。

于 2012-01-13T10:58:46.117 回答
2

如果使用 Framework 3.51 而不是 3.5 或 3.0,WPF 应用程序的启动时间会快得多。3.51确实是一个改进。

于 2009-05-15T14:07:39.307 回答
2

这是一个旧线程,但我在尝试修复 Win10 系统上的 WPF 应用程序的启动性能问题时已经多次来到这里,所以我想我会陈述一个可能对其他人有帮助的答案 - 一个需要该系统上所有 WPF 应用程序的可怕启动时间为 5 秒,降至几毫秒。 删除 nVidia“3d Vision”驱动程序。我有一张 GeForce GTX 650 卡,“3d Vision”驱动程序似乎没有提供任何有用的东西,所以删除它对我来说没有问题。VisualStudio2015 性能分析工具最终帮助显示,在通过 nvapi64.dll(nVidia 驱动程序)调用后,几乎所有的 5 秒启动时间都花在了 IDLE 上。哇。

于 2016-01-27T02:10:34.707 回答
0

Stuart 链接到的优秀文章对我帮助最大的是 XmlSerializer 技巧。这真的剃了好几秒钟。此外,不要低估对 HD 进行碎片整理 :-)

于 2010-04-23T15:22:16.337 回答