为什么.net使用JIT编译器而不是仅在目标机器上编译一次代码?

时间:2010-10-01 20:04:07

标签: c# .net jit

标题几乎总结了一下,但我想知道为什么像.net这样的系统每次运行时都会编译代码,而不是只在目标机器上编译一次?

6 个答案:

答案 0 :(得分:25)

使用.NET或Java等中间格式可以获得两件事:

  1. 您可以在任何平台上运行该程序,因为代码是以中间格式而不是本机代码表示的。你只需要为中间格式编写一个解释器。
  2. 它允许在编译时不能(轻松)实现某些运行时优化:例如,您可以利用新CPU上的特殊功能,即使编写程序时这些CPU不存在也是如此 - 只有JIT编译器需要知道这一点。
  3. 现在,至于为什么你可能不想在第一次运行时执行编译然后只是缓存它 - 这也可能有几个原因。

    如果你在启动之前编译,那么用户必须等待很长时间才能进行第一次运行 - 在那个时间点,你无法知道用户将实际使用什么。通过只编译你需要的东西,当你需要它时,你可以更快地开始,只是因为你没有多少工作要做,而且你没有存储很多用户永远不会使用的代码(对于大型程序可以是很多代码。)

    如果您开始跨会话缓存JIT代码,则需要跟踪已编译的内容然后将其存储到磁盘。对于大型程序,您可能需要从磁盘加载大量本机代码。磁盘I / O非常昂贵,因此等待磁盘可能需要更长的时间而不是重新JIT它。此外,您需要跟踪缓存可用的时间。如果硬件发生更改,您可能需要重新进行JIT以应用一些新的优化。如果程序发生变化,则不能使用任何旧的编译代码。如果运行时编译器发生更改,则可能已修复了安全漏洞,您需要重新编译以确保该漏洞不会保留在您的本机代码中。

    基本上,JIT编译器突然有很多工作要做(包括用于处理缓存的磁盘I / O),并且变得更加复杂和缓慢,从而降低了JIT的作用。

    现在,这并不意味着预编译某些程序集有时不会有利,正如Matthew Ferreira指出的那样,ngen工具可以做什么 - 但在一般情况下,它只是不值得这样做,因为JIT编译通常足够快。

答案 1 :(得分:6)

我不编写编译器或运行时,所以我不能肯定地说这个,但我不相信每次都是及时的。它只是意味着我们要等到我们真的需要编译,但是一旦为该实例完成编译,它就不会再次编译。

答案 2 :(得分:5)

如果将可执行文件移动到另一台计算机怎么办?如果为第一台机器的特定处理器编译了一次,它将无法利用新处理器上可用的(可能的)更高级或更有效的指令集。如果您真的想在目标计算机上编译一次,请考虑在应用程序安装期间使用ngen.exe

答案 3 :(得分:3)

JIT编译的另一个优点:10年或15年前编写的代码可以通过使用最先进的JIT运行来获得显着的性能提升,而不需要对代码进行任何更改。

答案 4 :(得分:2)

  

标题几乎总结了一下,但我想知道为什么像.net这样的系统每次运行时都会编译代码,而不是只在目标机器上编译一次?

.NET会将CIL的编译结果缓存到给定目标上的本机代码,但这并非易事:

  • 在从磁盘加载缓存数据和即时重新编译之间需要权衡。
  • 任何形式的运行时代码生成都需要能够即时编译。这不仅是System.Reflection.Emit的使用,还包括动态加载的程序集中的类型的统一,甚至在多态递归期间创建新的值类型。

答案 5 :(得分:0)

如果您直接编译为机器代码,那么您已针对特定系统:

Code --> Machine Code

但是,在.NET,Java等中,您所针对的是虚拟机。这将生成任何符合.NET的VM可以解释的代码,并依次将JIT转换为真实的机器代码。

所以,在.NET中:

Code --> IL --> VM (executing) --> JIT'd Machine Code

最初它看起来更复杂,但考虑针对多种架构:

Code(x86) --> Machine Code(x86)
Code(ppc) --> Machine Code(ppc)
etc... (1x code-set per target)

对战

Code --> IL --> VM(x86) (executing) --> JIT'd x86 Machine Code
                VM(ppc) (executing) --> JIT'd ppc Machine Code
                etc...

我的插图很粗略,但你会注意到一个.NET代码包可以在多个平台上定位和运行