是什么让 WPF 应用程序启动缓慢? [英] What makes WPF application startup slow?

查看:135
本文介绍了是什么让 WPF 应用程序启动缓慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

以下文字摘自 这篇关于改进 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 签名的程序集必须通过证书颁发机构 (CA) 进行验证.这种验证可能很耗时,因为它可能需要多次连接到网络才能下载当前的证书吊销列表.它还确保在通往受信任根的路径上存在完整的有效证书链.在加载程序集时,这可能会导致几秒钟的延迟.

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

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

<预><代码><配置><运行时><generatePublisherEvidence enabled="false"/></运行时></配置>

比较 Windows Vista 上的性能

Windows Vista 中的内存管理器有一项称为 SuperFetch 的技术.SuperFetch 会随着时间的推移分析内存使用模式,以确定特定用户的最佳内存内容.它持续工作以始终保持该内容.

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

高效使用 AppDomains

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

为了获得最佳性能,请通过减少跨域调用来实施高效的跨域通信.如果可能,请使用不带参数或带原始类型参数的调用.

使用 NeutralResourcesLanguage 属性

使用 NeutralResourcesLanguageAttribute 指定中性文化对于 ResourceManager.这种方法避免了不成功的程序集查找.

使用 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 方法.

I noticed that WPF application startup is sometimes pretty slow. Does anybody know if the the cause is the elements initialization or DLLs loading or something else?

解决方案

The text below was extracted from this MSDN article on Improving WPF applications startup time (Edit: now merged into WPF Application Startup Time)

Application Startup Time

The amount of time that is required for a WPF application to start can vary greatly. This topic describes various techniques for reducing the perceived and actual startup time for a Windows Presentation Foundation (WPF) application.

Understanding Cold Startup and WarmStartup

Cold startup occurs when your application starts for the first time after a system reboot, or when you start your application, close it, and then start it again after a long period of time. When an application starts, if the required pages (code, static data, registry, etc) are not present in the Windows memory manager's standby list, page faults occur. Disk access is required to bring the pages into memory.

Warm startup occurs when most of the pages for the main common language runtime (CLR) components are already loaded in memory, which saves expensive disk access time. That is why a managed application starts faster when it runs a second time.

Implement a Splash Screen

In cases where there is a significant, unavoidable delay between starting an application and displaying the first UI, optimize the perceived startup time by using a splash screen. This approach displays an image almost immediately after the user starts the application. When the application is ready to display its first UI, the splash screen fades. Starting in the .NET Framework 3.5 SP1, you can use the SplashScreen class to implement a splash screen. For more information, see How to: Add a Splash Screen to a WPF Application.

You can also implement your own splash screen by using native Win32 graphics. Display your implementation before the Run method is called.

Analyze the Startup Code

Determine the reason for a slow cold startup. Disk I/O may be responsible, but this is not always the case. In general, you should minimize the use of external resources, such as network, Web services, or disk.

Before you test, verify that no other running applications or services use managed code or WPF code.

Start your WPF application immediately after a reboot, and determine how long it takes to display. If all subsequent launches of your application (warm startup) are much faster, your cold startup issue is most likely caused by I/O.

If your application's cold startup issue is not related to I/O, it is likely that your application performs some lengthy initialization or computation, waits for some event to complete, or requires a lot of JIT compilation at startup. The following sections describe some of these situations in more detail.

Optimize Module Loading

Use tools such as Process Explorer (Procexp.exe) and Tlist.exe to determine which modules your application loads. The command Tlist <pid> shows all the modules that are loaded by a process.

For example, if you are not connecting to the Web and you see that System.Web.dll is loaded, then there is a module in your application that references this assembly. Check to make sure that the reference is necessary.

If your application has multiple modules, merge them into a single module. This approach requires less CLR assembly-loading overhead. Fewer assemblies also mean that the CLR maintains less state.

Defer Initialization Operations

Consider postponing initialization code until after the main application window is rendered.

Be aware that initialization may be performed inside a class constructor, and if the initialization code references other classes, it can cause a cascading effect in which many class constructors are executed.

Avoid Application Configuration

Consider avoiding application configuration. For example, if an application has simple configuration requirements and has strict startup time goals, registry entries or a simple INI file may be a faster startup alternative.

Utilize the GAC

If an assembly is not installed in the Global Assembly Cache (GAC), there are delays caused by hash verification of strong-named assemblies and by Ngen image validation if a native image for that assembly is available on the computer. Strong-name verification is skipped for all assemblies installed in the GAC. For more information, see Gacutil.exe (Global Assembly Cache Tool).

