现在的位置: 首页 > 综合 > 正文

利用本机映像生成器 (Ngen.exe)提高托管应用程序性能

2013年10月09日 ⁄ 综合 ⁄ 共 9331字 ⁄ 字号 评论关闭

   
本机映像生成器 (Ngen.exe) 是一个提高托管应用程序性能的工具。Ngen.exe 创建本机映像(包含经编译的特定于处理器的机器代码的文件),并将它们安装到本地计算机上的本机映像缓存中。运行库可从缓存中使用本机映像,而不是使用实时 (JIT) 编译器编译原始程序集。

在 .NET Framework 2.0 版中,Ngen.exe 有了很大变化:

安装程序集时还将安装其依赖项,从而简化了 Ngen.exe 的语法。

现在可以在应用程序域之间共享本机映像。

可利用新增操作 update 重新创建已经失效的映像。

操作可由计算机上使用空闲时间生成和安装映像的服务推迟执行。

消除了一些导致映像无效的因素。

有关如何使用 Ngen.exe 和本机映像服务的其他信息,请参见本机映像服务。

注意
在本机映像生成器 (Ngen.exe) 旧式语法中可以找到 .NET Framework 1.0 和 1.1 版的 Ngen.exe 语法。

ngen <action> [options]
ngen /? | /help
操作

下表说明了每个操作的语法。有关 actionArguments 各部分的说明,请参见参数、方案和配置表。选项表描述了 options 和帮助开关。

操作 说明
install [assemblyName | assemblyPath] [scenarios] [config] [/queue[:{1|2|3}]]
生成程序集及其依赖项的本机映像,并在本机映像缓存中安装这些映像。

如果指定了 /queue,则操作将排队等待本机映像服务。默认优先级是 3。

uninstall [assemblyName | assemblyPath | *] [scenarios] [config]
将程序集及其依赖项的本机映像从本机映像缓存中删除。

若要卸载单个映像及其依赖项,可使用与安装此映像时相同的命令行参数。

update [/queue]
更新已无效的本机映像。

如果指定了 /queue,则更新将排队以等待本机映像服务。更新的优先级总是预先设定为 3,因此它们在计算机空闲时运行。

display [assemblyName | assemblyPath]
显示程序集及其依赖项的本机映像的状态。

如果未提供参数,则显示本机映像缓存中的所有内容。

executeQueuedItems [1|2|3]
执行排队的编译作业。

如果指定了优先级,则执行具有较高或同等优先级的编译作业。如果未指定优先级,则执行所有排队的编译作业。

queue {pause | continue | status}
暂停本机映像服务,允许暂停的服务继续,或查询服务状态。

参数

参数 说明
assemblyName
程序集的名称。可提供程序集的部分名称(如 myAssembly),也可提供完整的显示名称(如 myAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0038abc9deabfle5)。

每个 Ngen.exe 命令行只能指定一个程序集。

assemblyPath
程序集的显式路径。可指定完整路径或相对路径。

如果指定文件名而不指定路径,则程序集必须位于当前目录中。

每个 Ngen.exe 命令行只能指定一个程序集。

方案

方案 说明
/Debug
生成可在调试器下使用的本机映像。

/Profile
生成可在探查器下使用的本机映像。

/NoDependencies
生成指定方案选项所需的最小数目的本机映像。

配置

配置 说明
/ExeConfig: exePath
使用指定的可执行程序集的配置。

绑定到依赖项时,Ngen.exe 需要作出与加载程序相同的决策。如果在运行时使用 Load 方法加载共享组件,应用程序的配置文件决定了为该共享组件加载的依赖项,例如,所加载依赖项的版本。/ExeConfig 开关就运行时将加载哪些依赖项向 Ngen.exe 给出指导。

/AppBase: directoryPath
查找依赖项时,使用指定目录作为应用程序基础。

选项

选项 说明
/nologo
禁止显示 Microsoft 启动版权标志。

/silent
禁止显示成功消息。

/verbose
显示详细的调试信息。

注意
由于操作系统限制,此选项显示的附加信息比在 Windows 98 和 Windows Millennium Edition 上显示的少。

/help, /?
显示当前版本的命令语法和选项。

备注

若要运行 Ngen.exe,您必须具有管理特权。

