CLR 完全介绍

NET Framework 3.5 SP 1 中的 CLR 优化

Surupa Biswas

内容

启动性能改进
JIT 优化
NGen 和 ASLR
.NET Framework 3.5 客户端配置
向上覆盖

工时的意外量进入 Microsoft.NET Framework 的核心.NET Framework 3.5 SP 1 在 8 月 2008 发布。 此处,我将提供您可以通过只针对此最新的 Service Pack 中运行现有的基于 CLR 2.0 的应用程序预期的有关我们 CLR 团队的公共语言运行库 (CLR) 所做更改的和改进功能详细信息。 我们的工作的大部分被居中改善性能、 安全和部署面向.NET 平台的应用程序。

之前 delving 到详细信息,请注意您可以下载该 .NET Framework 3.5 SP 1从 Microsoft 下载中心。 也是可通过 Windows Update。 此外,一下 图 1 。 它回答 perennial 问题: 的.NET Framework 的版本中的 CLR 版本是?

图 1.NET 版本和 CLR 版本
.NET Framework 版本 包含 CLR 版本
2.0 2.0
3.0 2.0、 3.0
3.5 2.0 SP 1 3.0 SP 1,3.5
3.5 SP 1 2.0 SP 2 3.0 SP 2,3.5 SP 1

启动性能改进

提高启动性能,尤其托管应用程序的冷启动时间,是的.NET Framework 3.5 SP 1 的重点。

.NET Framework 中的托管程序集是很大程度上预编译,通过 NGen (" NGen 的性能优势"),并且代码和 NGen 映像中的数据的布局的使用在 Framework 的应用程序启动性能有强的影响。 特别,因为冷启动时间通常需要从磁盘中读取的图像的页面的绑定的任何努力减小转换为更好的包图像,以便仅一小部分及其页面在启动过程中读取。

为该效果 CLR 使用配置文件驱动引擎来优化的.NET Framework 中的程序集的 NGen 映像的布局。 配置文件数据由运行我们认为是代表我们框架的使用情况,然后作为和包含相应程序集中的某个资源被生成时的情况收集。 在 NGen,如果类资源找到程序集中它则使用为包"热"的代码和数据 (那些培训方案运行时被访问的) 一起,从而都移动"cold"的代码和数据为图像中自己部分。

此外,NET Framework 3.5 SP 1 包含多个改进我们热 / cold 拆分的基础结构这反过来提高的 NGen 映像位置。 特别,Basic 包含不同类型的控制流的方法中的阻止例如切换语句或无条件的分支,现在重新排列的更多的冷块可进行拆分关闭并移至的 NGen 映像冷部分。 我们还实现使用更好的算法来合并来自多个培训方案的配置文件数据,我们现在设置优先级一起打包数据 / 代码由最多一给定的培训组中的方案的访问。 最后,以进一步改善执行一起 temporally 的代码空间局部性,NGen 映像中的可执行代码被划分为 RunOnce、 RunMany 和 RunNever 部分。 我们的配置文件数据收集和使用这些改进进行 32 位和 64 位平台。 因以下的更改没有大于比 50%(有时会明显更高版本) 的培训方案重新运行之上经过培训的.NET Framework 时涉及的冷页数减少。

遗憾的是,我们配置文件数据收集 Framework 并仍内部只。 而此工作中受益托管使用所有应用程序.NET Framework 库在启动,可以进一步应用程序的程序集的 NGen 在通过使用我们的培训优化启动性能的 Framework。 我们希望使某个时候的可用此框架,因此保持优化 !

请注意,在某种程度的相关更改 (" 强名称跳过") 还解决了我本文前面提到的另一个 NGen 相关问题" NGen 的性能优势"即,花费时间验证强名称签名。

JIT 优化

有点切换齿轮,NET Framework 3.5 SP 1 还包含由 32 位和 64 位的 JIT 编译器生成代码质量改进。 特别,32 位 JIT 可以现在内联方法调用涉及传递、 返回,或结构上运行的。 才能本版,32 位 JIT 只是放弃尝试的内联类函数由于结构不在 x 86 的一流成员 JIT,降低到编译过程中的很早的 byref 类型指针。 一旦出现这种情况 JIT 将不再能够识别该结构,并且不能执行普通优化,如复制传播,基元类型上执行的。

因此在以下示例,JIT 编译器无法确定它只是需要传播"a"到最后一个语句并而生成的冗余副本的三个集的代码 (b = 一个; c = b ; d = c ;)。

