mterp(Dalvik VM)如何组织其字节码解释循环?

时间:2012-11-13 13:07:46

标签: android performance interpreter

我正在研究Android Dalvik VM,当我在文件vm / mterp / out / InterpC-portable.cpp中读取mterp代码时会遇到一个问题。实际上它是dalvik vm的主要解释器循环来解释dex文件中的字节代码。如果我写了这个文件,我会选择一个这样的开关盒结构:

while (hasMoreIns()) {
   int ins = getNextIns();
   switch(ins) {
     case MOV:
       //interprete this instruction
       ...
       break;
     case ADD:
       ...
       break;
     ...
     default: break;
   }
 }

然而,mterp使用的与我的想法非常不同,它使用了一些神奇的代码(对我而言):

FINISH(0);

HANDLE_OPCODE(OP_NOP)
   FINISH(1);
OP_END

HANDLE_OPCODE(OP_MOVE)
   ...
OP_END

...

我谷歌并发现它似乎是一个修改过的“线程”式执行,它与switch-case风格不同,并且具有更好的性能,因为它删除了while循环中的分支操作。但我仍然无法理解这段代码以及为什么它在性能上更好。它如何找到解释器的下一个代码?

1 个答案:

答案 0 :(得分:7)

作为一个简短的指导,out目录中填充了预处理文件,如果您正在尝试找出代码,那么这不是我想要读的好东西。与InterpC-portable.cpp对应的源(本身)是portablec目录的内容。

就代码执行操作码调度的方式而言,您需要查看FINISHportable/stubdefs.cpp宏的定义:

# define FINISH(_offset) {                                                  \
        ADJUST_PC(_offset);                                                 \
        inst = FETCH(0);                                                    \
        if (self->interpBreak.ctl.subMode) {                                \
            dvmCheckBefore(pc, fp, self);                                   \
        }                                                                   \
        goto *handlerTable[INST_INST(inst)];                                \
    }

此宏在每个操作码定义的末尾使用,并用作switch (opcode)语句的等效项。简而言之,这将读取PC指向的指令代码单元 - inst = FETCH(0) - 从其中获取操作码 - INST_INST(inst) - 并将该操作码用作所有操作码的地址表的索引。地址直接用goto语句分支。

goto是一个“计算goto”,它是一个非标准的GCC扩展。您可以在GCC手册中阅读相关内容,您也可以在2008年我在Google IO上的Dalvik内部演示文稿中找到有关该主题的内容。(在https://sites.google.com/site/io/dalvik-vm-internals查找。)

我的演讲还谈到了这项技术的性能特征。简而言之,它节省了一些分支并且在分支预测方面起到了相对较好的作用。但是,有更好的方法来编写一个解释器(正如我在演讲中所介绍的那样,以及特定于CPU的Dalvik解释器实际上工作)。

对于更大的上下文,将字节码编译为本机CPU指令通常会导致执行速度比最精心调整的解释器更快,假设您有足够的RAM来保存编译结果。在Froyo中引入的基于跟踪的Dalvik JIT旨在进行权衡,其中使用适量的额外RAM来实现相当富有成效的性能提升。