Какой метод отправки виртуальной машины является более эффективным?

Какой метод диспетчеризации был бы более эффективным для того, чтобы мои времена выборки-декодирования-выполнения были немного быстрее?

Для простоты я свел это к минимуму, например, операции оперируют однобайтовыми операндами, а их, например, всего два.

Метод, который я использую в данный момент (упрощенно):

typedef unsigned char byte;

vector<byte> _program = { INST::PUSH, 32, INST::POP};

enum INST {
PUSH =0, /*index=0*/
POP =1, /*index=1*/
}

//DISPATCHING METHOD #1
switch (curr_instruction) {
case INST::PUSH: {
/*declared inline*/ _push_to_stack(_program[instr_ptr+1]);
}
case INST::POP: {
/*declared inline*/ _pop_stack();
}
}

ИЛИ используя таблицу указателей на функции для выполнения каждой инструкции в «программе» (вектор байтов / vector _program), вот так:

typedef void (*voidptr)();

void hndl_push(){
/*declared inline*/ _push_to_stack(_program[instr_ptr+1]);
}
void hndl_push(){
/*declared inline*/ _pop_stack();
}

funcptr handlers[2] = {&hndl_push /*index=0*/, & hdnl_pop /*index=1*/}'
vector<byte> _program = { INST::PUSH, 32, INST::POP};

size_t instr_ptr=0;

//DISPATCHING METHOD #2
while (instr_ptr != _program.size()){
instr_ptr++;
_handlers[instr_ptr]();
}

Я использую компилятор VC ++ (Visual Studio), версия 2015 года.

Какие из них преобразуются в более эффективный ассемблер с наименьшими накладными расходами или они одинаковые?

Заранее спасибо!

2

Решение

Единственный способ узнать, что будет быстрее — это измерить.

Оптимизатор может сделать довольно много с любой техникой. Операторы плотного переключения часто сводятся к таблице переходов, а вызовы функций могут быть встроенными, так что мог будь самым быстрым подходом.

Но если по какой-либо причине оптимизатор не может встроиться или если оператор switch становится каскадом операторов if-else, то вызовы указателя функции могут быть быстрее.

В Википедии есть достойная статья о резьбовой код, который описывает различные методы для отправки кодов операций в виртуальной машине или интерпретаторе.

5

Другие решения

Как второе решение может быть быстрее первого, но лучше всего компилятор может преобразовать второе в первое в любом случае.

Как примечание вы должны изменить указатель программы в зависимости от кода операции.

0

Косвенное предсказание ветвления сложно, но, по крайней мере, есть только одна безусловная ветвь. Предсказатель ветвления должен правильно предсказать целевой адрес ветвления, чтобы поддерживать конвейер загруженным.

Однако непредсказуемая условная ветвь тоже плохая. При достаточном количестве случаев одна непрямая ветвь будет работать лучше, чем несколько условных ветвлений, так что именно это делают компиляторы. Всего в двух случаях вы почти наверняка получите лучшие результаты, если компилятор выберет способ реализации переключателя.

Условные предикторы ветвления в некоторых процессорах могут лучше распознавать простые (но нетривиальные) шаблоны, чем косвенные предикторы ветвления, но ЦП семейства Intel SnB, по крайней мере, могут распознавать некоторые шаблоны целевого адреса для косвенных ветвлений. (Микроарх Агнера Фога pdf имеет немного информации о предикторах ветвления.)


Увидеть Таблица поиска и переключатель в C встроенного программного обеспечения, включая мой ответ на этот вопрос, где я опубликовал некоторые результаты компилятора x86.

Обратите внимание на тот факт, что clang будет использовать таблицу переходов для перехода к call инструкции, а не помещать указатели функций в массив. Так когда таблица переходов является правильным выбором, реализация ее самостоятельно с помощью массива указателей функций сделает код лучше, чем текущий clang.

Ваш код точно такой же случай: отправка множеству функций, которые все имеют так же прототип и (отсутствие) аргументов, поэтому они могут входить в массив указателей на функции.

0
По вопросам рекламы [email protected]