.NET Core 2.1中的分层编译(预览)

若是您是.NET性能的粉丝,最近有不少好消息,例如.NET Core 2.1中的性能改进宣布.NET Core 2.1,但咱们还有更多的好消息。分层编译是一项重要的新特性功能,咱们能够做为预览供任何人试用,从.NET Core 2.1开始。在咱们测试的许多场景中,应用程序启动更快,而且在稳定状态下运行得更快。一个在.NET Core 2.1上运行的项目,以及对环境变量或项目文件进行微不足道的更改以启用它。在本文的其他部分,咱们将介绍它是什么,如何使用它,以及为何它是2.1版本的隐藏技能!git

什么是分层编译?

从.NET Framework开始,代码中的每一个方法一般都编译一次。可是,在决定如何进行会影响应用程序性能的编译时,须要进行权衡。例如,JIT能够进行很是积极的优化并得到很好的稳定性能,可是优化代码并非一件容易的事情,所以您的应用程序启动速度很是慢。或者JIT可使用很是简单的编译算法,这些算法能够快速运行,所以您的应用程序能够快速启动,但代码质量会更差,而且应用程序吞吐量会受到影响。.NET一直试图采用一种平衡的方法,在启动和稳定性能方面作得很合理,但使用单一编译意味着须要妥协。github

分层编译功能经过容许运行时热交换技术对.NET进行屡次编译同一个方法改变了以上前提。两套机制的分离以便咱们能够选择最适合启动的技术,选择最稳定状态而且在二者上都表现出更好性能的第二种技术(分层编译)。在.NET Core 2.1中,这就是Tiered Compilation旨在为您的应用程序作的事情:算法

  • 更快的应用程序启动时间 - 当应用程序启动时,它会等待一些MSIL代码到JIT。分层编译要求JIT快速生成初始编译,若是须要,牺牲代码质量优化。以后,若是频繁调用该方法,则在后台线程上生成更优化的代码,并替换初始代码以保持应用程序的稳定性能。
  • 更快的稳定状态下的性能 - 对于典型的.NET Core应用程序,大多数框架代码将从预编译(ReadyToRun)映像加载这对于启动很是有用,但预编译的映像具备版本控制约束和禁止某些类型优化的CPU指令约束。对于常常调用的这些镜像中的任何方法,分层编译请求JIT在后台线程上建立优化代码,以替换预编译版本。

更快?到底有多快?

咱们将此做为预览版发布的部分缘由是要了解它对您的应用程序的执行状况,但如下是咱们对其进行测试的一些示例。虽然很是依赖于场景,但咱们但愿这些结果是您在相似工做场景上的典型表明,而且随着功能的成熟,结果将继续改进。基准测试是在默认配置下运行的.NET Core 2.1 RTM,而且全部数字都通过缩放,所以基准始终为1.0。在第一组中,咱们有几个Tech Empower测试和MusicStore(用来专门测试的项目),这是咱们经常使用的ASP.NET应用示例。json

 

虽然咱们的一些ASP.NET基准测试得益于特别好(MvcPlaintext RPS超过60% - 哇!),但分层编译并不特定于ASP.NET。如下是您在平常开发中可能遇到的一些示例.NET Core命令行应用程序:数据结构

你的应用程序将如何运做?测量比预测要容易得多,但咱们能够提供一些普遍的经验法则。app

  1. 启动改进主要适用于减小管理托管代码的时间。您可使用PerfView工具来肯定您的应用花费多少时间。在咱们的测试中,jitting花费的时间一般会减小约35%。
  2. 稳定状态的改进主要适用于CPU绑定的应用程序,其中一些热代码来自.NET或ASP.NET预编译库。例如PerfView能够帮助您肯定您的应用程序是这一类。

尝试一下

一个小免责声明,该功能仍然是一个预览。咱们已对其进行了大量测试,但默认状况下未启用此功能,由于咱们但愿收集反馈并继续进行调整。打开它可能不会使你的应用程序更快,或者你可能遇到咱们没有覆盖到的地方。若是遇到问题,微软随时为您提供帮助,您能够随时轻松将其禁用。若是您愿意,能够在生产中启用此功能,但咱们强烈建议您事先进行测试。框架

