DeCode и отправка интерпретация против резьбовых интерпретаций
-
27-09-2019 - |
Вопрос
Я пытаюсь понять практическую разницу во время выполнения программы в Декодирование и рассылка интерпретации и резьбовое интерпретация.
Пример обоих действительно поможет.
Я понимаю, как работает Java Bytecode и как работает языком сборки. Но где DDI и Ti вписываются?
Контекст: Виртуальные машины: универсальные платформы для систем и процессов
Решение
(Примечание. Я предположим, что по «декодированию и отправке» вы имеете в виду переводчик на основе коммутатора.)
Разница между выключателем и резьбовым интерпретатором во время выполнения, в основном, количество проведенных прыжков.
В интерпретатере на основе коммутатора инструкции декодируются в каком-либо центральном расположении, и на основе результата декодирования скачок проводится на кусок кода, который обрабатывает декодированную инструкцию. Как только этот кусок кода завершил интерпретацию инструкции, он переходит обратно в централизованный код декодирования, который выходит с следующей инструкцией. Это означает, что (по крайней мере) два прыжка выполняются на интерпретацию инструкции. Следующий кусок C-кода 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 «Computed 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 */
}
Помимо спасения прыжка, такие резьбовые переводчики также более эффективны, потому что реплицированный косвенный прыжок (к следующей инструкции) может быть прогнозирован лучше современными процессорами. У Anton Ertl есть некоторые интересные документы на его домашняя страница, Особенно тот, который называется «структурой и производительность эффективных переводчиков», из которых были адаптированы вышеуказанные кусочки кода.