Ngen.exe 为指定程序集及其所有依赖项生成本机映像。依赖项是根据程序集清单中的引用来确定的。仅当应用程序使用反射(例如通过调用 System.Reflection.Assembly.Load 方法)来加载依赖项的情况下才需要单独安装依赖项。

要点
不要将 System.Reflection.Assembly.LoadFrom 方法用于本机映像。使用此方法加载的映像不能由执行上下文中的其他程序集使用。

Ngen.exe 维护着一个与依赖项有关的计数。例如,假设本机映像缓存中同时安装了 MyAssembly.exe 和 YourAssembly.exe,而且它们都具有对 OurDependency.dll 的引用。如果卸载了 MyAssembly.exe,则不会卸载 OurDependency.dll。只有当 YourAssembly.exe 也被卸载时才会将其移除。

如果为全局程序集缓存中的程序集生成本机映像,请指定其显示名称。请参见 System.Reflection.Assembly.FullName。

Ngen.exe 生成的本机映像可以在应用程序域之间共享。这意味着,在要求在应用程序域之间共享程序集的应用程序方案中可以使用 Ngen.exe。若要指定域非特定性:

将 LoaderOptimizationAttribute 属性应用于应用程序。

为新的应用程序域创建安装信息时,设置 System.AppDomainSetup.LoaderOptimization 属性。

将同一个程序集加载到多个应用程序域中时,总是使用非特定于域的代码。如果本机映像在已加载到共享域之后又被加载到非共享的应用程序域中,则该映像将无法使用。

注意
非特定于域的代码无法卸载,并且性能可能会稍微降低,尤其是在访问静态成员时。

为不同的方案生成映像

在您生成一个程序集的本机映像后,每当运行库运行该程序集时,都会自动尝试找到并使用该本机映像。根据使用方案的不同,可生成多个映像。

例如,如果您在调试或分析方案中运行程序集,则运行库将查找利用 /Debug 或 /Profile 选项生成的本机映像。如果运行库无法找到匹配的本机映像,它将恢复为标准的 JIT 编译。调试本机映像的唯一方式是使用 /Debug 选项创建本机映像。

uninstall 操作也能识别方案,因此您可以卸载所有方案或只卸载选择的方案。

确定何时使用本机映像

本机映像可从两方面提高性能:改善内存使用情况和减少启动时间。

注意
本机映像的性能取决于很多因素,这些因素使得分析难以进行,如代码和数据访问模式,有多少调用跨模块边界进行,以及多少依赖项已由其他应用程序加载。确定本机映像是否对应用程序有利的唯一方式是在关键部署方案中仔细进行性能测量。

改善内存使用情况
当代码在进程间共享时,本机映像可显著改善内存使用情况。本机映像为 Windows PE 文件,因此一个 .dll 文件的单个副本可由多个进程共享;而 JIT 编译器生成的本机代码存储在私有内存中,并且不可共享。

运行于终端服务下的应用程序也可从共享代码页中获益。

此外,不加载 JIT 编译器会为每个应用程序实例节省固定量的内存。

更快的应用程序启动速度
使用 Ngen.exe 预编译程序集可减少某些应用程序的启动时间。通常,如果应用程序共享组件程序集,则可从中获益,因为在第一个应用程序启动之后,共享组件即已加载,可供后续应用程序使用。而冷启动(应用程序中的所有程序集必须从硬盘上加载)则不会从本机映像中获得相同的益处,因为硬盘访问时间占了很大比重。

硬绑定可影响启动时间,因为硬绑定至主应用程序程序集的所有映像必须同时加载。

注意
如果您有强命名的共享组件,则请将它们放置在全局程序集缓存中。加载程序对未处于全局程序集缓存中的强命名程序集执行额外验证,实际抵消了使用本机映像在启动时间方面获得的任何改善。

程序集基址的重要性
因为本机映像为 Windows PE 文件,所以它们和其他可执行文件一样有着相同的重定基址问题。如果采用硬绑定,则重定位的性能开销甚至更显著。

若要设置本机映像的基址,请使用编译器的相应选项设置程序集的基址。Ngen.exe 对本机映像使用此基址。

注意
本机映像大于创建它时所基于的托管程序集。基址必须进行计算以允许使用这些更大的大小。

