我试图理解在解码和调度解释以及线程解释中执行程序时的实际差异。
两者的例子都会有所帮助。
我理解Java字节码的工作原理以及汇编语言的工作原理。但是DDI和TI在哪里适合?
背景:Virtual machines: versatile platforms for systems and processes
答案 0 :(得分:18)
(注意:我假设“解码和发送”是指基于交换机的解释器。)
基于开关和线程解释器在运行时的区别基本上是执行的跳转次数。
在基于交换机的解释器中,指令在某个中心位置被解码,并且基于解码的结果,对处理解码指令的代码片段执行跳转。一旦该代码片段完成对指令的解释,它就会跳回到集中解码代码,该代码继续执行下一条指令。这意味着每个解释的指令执行(至少)两次跳转。下面的C代码说明了这样的解释器可能是什么样的:
typedef enum {
add, /* ... */
} instruction_t;
void interpret() {
static instruction_t program[] = { add /* ... */ };
instruction_t* pc = program;
int* sp = ...; /* stack pointer */
for (;;) {
switch (*pc++) {
case add:
sp[1] += sp[0];
sp++;
break;
/* ... other instructions */
}
}
}
在线程解释器中,解码代码不是集中式的,而是在处理指令的每段代码的末尾复制。这意味着一旦解释了指令,解释器就会解码下一条指令并立即跳转到它,而不是跳回到某些集中解码代码。实际上在ANSI-C中有效地实现线程代码实际上是不可能的,但是GCC的“计算goto”扩展在这方面非常有效。这是前一个解释器的线程版本:
void interpret() {
void* program[] = { &&l_add, /* ... */ };
int* sp = ...;
void** pc = program;
goto **pc; /* jump to first instruction */
l_add:
sp[1] += sp[0];
++sp;
goto **(++pc); /* jump to next instruction */
/* ... other instructions */
}
除了保存跳转之外,这样的线程解释器也更有效,因为现代CPU可以更好地预测复制的间接跳转(到下一条指令)。 Anton Ertl在his home page上发表了一些有趣的论文,特别是一本名为“高效口译员的结构和表现”的论文,其中上面的代码进行了修改。