HyperIris原创文章,谢绝转载
JIT这个概念在计算机科学中,常见于两种上下文:对一般程序员来说,经常见到的是Just-in-time debugging,也就是当程序崩溃时,操作系统会根据预先的配置调用调试器来对故障进行诊断和处理;对于虚拟机来说,JIT意味着另一种技术-dynarec。
dynarec是两个单词的缩写,即dynamic recompilation。顾名思义,动态重编译是一种编译技术,但是又和我们常见的编译器不一样,它是在代码的执行阶段动态的将一种代码的表示方式翻译成另一种,例如,常常是字节码翻译成本地机器码。
我们每天使用的编译器,绝大多数都是将文本形式的源代码编译成机器可以执行的本地代码或者是虚拟机的字节码。这种编译过程又被称为ahead-in-time compile。这个编译过程往往是很慢的,在研发阶段进行的。因为这种编译过程往往涉及到复杂的词法、语法、语义分析和优化以及代码生成。由于现代编程语言大都规模庞大,所以这个编译过程通常需要较多的时间。
在何时需要引入dynarec呢?对于传统编译语言来说,是不需要的。在基于虚拟机的语言中,常常纯粹的解释执行不能带来满意的执行速度,这个时候就需要dynarec来提高速度。也就是,虚拟机不再解释执行字节码而是根据某种规则,将字节码翻译成本地机器指令(native code),然后再执行。由于解释执行往往比本地代码慢一到两个数量级,所以使用dynarec能带来较大的性能改善。
注意,无论如何,dynarec这个过程也是需要时间的,如果每次执行一条字节码都要执行dynarec然后再执行生成的本地代码,这显然是得不偿失的,dynarec必须要和一定的缓存策略配合才能真正起到作用。也就是说,我们将按照一定的策略,比如按照一定的字节码数,或者以函数为单位整体进行dynarec,然后将生成的本地代码缓存起来。这样,在最理想的情况下,dynarec只需要一次,当字节码下一次被执行的时候,虚拟机可以直接执行已经翻译的本地代码。
如果生成字节码的高级语言编译器能够在生成的字节码流之中或者其他地方提供一些额外的信息,那么可以提高dynarec这个过程的性能。
那么,binary translation又是什么呢?一般定义上它所处理的是从一种体系结构的机器指令到另一种体系结构的机器指令的翻译。注意输入输出都是传统意义上的机器指令。
binary translation分为静态和动态两种,从理论上讲,我们可以编写一个“编译器”,把一种体系的可执行代码翻译成另一种体系,然后永久保存下来,比如说我们有一个windows x86平台的文字处理程序,可以用这种“编译器”将它编译成linux powerpc平台的可执行程序。但是一般来说这仅仅是理论上可行,实际实现难度极大,以后我将另行撰文说明。动态的binary translation是dynarec的一个特殊分支,它的输入不是字节码,而是本地代码。