可使用 dumpbin.exe 之类的工具查看本机映像的首选基址。

使用注意事项摘要
下面的常规注意事项和应用程序注意事项可能有助于您决定是否对应用程序本机映像进行评估:

本机映像的加载速度比 MSIL 更快,因为本机映像不必执行很多启动操作,如 JIT 编译和类型安全验证。

本机映像需要较小的初始工作集,因为它不需要 JIT 编译器。

本机映像允许在进程间共享代码。

本机映像需要占用比 MSIL 程序集更大的硬盘空间,并且可能需要相当长的时间才能生成。

必须维护本机映像。

在提供原始程序集或原始程序集的某个依赖项后,需要重新生成映像。

一个程序集可能需要多个本机映像,分别用在不同应用程序或不同方案中。例如,两个应用程序中的配置信息可能为同一个依赖程序集生成不同的绑定方案。

必须由管理员(也就是从 Administrators 组中的某个 Windows 帐户)来生成本机映像。

除了这些常规注意事项之外,在确定本机映像是否可提供性能益处时,必须考虑应用程序的性质:

如果应用程序运行于使用很多共享组件的环境中,本机映像允许多个进程共享组件。

如果应用程序使用多个应用程序域,本机映像允许跨域共享代码页。

注意
在 .NET Framework 1.0 和 1.1 版中,本机映像不能跨应用程序域共享。而在 2.0 版中则可以。

如果应用程序将在终端服务器下运行,本机映像允许共享代码页。

编译为本机映像通常有利于大型应用程序。小型应用程序通常不会获益。

对于长时间运行的应用程序,运行时 JIT 编译的性能略高于本机映像。(硬绑定某种程度上可减少这一性能差别。)

硬绑定

硬绑定增加吞吐量并减少本机映像的工作集大小。硬绑定的缺点是硬绑定到程序集的所有映像在加载程序集时必须都加载。对于大型应用程序,这会大大增加启动时间。

硬绑定适合于在所有应用程序性能关键的方案中加载的依赖项。与本机映像使用情况的任何方面一样,仔细测量性能是确定硬绑定是否可改善应用程序性能的唯一方式。

DependencyAttribute 和 DefaultDependencyAttribute 属性可使您向 Ngen.exe 提供硬绑定提示。

注意
这些属性是对 Ngen.exe 的提示,而不是命令。使用这些提示不保证进行硬绑定。这些属性的含义在将来的版本中可能会更改。

为依赖项指定绑定提示
将 DependencyAttribute 应用于程序集可指示加载指定依赖项的可能性。System.Runtime.CompilerServices.LoadHint.Always 指示适合进行硬绑定,Default 指示应使用依赖项的默认提示,而 Sometimes 则指示不适合使用硬绑定。

下面的代码显示有两个依赖项的程序集的属性。第一个依赖项 (Assembly1) 适合于进行硬绑定,而第二个 (Assembly2) 不适合进行硬绑定。

Visual Basic
Copy code

    Imports System.Runtime.CompilerServices
    <Assembly:DependencyAttribute("Assembly1", LoadHint.Always)>
    <Assembly:DependencyAttribute("Assembly2", LoadHint.Sometimes)>

C#
Copy code

    using System.Runtime.CompilerServices;
    [assembly:DependencyAttribute("Assembly1", LoadHint.Always)]
    [assembly:DependencyAttribute("Assembly2", LoadHint.Sometimes)]

C++
Copy code

    using namespace System::Runtime::CompilerServices;
    [assembly:DependencyAttribute("Assembly1", LoadHint.Always)];
    [assembly:DependencyAttribute("Assembly2", LoadHint.Sometimes)];

程序集名称不包括文件扩展名。可使用显示名称。

为程序集指定默认绑定提示
只有某些程序集需要默认绑定提示:这些程序集将由依赖于它们的任何应用程序直接并经常使用。以 System.Runtime.CompilerServices.LoadHint.Always 将 DefaultDependencyAttribute 应用于这样的程序集可指定应使用硬绑定。

注意
不应将 DefaultDependencyAttribute 应用于不属于此类别的 .dll 程序集,因为以 System.Runtime.CompilerServices.LoadHint.Always 之外的其他值应用该属性的效果与根本不应用该属性相同。

