CLR via C# 读书笔记1-5

时间:2022-12-28 17:25:35

本地代码生成器: NGen.exe

NGen.exe 能把 IL 代码变异为本地代码,这使得在运行时 CLR 的 JIT 编译器不需要在运行时编译 IL 代码从而提高运行效率。NGen.exe 适用以下两个场景:
■ 提高程序的启动速度
■ 降低程序的 working set (当你的程序集会被多个进程适用的时候) 
NGen.exe 有这个作用的原因是它把编译后的本地代码存放在一个独立文件中,而这个文件可以被内存映射到多个进程的地址空间中,这些进程可以共用一份代码,而不需要各自搞一份。

NGen.exe 把生成的本地代码文件存放在类似 %SystemRoot%\Assembly\NativeImages_v4.0.#####_64 的目录中,目录名包含了 CLR 的版本号以及 32-bit 或 64-bit 信息。每当 CLR 载入一个程序集时,他总是检查是否有对应版本的被 NGen 过的本地文件存在,如果不存在 CLR JIT 编译器照常介入,反之 CLR 使用这些变异过的代码。

但是NGen 也有以下的缺点:
■ NGen 生成的文件无法脱离包含 IL 文件独立运行,因为他需要metadata。另外某些情况下(见后文) CLR 无法使用 NGen 生成的文件,JIT 编译器还是需要 IL 代码。
■ NGen 生成后文件后,原程序或环境发生了变更,造成不同步。例如:

• CLR 版本:升级或打补丁
• CPU 类型:硬件升级
• Windows 操作系统版本:升级或打补丁
• 程序集标示号 ssembly’s identity module version ID (MVID):重新编译
• 参照的程序集版本标示:参照程序集被重新编译
• 安全:安全策略变更(比如: declarative inheritance, declarative ink-time, SkipVerification, 或 UnmanagedCode 权限)

小提示: NGen.exe 可以运行在 update 模式中,在这种模式下,已经被 NGen 处理过的文件会被及时更新,并保持同步。

之前的文章中提到过NGen.exe 因为无法获晓代码运行时的系统环境,所以无法做到 JIT compiler 那样生成高优化率的指令,有些情况下它生成的代码要比通过 JIT 编译器执行的方式慢5%左右。

通常来说 server 端的程序,适用 NGen.exe 意义不大,因为仅仅首次的客户请求得到优化,而且由于大多数server 端的程序仅一个实例,所以降低 working set 消耗的特性也享受不到。
对于客户端的程序,NGen.exe 通常能改善启动时间,降低 working set 消耗。而且如果该程序的所有模块都被 NGen.exe 生成过本地代码, CLR 就完全不需要载入 JIT 编译器。

对于启动时间过长的胖客户端的程序,Microsoft 提供一个名为 Managed Profile Guided Optimization tool (MPGO.exe)。这个工具能分析程序启动时的执行情况,并提供信息给 NGen.exe, 从而使其生成的更好的本地代码,从而进一步提高启动速度。