其他分享
首页 > 其他分享> > 乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 超前预编译AOT和即时编译JIT

乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 超前预编译AOT和即时编译JIT

作者:互联网

什么是即时编译JIT

在计算机领域,即时编译(Just In Time, JIT)(也是动态编译或运行时编译)是一种执行计算机代码的方式,它涉及在程序执行期间(运行时)而不是在执行之前进行编译。这可能包括源代码翻译,但更常见的是字节码翻译成机器码,然后直接执行。实现JIT编译器的系统通常会持续分析正在执行的代码,并确定代码的哪些部分从编译或重新编译中获得的速度会超过编译该代码的开销。

image

JIT编译是两种传统的机器码翻译方法的结合,即时间先期编译(AOT)和解释,并结合了两者的一些优点和缺点。粗略地说,JIT编译结合了编译代码的速度和解释的灵活性,以及解释器的开销和编译和连接(不仅仅是解释)的额外开销。JIT编译是动态编译的一种形式,并允许自适应优化,如动态重新编译和针对微架构的速度提升。解释和JIT编译特别适用于动态编程语言,因为运行时系统可以处理迟来的数据类型并执行安全保证。

设计原理

在字节码编译的系统中,源代码被翻译成一种被称为字节码的中间表示。字节码不是任何特定计算机的机器代码,并且可以在计算机架构之间移植。然后,字节码可以被虚拟机解释,或在虚拟机上运行。JIT编译器以许多部分(或全部,很少)读取字节码,并将其动态编译为机器代码,以便程序可以更快地运行。这可以在每个文件、每个函数甚至任何任意的代码片段上进行;代码可以在即将执行时被编译(因此被称为 "即时"),然后缓存并在以后重新使用而不需要重新编译

相比之下,传统的解释型虚拟机将简单地解释字节码,通常性能低得多。有些解释器甚至可以解释源代码,而不需要先编译成字节码,其性能甚至更差。静态编译的代码或本地代码在部署之前就已经被编译了。动态编译环境是指在执行过程中可以使用编译器。使用JIT技术的一个共同目标是达到或超过静态编译的性能,同时保持字节码解释的优势。在部署之前,解析原始源代码和执行基本优化的大部分"繁重工作"通常在编译时处理:从字节码到机器码的编译比从源代码编译快得多。部署的字节码是可移植的,与本地代码不同。由于运行时对编译有控制权,就像解释的字节码一样,它可以在安全的沙盒中运行。从字节码到机器码的编译器更容易编写,因为可移植的字节码编译器已经完成了很多工作。

JIT代码通常提供比解释器好得多的性能。此外,在某些情况下,它可以提供比静态编译更好的性能,因为许多优化只有在运行时才是可行的

由于JIT必须在运行时渲染和执行本地二进制映像,真正的机器码JIT必须有允许在运行时执行数据的平台,因此在基于哈佛架构的机器上使用这种JIT是不可能的;对于某些操作系统和虚拟机也是如此。然而,一种特殊类型的"JIT"有可能不针对物理机的CPU架构,而是针对优化的虚拟机字节码,其中对原始机器代码的限制占了上风,特别是当该字节码的虚拟机最终利用JIT来处理本地代码时。

性能表现

由于加载和编译字节码所需的时间,JIT在应用程序的初始执行中会造成轻微到明显的延迟。有时这种延迟被称为"启动时间延迟"或"预热时间"。一般来说,JIT执行的优化越多,它生成的代码就越好,但初始延迟也会增加。因此,JIT编译器必须在编译时间和它希望生成的代码的质量之间做出权衡。启动时间除了JIT编译之外,还可能包括增加的IO绑定操作:例如,Java虚拟机(JVM)的rt.jar类数据文件是40MB,JVM必须在这个上下文巨大的文件中寻找大量的数据。

Sun公司的HotSpot Java虚拟机所使用的一种可能的优化是把解释和JIT编译结合起来。应用程序代码最初是被解释的,但JVM监控哪些字节码序列经常被执行,并将它们翻译成机器代码,以便在硬件上直接执行。对于只执行几次的字节码,这可以节省编译时间并减少初始延迟;对于经常执行的字节码,JIT编译被用来在缓慢解释的初始阶段后高速运行。此外,由于一个程序大部分时间都在执行其少数的代码,因此减少的编译时间是很重要的。最后,在最初的代码解释阶段,可以在编译前收集执行统计数据,这有助于进行更好的优化。