Microsoft 使用 DefaultDependencyAttribute 指定硬绑定为 .NET Framework 中极少数程序集(如 mscorlib.dll)的默认绑定。

疑难解答

若要确认应用程序正在使用本机映像,可使用程序集绑定日志查看器 (Fuslogvw.exe)。在绑定日志查看器窗口上,选择“日志类别”框中的“本机映像”。Fuslogvw.exe 提供了有关为什么拒绝本机映像的信息。

可使用 JitCompilationStart 托管调试助手 (MDA) 确定 JIT 编译器何时开始编译函数。

推迟处理

超大型应用程序的本机映像生成过程可能需要相当长的时间。同样,更改共享组件或更改计算机设置可能需要更新很多本机映像。install 和 update 操作有一个 /queue 选项,该选项将该操作排入队列,以由本机映像服务推迟执行。此外,Ngen.exe 具有 queue 和 executeQueuedItems 操作,这些操作提供了对本机映像服务的某些控制。有关更多信息,请参见本机映像服务。

本机映像和 JIT 编译

如果 Ngen.exe 在程序集中遇到它无法生成的任何方法,则它会将这些方法从本机映像中排除。当运行库执行此程序集时,对于那些不包括在本机映像中的方法,它将恢复为 JIT 编译。

此外,如果程序集已更新,或者本机映像出于任何原因已失效,则不会使用本机映像。

无效映像
当您使用 Ngen.exe 创建程序集的本机映像时,输出取决于您指定的命令行选项以及计算机上的某些设置。这些设置包括:

.NET Framework 的版本。

操作系统的版本(在从 Windows 9x 系列更改为 Windows NT 系列的情况下)。

程序集的确切标识(重新编译将更改标识)。

程序集引用的所有程序集的确切标识(重新编译将更改标识)。

安全因素。

Ngen.exe 在生成本机映像时记录这些信息。当您执行程序集时,运行库将查找用匹配计算机的当前环境的选项和设置生成的本机映像。如果运行库没有找到匹配的本机映像,它将恢复为程序集的 JIT 编译。对计算机的设置和环境进行以下更改会导致本机映像失效:

.NET Framework 的版本。

如果将更新应用于 .NET Framework,则使用 Ngen.exe 创建的所有本机映像都将失效。因此,.NET Framework 的所有更新都执行 Ngen Update 命令,以确保重新生成所有的本机映像。.NET Framework 为它安装的 .NET Framework 库自动创建新的本机映像。

操作系统的版本(在从 Windows 9x 系列更改为 Windows NT 系列的情况下)。

例如,如果计算机上运行的操作系统的版本从 Windows 98 更改为 Windows XP,则存储在本机映像缓存中的所有本机映像都将失效。但是,如果将操作系统从 Windows 2000 更改为 Windows XP,则这些映像将不会失效。

程序集的确切标识。

如果重新编译程序集,则程序集的相应本机映像将失效。

程序集引用的任何程序集的确切标识。

如果更新一个托管程序集,则所有直接或间接依赖该程序集的本机映像都将失效,并需要重新生成。这既包括一般引用,也包括硬绑定依赖项。每当应用软件更新,安装程序就应执行 Ngen Update 命令,以确保重新生成所有依赖的本机映像。

安全因素。

更改计算机安全策略以限制先前授予某个程序集的权限,这样会导致该程序集的先前编译的本机映像失效。

有关公共语言运行库如何管理代码访问安全以及如何使用权限的详细信息,请参见代码访问安全性

示例

下面的命令为当前目录中的 ClientApp.exe 生成本机映像,并在本机映像缓存中安装该映像。如果该程序集存在配置文件,Ngen.exe 将使用它。此外,还会为 ClientApp.exe 所引用的任何 .dll 文件生成本机映像。

ngen install ClientApp.exe
使用 Ngen.exe 安装的映像也称为根。根可以为应用程序或共享组件。

下面的命令生成具有指定路径的 MyAssembly.exe 的本机映像。

ngen install c:/myfiles/MyAssembly.exe
当查找程序集及其依赖项时,Ngen.exe 使用与公共语言运行库所使用的相同的探查逻辑。默认情况下,包含 ClientApp.exe 的目录用作应用程序基目录,所有程序集的探查均从此目录开始。使用 /AppBase 选项可重写此行为。