有几种方式能够选择加入此功能,全部这些方法都具备相同的效果:工具

  • 若是使用.NET 2.1 SDK 自行构建应用程序 - 将MSBuild属性<TieredCompilation> true </ TieredCompilation>添加到项目文件中的默认属性组。例如:

GitHub 连接可找到如下代码性能

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>netcoreapp2.1</TargetFramework>
      <TieredCompilation>true</TieredCompilation>
    </PropertyGroup>
</Project>
  • 若是运行已构建的应用程序,请编辑runtimeconfig.json以将System.Runtime.TieredCompilation = true添加到configProperties。例如:
  {
      "runtimeOptions": {
        "configProperties": {
          "System.Runtime.TieredCompilation": true
        }
      },
      "framework": {
        ...
      }
    }
  • 若是您想运行应用程序但不想修改任何文件,请设置环境变量
COMPlus_TieredCompilation=1

有关尝试和测量性能的更多详细信息,请查看分层编译演示测试

得到这个技术

好奇它是如何工做的?不要惧怕,理解这些内部细节不是使用分层编译所必需的,若是您愿意,能够跳过本节。一目了然,该功能可分为四个不一样的部分:

  • JIT编译器能够配置为生成不一样质量的汇编代码 - 令许多人惊讶的是,到目前为止,这还不是该功能的重点。回到.NET的起始,JIT支持默认编译模式和用于调试的无优化编译模式。正常模式产生更好的代码质量而且编译须要更长时间,而“无优化”模式则相反。对于分层编译,咱们建立了新的配置名称“Tier0”和“Tier1”,但这些配置生成的代码与咱们一直使用的“无优化”和“正常”模式大体相同。到目前为止,大多数JIT更改都涉及在请求“Tier0”代码时使JIT生成代码更快。咱们但愿未来继续提升Tier0编译速度,
  • CodeVersionManager(代码版本管理)跟踪同一方法的不一样代码编译(版本) - 最基本的是一个大内存字典,它存储应用程序中.NET方法之间的映射和不一样程序集实现的列表运行时可使用它来执行该方法。咱们使用一些技巧来优化这种数据结构,但若是你想深刻研究项目的这个方面,能够参考咱们提供的很是好的规范
  • 相同方法的不一样汇编代码汇编之间,在运行时状态下热更新的机制, - 当方法A调用方法B时,调用将依赖于jmp指令。经过调整运行时的jmp指令能够控制执行B的哪一个实现。
  • 决定要建立哪些代码版本以及什么时候在它们之间切换的策略 - 运行时始终首先建立Tier0,这是从ReadyToRun映像加载的代码,或者是使用最小化优化的代码。呼叫计数器用于肯定频繁运行哪些方法,并使用计时器来避免在启动期间过早建立Tier1的工做。一旦计数器和计时器都知足,该方法就会排队,后台线程会编译Tier1版本。有关详细信息,请查看规范

咱们从哪里开始?

分层编译创造了各类可能性,咱们能够继续充分利用将来的时间。既然运行时能够利用更极端的状况,那咱们就有了扩展边界的动力,既能够加快编译速度,又能够生成更高质量的代码。经过代码的运行时热更新,.NET能够进行更详细的分析,而后使用运行时反馈来进行更好的优化(配置文件引导优化)。这些技术能够容许代码生成器甚至超出没法访问配置文件数据的最佳静态优化器。或者还有其余选项,例如用于更好诊断的动态去优化,用于减小内存使用的可收集代码,以及用于性能检测或服务的热补丁。目前,咱们最直接的目标仍然接近实际 - 确保预览中的功能运行良好,响应您的反馈,并完成工做的第一次迭代。

总结

咱们但愿Tiered Compilation为您的应用程序提供与咱们的基准测试相同的重大改进,而且咱们知道还有更多还没有开发的潜力。试一试,而后访问github,向咱们提供反馈,讨论,提问,甚至能够贡献一些本身的代码。谢谢!

原文:.NET Core 2.1中的分层编译(预览)

相关文章
相关标签/搜索