正确的权衡会因情况不同而不同。例如,Sun公司的Java虚拟机有两种主要模式--客户端和服务器。在客户端模式下,会进行最小的编译和优化,以减少启动时间在服务器模式下,会进行大量的编译和优化,通过牺牲启动时间,使应用程序运行后的性能最大化。其他的Java即时编译器使用方法执行次数的运行时间测量,结合方法的字节码大小作为启发式的决定何时编译。还有一个使用执行次数与循环的检测相结合。一般来说,在短时运行的应用程序中准确预测哪些方法需要优化比在长时运行的应用程序中更难。

微软的Native Image Generator(Ngen)是另一种减少初始延迟的方法。Ngen将通用中间语言(Common Intermediate Language, CIL) 镜像中的字节码预编译(或"预JIT")为机器本地代码。因此,不需要在运行时进行编译。与Visual Studio 2005一起携带的.NET框架2.0在安装后立即对所有的微软库DLLs运行Ngen。Pre-jitting提供了一种改善启动时间的方法。然而,它产生的代码质量可能不如JIT的代码,原因与静态编译的代码在没有剖析指导下的优化,在极端情况下不能像JIT编译的代码那样好的原因一样:缺乏剖析数据来驱动,例如,内联缓存。

也有一些Java实现将AOT(超前)编译器与JIT编译器(Excelsior JET)或解释器(GNU Compiler for Java)相结合

什么是提前编译AOT

什么是本机映像生成器NGen

本机映像生成器(Native Image Generator, NGen)是.NET框架的提前编译(AOT)服务。它允许对CLI程序集进行预编译,而不是让通用语言运行时(CLR)在运行时进行及时编译(JIT)。在某些情况下,执行速度将明显快于JIT。

本机映像生成器为当前环境(即操作系统)生成本机二进制映像这以牺牲可移植性和磁盘空间为代价消除了JIT开销;每当NGen生成的映像在不兼容的环境中运行时,.NET Framework都会自动恢复为使用JIT。对程序集运行NGen后,生成的本机映像将放置到本机映像缓存(NIC)中,以供所有其他CLI程序集使用。例如,这使得在安装时使用NGen处理CLI程序集成为可能,从而在以后最终用户在其系统上调用应用程序时节省处理器时间。

NGen旨在通过在运行时删除JIT编译过程来使程序集执行得更快,但这并不总能提高性能,因为某些优化只能由JIT编译器完成(例如,如果JIT编译器知道代码已经在运行)在完全信任的情况下,它可以跳过某些昂贵的安全检查)。由于这个事实,只有在对应用程序性能进行前后基准测试之后才使用NGen是有意义的

快速JIT

对于不包含循环且不可使用预编译代码的方法,快速JIT可以更快完成编译,但不会进行优化。

启用快速JIT会缩短启动时间,但可能会生成性能下降的代码。例如,代码可能会使用更多堆栈空间、分配更多内存并以更慢的速度运行。

如何开启快速JIT

方式一runtimeconfig.json

{
   "runtimeOptions": {
      "configProperties": {
         "System.Runtime.TieredCompilation.QuickJit": true
      }
   }
}

方式二*..csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TieredCompilationQuickJit>true</TieredCompilationQuickJit>
  </PropertyGroup>

</Project>

方式三,环境变量

COMPlus_TC_QuickJit=1
# 或者
DOTNET_TC_QuickJit=1

默认情况

包含循环的用法

改用System.Runtime.TieredCompilation.QuickJitForLoops

分层编译

分层编译会会将JIT实时编译器的方法拆成两个层级:

如何开启分层编译

方式一runtimeconfig.json

{
   "runtimeOptions": {
      "configProperties": {
         "System.Runtime.TieredCompilation": true
      }
   }
}

方式二*..csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TieredCompilation>true</TieredCompilation>
  </PropertyGroup>

</Project>

方式三,环境变量

COMPlus_TieredCompilation=1
# 或者
DOTNET_TieredCompilation=1

参考

标签:字节,编译器,代码,跨平台,编译,JIT,Net,运行
来源: https://www.cnblogs.com/taylorshi/p/16629071.html