MyStruct a, b, c, d;
a = new MyStruct(1, 2);
b = a;
c = b;
d = c;
Console.WriteLine(d);

此工作的目标是允许的函数使用结构,并启用后续优化 (如复制 prop 内联的因此。 执行该方法是选择满足特定的试探法的结构 (如不超过四个的字段的字段是基元类型或引用,等),"提升域在的编译器中的内部的局部变量,并利用标准的优化已启用为基元类型和引用的事实。 因此,提升结构上的所有操作将都更改以反映操作单个"字段局部变量" 在博客上可找到此的更多详细信息 JIT、 NGen,和其他托管代码生成内容. 调整 inliner 试探法之后, 我们最终更好的代码质量和较低的 NGen 时间使此工作已赢得的 Win。 注意在 64 位 JIT inliner 不同,并不在 3.5 SP 1 中极大地更改 ; 但是,我们已经做出几个的改进,它自的应该可以在即将发布的.NET Framework 4.0 版本。

除了该 inliner 工作 3.5 SP 1 中的两个 JIT 编译器目前更好地在传播 (什么叫为 True) 的声明在生成代码又会导致更好的代码质量因清除的空检查 TypeOf 检查,对常量、 变量和之间的比较的比较。

摆 JIT 编译器可以现在也正分支的预测分支更有可能或不执行。 因此,如果采取的分支应为常用比在秋季-通过代码路径,贯穿的块移动进一步向下,分支目标在块之后。 (分支的条件也反转,当然)。 straightening 分支以这种方式帮助分支预测 (正向分支机构通常不执行任何分支历史记录时假定) 并提高了缓存位置。 静态预测某些分支的结果时 (异常的代码路径假定不能执行,例如),我们还使用前面提到的配置文件数据,以便分支 straightening。 此工作自己导致大于 10%吞吐量我们 ASP.NET 的一些改进比基准。

尽管这不是通过任何方式的 JIT 优化,我们做 32 位 JIT 编译器,3.5 SP 1 中进行另一个重大的更改。 遍为托管调用的堆栈,EBP 链可以现在被历 (通过一个调试程序或事件探查器) 来确定哪些功能被称为。 这只是意味着 JIT 编译器不再 EBP 用作草稿的注册,并它使用以独占方式来存储框架指针。 注意,JIT 编译器不能保证它会推或从 (以避免的这样小的方法的性能开销) 的每个函数调用堆栈中弹出 EBP。 因此,现在遍历 EBP 链可以被调用的方法的顺序的列表,但可以在列表中缺少的方法。 确定哪些方法生成的 EBP 帧,所以应该相当容易确定完整的列表提供程序的了解调用关系图时,我们做选择保守的试探法。 当然,在调试模式下我们推或弹出 EBP 寄存器,为每个函数调用。 要启用内核模式分析器以遍历堆栈,运行托管应用程序时不能安全地使用我们托管的堆栈遍历 API 的主要动机,本文后面。

NGen 和 ASLR

我们进行生成,并写出 3.5 SP 1 中的 NGen 映像基础结构的一个重要改进。 它已更有效、 消耗内存较少,生成是更紧凑的图像。 这导致 NGen 时间超过 100%(当与使用旧的 NGen 基础结构的一个运行时) 中的改进和大约在 10%改进的一些大型应用程序 (如表达式 Blend 冷启动时间。 启动时间改进是由结构将保留在 NGen 映像的数据被更紧凑的事实带来的副作用。 此工作改进.NET Framework 为在 Framework 中的程序集生成 NGen 映像所用设置时间以及,因为安装时的好部分。

从此 Service Pack 开始我们已还加入 NGen 映像到使用地址空间布局随机化 (ASLR),一种已添加到 Windows Vista (请参阅"中的 Windows 的安全功能 地址空间布局随机化,Windows Vista 中"). ASLR 依赖,有效地随机加载 PE 映像的能力在虚拟地址空间中的位置并且因此能够有效地解决了地址上引用图像本身中。 与非托管的图像不同的可以包含仅到位置在相同的映像中用于还包含对其他程序集的 NGen 映像中的代码 / 数据的直接引用的 NGen 映像的直接引用。 (请参阅我以前的文章中的硬绑定上一节" NGen 的性能优势.") 不能轻松地由逻辑,内核修补处理类外部引用。 因此,.NET Framework 3.5 SP 1 中更重要的工作之一涉及更改以消除这样的直接指针的使用不带放弃我们会获得我们引入了硬绑定在性能 wins NGen 映像格式。 间接引用),托管的代码通常包含大量它们属于成本不仅由于其他涉及,但也因为他们需要默认情况下更正的间接寻址级别。 (和写入间接寻址单元格的结果不能在进程之间共享的专用页中。 这些间接引用已 strewn 所有通过 NGen 映像 (并导致大量专用的页面) ; 因此投入比大量已位于重建所有间接寻址单元格是台、 一起,修补程序和导致写入在运行时间为可能的为几页的图像。 在结束,性能下降处于可衡量仅几热启动,吞吐量的方案但即使在这些情况下影响最小。

ASLR 工作之一是副作用的不再需要担心如何选择 noncolliding 基本地址在 NGen 映像 (请参阅"这一事实 NGen 的性能优势") 面向 Windows Vista 或更高版本的平台时。 (它是仍然在 Windows XP 和较早的平台上的一个问题但是。 在另一个的不相关的安全更改用我们 3.5 SP 1 工具集生成的所有托管应用程序是立即加入到 数据执行保护 默认情况下。

.NET Framework 3.5 客户端配置

在 NET Framework 3.5 SP 1,我们也试图解决一个其他主要问题我们已经有与 Framework 即,获取最终用户计算机上部署它。 我们决定专注于提供 leaner 包某些类型的应用程序所做的使用只 Framework 所提供的功能的子集的框架。 此包的焦点由于使开发人员能够编写客户端应用程序主要是基于 WPF 的应用程序我们称之为客户端配置文件。 请注意客户端配置文件是 True.NET Framework 全功能版的子集。

介绍一种拆分证明为更有挑战性的任务比我们想最初预想,才能在 Service Pack 版本中在 Framework 的新方法。 特别,我们已有一个"水平分割"的产品 ; 我们现在引入一个"垂直分割"。

有趣的方案涉及安装新的部分框架的顶部的旧框架的此潜在顾客,反之亦然。 要裁剪下的可能方案集,我们进行以下简化:

  • 可以仅在没有安装 (因为"干净"的 Windows XP 计算机上安装 Framework 是最大的获得认可点) 的任何基于 CLR 2.0 的 Framework 的计算机上安装客户端配置文件。
  • Framework 的旧版本无法安装客户端配置文件的顶部 (因此.NET Framework 2.0 中包括 ASP.NET 堆栈无法安装之上 3.5 SP 1 客户端配置文件中包含新的 CLR) 有只是不是为我们要测试的旧 / 新二进制文件的所有结果组合一个好方法。

测试客户端配置文件的顶部某些应用程序时,我们遇到另一个有趣的情况。 某些应用程序针对现有的.NET Framework 版本 (2.0/3.0/3.5) 似乎安装并运行客户端配置文件的顶部,(因为它们都包含相同的兼容 2.0 CLR),但然后失败试图调用不包含客户端配置文件中的 API 时 ! 做我们认为此问题的特定应用程序具有不查找安装程序时为该记录 Framework 的安装项中,我们快速实现这将影响所有应用 Xcopy 部署程序以及。 因而我们决定要阻止来自运行客户端配置文件的顶部,除非它们已显式标记为能够子集上运行的应用程序。 我们然后更改运行时能够确保非客户端应用程序正常,无法立即,和诊断错误消息 (请求最终用户安装完整.NET Framework 3.5 SP 1) 时它们未执行客户端配置文件上。

A 多目标客户端配置文件功能,它所支持,并且在找到符合等的子集的详细信息," .NET Framework 客户端配置文件"MSDN 上的文章。

向上覆盖

覆盖设置之前, 我想提到我们对运行库 3.5 SP 1 中, 大量用户的要求在过去的所做的其他更改,能够启动从本地 Intranet 共享的托管应用程序在完全信任而不必手动调整安全设置。 这是.NET 安全博客文章将进一步详细介绍" 在该 LocalIntranet FullTrust"但似乎值得提及以防您不知道有关还,这是内容所要长时间。

本文是只被为了使您了解我们对在.NET Framework 最新 Service Pack 版本 CLR 所做的更改。 请注意了大量对.NET Framework 以及 (概述可在的其他部分所做的更改 Scott Guthrie 的博客张贴内容. conclusion,我希望您现在兴奋有关尝试已经在进行中的每年的此版本。

如果任何问题或注释,不要尽发送我们的方式。

将您的问题和提出的意见发送至 clrinout@Microsoft.com.

Surupa 经理 是 CLR 团队在 Microsoft 的程序经理。 她适用于运行库的后端编译器,并主要重点 pre-compilation 的技术。