今天,混合模式的编译系统采用的常用方法是识别频繁执行的或者叫热方法。热方法传递给JIT编译器以便编译成机器代码。然后,当解释器看见应用调用被编译过的方法时,它就会分发机器代码去执行。
这种面向方法的方法已经采用许多年了,但是仍然需要大量的开发方面的前期投资。直到能够编译一门语言中出现的所有功能时,这样的系统才能提高方法的执行效率。对重要的应用来说,这需要JIT编译整个语言,包含那些复杂的已经用高级虚拟指令体实现了的功能,例如那些方法调用,对象创建,异常处理。
1 Compiling Cold Code
因为一个方法被频繁执行,并不意味着方法内的所有指令也会频繁执行。实际上,热方法的部分区域可能是“冷的”,也就是说可能他们永远也不会执行。编译冷代码除了浪费时间外还有更多的隐含意味。除最高级别的优化外,分析冷代码可以证明哪些是热代码,除了这一点好除外,没有其他更多益处。一个更严重的问题是,冷代码增加了动态编译的复杂度。我们给出3个例子。
首先,对于延迟绑定语言如Java来说,冷代码很有可能包含对那些还没有绑定的外部符号的引用,因此当冷代码最终并没有运行时,生成的代码还有支持它运行的环境(Runtime)必须处理延迟绑定的复杂性。
第二,某些动态优化如果不运行Profiling信息是不可能做到的. Foremost amongst these is the optimization of virtual function calls. 因此没有Profiling信息,JIT只能为冷代码生成相对慢的保守代码。这个问题对弱类型(runtime typed)的语言如python来说变得更加重要,因为虚拟指令的操作数类型可能只有在运行时刻才能知道。没有运行信息,无论是动态还是静态的Python编译器都不能判别简单算数运算如加法的操作数是什么类型,是整形、浮点型还是字符串型。
第三,随着指令的执行,编译过的方法里以前认为冷的区域可能变热。第一次编译是作出的保守的假设现在对性能却构成了拖累。直接的方法就是再次编译这些冷代码,这样做就会对编译带来的优势造成了损害。而且,问题是如此复杂以至于对那些还在方法中执行的线程来说,它们需要做什么?或者未来如何返回到方法?