Подтвердить что ты не робот

Адрес лейблов (MSVC)

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

Мы исследовали вытягивание адреса метки каждого случая и сохранение его в потоке самого байтового кода, а не идентификатор команды, который мы обычно включаем. Если мы это сделаем, мы сможем пропустить таблицу перехода и сразу перейдем к расположению кода исполняемой команды. Это работает фантастически в GCC, однако MSVC, похоже, не поддерживает такую ​​функцию.

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

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

4b9b3361

Ответ 1

Единственный способ сделать это в MSVC - использовать встроенную сборку (которая в основном подталкивает вас к x64):

int _tmain(int argc, _TCHAR* argv[])
{
case_1:
    void* p;
    __asm{ mov [p],offset case_1 }
    printf("0x%p\n",p);
    return 0;
}

Если вы планируете делать что-то вроде этого, лучшим способом было бы написать весь интерпретатор в сборке, а затем связать его с основным бинарником через компоновщик (это то, что сделал LuaJIT, и это главная причина VM настолько ослепительно быстро, что не работает JIT-код).

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

Ответ 2

Посмотрите, что делает Erlang для создания на Windows. Они используют MSVC для большей части сборки, а затем GCC для одного файла используют расширение label-as-values. Полученный объектный код затем взломан, чтобы быть совместимым с компоновщиком MSVC.

http://www.erlang.org/doc/installation_guide/INSTALL-WIN32.html

Ответ 3

Кажется, вы могли просто перенести фактический код на функции, а не на ярлыки. Затем байтовый код может быть тривиально преобразован в прямые вызовы. То есть байт-код 1 будет переведен на CALL BC1. Поскольку вы генерируете прямые вызовы, у вас нет накладных расходов на указатели на функции. Конвейеры большинства CPU могут следовать за такими безусловными прямыми ветвями.

В результате оптимизируются фактические реализации каждого байтового кода, а преобразование из байтового кода в машинный код является тривиальным преобразованием 1:1. Вы получаете немного расширения кода, так как каждый CALL составляет 5 байтов (при условии x86-32), но это вряд ли станет серьезной проблемой.