.NET CLR JIT是否每次都编译每个方法?

27

我知道Java的HotSpot JIT有时会跳过对方法的即时编译,如果预计编译的开销低于在解释模式下运行该方法的开销。 那么.NET CLR是否基于类似的启发式算法工作呢?

4个回答

44
注意:这个答案是基于“每次运行”的上下文。代码通常在每次运行程序时进行JIT编译。使用ngen.NET Native也会改变这个情况...
与HotSpot不同,CLR JIT每次运行都只编译一次,它从不解释,并且永远不会根据实际使用重新编译更重的优化。
当然,这可能会改变,但自v1以来一直如此,我不指望它很快会改变。
优点在于它使JIT变得简单得多 - 不需要考虑已经运行的“旧”代码,撤销基于不再有效的前提条件的优化等等。
.NET的一个优点是大多数CLR语言默认将方法设置为非虚拟的,这意味着可以完成更多的内联。HotSpot可以内联方法,直到首次被覆盖为止,此时它会撤消优化(或者在某些情况下进行一些聪明的操作,根据实际类型有条件地仍然使用内联代码)。由于没有太多的虚拟方法需要担心,因此.NET可以基本忽略无法内联任何虚拟内容的痛苦。
编辑:以上描述了桌面框架。Compact Framework会在需要时抛弃原生代码,然后根据需要进行JIT编译。但是,这仍然不像HotSpots自适应优化。
微型框架显然没有进行JIT编译,而是解释代码。这对于资源受限的设备来说是有意义的。(我不太了解微型框架。)

4
即使如此,我并不认为它实际上会解释代码——调试器能够适当地遍历编译后的代码,仅此而已。但 Mono 确实有一个解释器。 - Jon Skeet
2
有趣的是,.net MF(嵌入式设备的MicroFramework)解释IL代码而不是编译它。 - Dejan Stanič
5
桌面版CLR不会卸载已编译的代码,因此它只编译一次方法。.NET Compact Framework中的CLR可以卸载代码并在需要时重新进行即时编译,以适应资源更有限的环境,这是紧凑CLR运行的环境。 - Curt Nichols
@EProgrammerNotFound:看看我在前几段中的编辑是否足够。感谢你提出这个问题。 - Jon Skeet
你好Jon,你的回答是否考虑了应用程序域、代码访问安全性,并且对于新的.NET框架(如4.0、4.5和4.6),这仍然适用吗?因为在撰写此答案时,这些框架还不可用。我尝试提出一个新问题,但它被关闭为重复的https://dev59.com/uZ_ha4cB1Zd3GeqPuzBh - Thomas Weller
显示剩余5条评论

15

.NET运行时在执行代码之前总是进行JIT编译,因此它永远不会被解释。

您可以在CLR Design ChoicesAnders Hejlsberg中找到更多有趣的阅读材料。尤其是这部分内容:

我看到微软决定IL始终都会被编译,从不被解释。那么如何在指令中编码类型信息有助于解释器更高效地运行呢?

Anders Hejlsberg: 如果解释器只需要盲目地按照指令执行而无需跟踪堆栈顶部的内容,它就可以更快。例如,当它看到iadd时,解释器不必首先弄清楚这是哪种加法,它知道这是一个整数加法。假设有人已经验证过堆栈的正确性,这样就可以节省一些时间,并且对于解释器来说,这很重要。然而,在我们的情况下,我们从未打算针对CLR进行解释执行的场景。我们打算始终使用JIT [即时编译],为了JIT的目的,我们需要跟踪类型信息。由于我们已经有了类型信息,将其放入指令中并没有带来任何好处。

Bill Venners: 许多现代JVM [Java虚拟机]都进行自适应优化,它们从解释字节码开始。他们在应用程序运行时对其进行分析,找出80%至90%的代码中执行10%至20%,然后将其编译为本地代码。尽管如此,它们不一定会即时编译这些字节码。一个方法的字节码仍然可以在后台被解释器执行时编译为本地代码并进行优化。当本地代码准备就绪时,它可以替换字节码。通过不针对解释执行场景,您是否完全排除了在CLR中采用这种执行方法的可能性?

Anders Hejlsberg: 不,我们并没有完全排除这种可能性。我们仍然可以解释代码,只是我们没有为解释而优化。我们没有针对编写最高性能的解释器进行优化,这种解释器只会解释代码。我认为现在已经没有人会这样做了。10年前,对于一个机顶盒来说,这可能是有趣的。但现在已经不再有趣了。JIT 技术发展到了一个地步,你可以有多种 JIT 策略可选。你甚至可以想象使用一个快速的 JIT 进行快速编译,然后当我们发现正在频繁执行某个特定的方法时,使用另一个 JIT 进行更好的优化并花费更多时间。在 JIT 方面还有很多可以做的。


3
在未来,对于内存较低的设备,有一些基于跟踪的JIT编译器将是很好的选择。它们将主要进行解释、查找热点,并将其转换为汇编代码并缓存起来。我认为这就是Google在其Android JIT中所做的事情,而Microsoft Research正在进行基于跟踪的JIT的研究项目。
我发现了一篇文章,SPUR: A Trace-Based JIT Compiler for CIL。也许有些内容将来会被整合到CLR中?

0

我不这么认为,也不认为它应该这样做。

JIT如何知道特定方法将被调用多少次?解释的频率不会影响决策吗?

我还要质疑JIT编译器能否很好地分析函数,以确定是否最好进行解释而不解释函数本身。鉴于这个事实(至少已经执行了一遍该方法),是否更好地编译每个方法以减少试图确定首先编译哪些方法的开销?


@Andrew - 运行时性能指标可以告诉您是否应该进行JIT编译,以及应该以多大的程度进行JIT编译(因为某些JIT优化比其他优化更耗时)。 - jsight
只是一个想法(我怀疑HotSpot不是这样工作的)-您可以在生成字节码时静态地分析一些内容(例如方法的长度),然后做出决定,然后只需使用“不编译”位标记此方法,因此JIT将知道它不应该在需要时编译它,而是回退到解释器。 - Marcin Deptuła
3
HotSpot可以在原地替换代码,即使该代码正在堆栈上执行。因此,一个循环可以开始于解释模式,但一旦重新编译完成,则可以在JIT编译模式下完成。 - jsight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接