注意
这是对 .NET Framework 1.0 和 1.1 版中的 Ngen.exe 行为的更改,在这些版本中,应用程序基目录设置为当前目录。

程序集可以具有不带引用的依赖项(例如,它使用 System.Reflection.Assembly.Load 方法加载 .dll 文件)。您可以使用 /ExeConfig,使用应用程序程序集的配置信息来为这样的 .dll 文件创建本机映像。下面的命令使用 MyApp.exe 的配置信息为 MyLib.dll, 生成一个本机映像。

ngen install c:/myfiles/MyLib.dll /ExeConfig:c:/myapps/MyApp.exe
在移除应用程序时将不移除以此方式安装的程序集。

若要卸载依赖项,请使用与安装时相同的命令行选项。下面的命令卸载上面示例中的 MyLib.dll。

ngen uninstall c:/myfiles/MyLib.dll /ExeConfig:c:/myapps/MyApp.exe
若要在全局程序集缓存中为程序集创建本机映像,请使用程序集的显示名称。例如:

ngen install "ClientApp, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=3c7ba247adcd2081, processorArchitecture=MSIL"
NGen.exe 会为您安装的每个方案生成一个单独的映像集。例如,下面的命令为正常操作生成一个完整的本机映像集,为调试生成另一个完整的映像集,并为探测生成第三个映像集:

ngen install MyApp.exe
ngen install MyApp.exe /debug
ngen install MyApp.exe /profile
显示本机映像缓存
在缓存中安装本机映像后,就可使用 Ngen.exe 显示这些映像。下面的命令显示本机映像缓存中的所有本机映像。

ngen display
display 操作首先列出所有的根程序集,然后列出计算机上的所有本机映像。

使用程序集的简单名称仅显示该程序集的信息。下面的命令显示本机映像缓存中与部分名称 MyAssembly 匹配的所有本机映像、其依赖项以及所有依赖 MyAssembly 的根:

ngen display MyAssembly
了解哪些根依赖于共享组件程序集对于在共享组件升级后确定 update 操作的影响非常有用。

如果指定了程序集的文件扩展名,则必须指定路径,或从包含该程序集的目录执行 Ngen.exe:

ngen display c:/myApps/MyAssembly.exe
下面的命令显示本机映像缓存中名为 MyAssembly 、版本为 1.0.0.0 的所有本机映像。

ngen display "myAssembly, version=1.0.0.0"
更新映像
映像通常是在共享组件更新之后进行更新的。若要更新本身发生更改或者其依赖项发生了更改的所有本机映像,请不带任何参数使用 update 操作。

ngen update
更新所有映像可能会耗费很长时间。使用 /queue 选项可对更新操作进行排队以等候本机映像服务执行。有关 /queue 选项和安装优先级的更多信息,请参见本机映像服务。

ngen update /queue
卸载映像
Ngen.exe 维护依赖项的列表,所以,只有当依赖于这些共享组件的所有程序集都被移除后,才会移除这些共享组件。此外,已安装为根的共享组件不会被移除。

下面的命令卸载根 ClientApp.exe 的所有方案:

ngen uninstall ClientApp
uninstall 操作可用于移除特定方案。下面的命令卸载 ClientApp.exe 的所有调试方案:

ngen uninstall ClientApp /debug
注意
卸载 /debug 方案不会卸载同时包含 /profile 和 /debug. 的方案

下面的命令卸载特定版本的 ClientApp.exe 的所有方案:

ngen uninstall "ClientApp, Version=1.0.0.0"
下面的命令卸载 "ClientApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3c7ba247adcd2081, processorArchitecture=MSIL", 的所有方案,或者只卸载该程序集的调试方案:

ngen uninstall "ClientApp, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=3c7ba247adcd2081, processorArchitecture=MSIL"
ngen uninstall "ClientApp, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=3c7ba247adcd2081, processorArchitecture=MSIL" /debug
与 install 操作一样,如果提供了扩展名,则需要从包含该程序集的目录执行 Ngen.exe,或者指定完整路径。

抱歉!评论已关闭.