Use Ngen.exe

Consider using the Native Image Generator (Ngen.exe) on your application. Using Ngen.exe means trading CPU consumption for more disk access because the native image generated by Ngen.exe is likely to be larger than the MSIL image.

To improve the warm startup time, you should always use Ngen.exe on your application, because this avoids the CPU cost of JIT compilation of the application code.

In some cold startup scenarios, using Ngen.exe can also be helpful. This is because the JIT compiler (mscorjit.dll) does not have to be loaded.

Having both Ngen and JIT modules can have the worst effect. This is because mscorjit.dll must be loaded, and when the JIT compiler works on your code, many pages in the Ngen images must be accessed when the JIT compiler reads the assemblies' metadata.

Ngen and ClickOnce

The way you plan to deploy your application can also make a difference in load time. ClickOnce application deployment does not support Ngen. If you decide to use Ngen.exe for your application, you will have to use another deployment mechanism, such as Windows Installer.

For more information, see Ngen.exe (Native Image Generator).

Rebasing and DLL Address Collisions

If you use Ngen.exe, be aware that rebasing can occur when the native images are loaded in memory. If a DLL is not loaded at its preferred base address because that address range is already allocated, the Windows loader will load it at another address, which can be a time-consuming operation.

You can use the Virtual Address Dump (Vadump.exe) tool to check if there are modules in which all the pages are private. If this is the case, the module may have been rebased to a different address. Therefore, its pages cannot be shared.

For more information about how to set the base address, see Ngen.exe (Native Image Generator).

Optimize Authenticode

Authenticode verification adds to the startup time. Authenticode-signed assemblies have to be verified with the certification authority (CA). This verification can be time consuming, because it can require connecting to the network several times to download current certificate revocation lists. It also makes sure that there is a full chain of valid certificates on the path to a trusted root. This can translate to several seconds of delay while the assembly is being loaded.

Consider installing the CA certificate on the client computer, or avoid using Authenticode when it is possible. If you know that your application does not need the publisher evidence, you do not have to pay the cost of signature verification.

Starting in .NET Framework 3.5, there is a configuration option that allows the Authenticode verification to be bypassed. To do this, add the following setting to the app.exe.config file:

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

Compare Performance on Windows Vista

The memory manager in Windows Vista has a technology called SuperFetch. SuperFetch analyzes memory usage patterns over time to determine the optimal memory content for a specific user. It works continuously to maintain that content at all times.

This approach differs from the pre-fetch technique used in Windows XP, which preloads data into memory without analyzing usage patterns. Over time, if the user uses your WPF application frequently on Windows Vista, the cold startup time of your application may improve.

Use AppDomains Efficiently

If possible, load assemblies into a domain-neutral code area to make sure that the native image, if one exists, is used in all AppDomains created in the application.

For the best performance, enforce efficient cross-domain communication by reducing cross-domain calls. When possible, use calls without arguments or with primitive type arguments.

Use the NeutralResourcesLanguage Attribute

Use the NeutralResourcesLanguageAttribute to specify the neutral culture for the ResourceManager. This approach avoids unsuccessful assembly lookups.

Use the BinaryFormatter Class for Serialization

If you must use serialization, use the BinaryFormatter class instead of the XmlSerializer class. The BinaryFormatter class is implemented in the Base Class Library (BCL) in the mscorlib.dll assembly. The XmlSerializer is implemented in the System.Xml.dll assembly, which might be an additional DLL to load.

If you must use the XmlSerializer class, you can achieve better performance if you pre-generate the serialization assembly.

Configure ClickOnce to Check for Updates After Startup

If your application uses ClickOnce, avoid network access on startup by configuring ClickOnce to check the deployment site for updates after the application starts.

If you use the XAML browser application (XBAP) model, keep in mind that ClickOnce checks the deployment site for updates even if the XBAP is already in the ClickOnce cache. For more information, see ClickOnce Security and Deployment.

Configure the PresentationFontCache Service to Start Automatically

The first WPF application to run after a reboot is the PresentationFontCache service. The service caches the system fonts, improves font access, and improves overall performance. There is an overhead in starting the service, and in some controlled environments, consider configuring the service to start automatically when the system reboots.

Set Data Binding Programmatically

Instead of using XAML to set the DataContext declaratively for the main window, consider setting it programmatically in the OnActivated method.

这篇关于是什么让 WPF 应用程序启动缓慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