2

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

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

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

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 года.

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

Спасибо заранее!

+1

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

+0

@HansPassant Но это быстрее? – SolaGratia

ответ

0

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

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

+0

Не знаю, поэтому я задал вопрос:] Кроме того, этот пример упрощен, но суть та же: switch-case, vs function pointer array dispatch. В моем примере не исключается обработчик op-кода, обрабатывающий счетчик программ. – SolaGratia

5

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

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

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

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

+0

Таблица перехода, по-видимому, является самым быстрым способом реализации диспетчеризации (ну, концептуально/не рассматривая реализации архитектуры), является ли последний пример так же быстро, как таблица переходов? Это был вопрос компилятора VC++. Благодарю. – SolaGratia

+1

У вызовов функций есть накладные расходы: нажатие параметров, пролог, эпилог, обработка исключений, указатели на рамки. В главном коде оптимизатор может устранить большую часть этих накладных расходов в идеальных условиях. Но вызов функций через указатели ограничивает возможности оптимизатора. Таким образом, он никогда не будет таким же быстрым, как скачок. C++ не дает прямого способа создать таблицу перехода, но простой оператор switch может скомпилировать в один. Так что я догадываюсь, что №1 быстрее, но единственный способ узнать - измерить, потому что многие факторы влияют на оптимизатор. –

0

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

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

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


См Lookup table vs switch in C embedded software, в том числе мой ответ на этот вопрос, где я разместил некоторый вывод x86 компилятор.

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

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

Смежные вопросы