Оптимальный цикл интерпретатора виртуальной машины / байт-кода - PullRequest
5 голосов
/ 11 апреля 2011

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

Основной цикл интерпретатора выглядит так:

while(true)
{
  uint8_t cmd = *code++;
  switch( cmd )
  {
    case op_1: ...; break;
    ...
  }
}

ВОПРОС: Есть ли более быстрый способ реализовать этот цикл без обращения к ассемблеру?

Единственный вариант, который я вижу, - это особенность GCC для использования динамического перехода с адресами меток. Вместо break в конце каждого случая я мог перейти непосредственно к следующей инструкции. Я надеялся, что оптимизатор сделает это за меня, но, глядя на разборку, он, очевидно, этого не делает: в конце большинства кодов операций повторяется постоянный переход.

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

Ответы [ 3 ]

3 голосов
/ 11 апреля 2011

Одна очень простая оптимизация состоит в том, что вместо switch / case / case / case / case / case,

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

switch(cmd)

просто выполните

array[cmd]()

Это означает, что у вас не слишком много команд.Кроме того, сделайте некоторую проверку, если вы не определите все возможные команды (возможно, у вас есть только 300 команд, но вы должны использовать 2 байта для их представления, поэтому вместо определения массива с 65536 элементами, просто проверьте, меньше ли командачем 301, и если это не так, не ищите)

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

В противном случае было бы полезно изучить хеш-таблицы, но я предполагаю, что у вас не так много команд, и в этом случае накладные расходы на выполнение хеш-функции, вероятно, обойдутся вам дороже, чем отсутствие переключения.(Или есть ОЧЕНЬ простая хеш-функция)

1 голос
/ 11 апреля 2011

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

0 голосов
/ 11 апреля 2011

Несколько очевидных оптимизаций, которые я вижу:

  1. Если вы не используете cmd где-либо, кроме switch(), тогда напрямую используйте косвенное указатель switch( *code++ ).Для более длинного цикла while(true) это может быть мало полезно.
  2. В switch() вы можете использовать continue вместо break.Потому что, когда continue используется внутри if/else или switch, компилятор знает, что выполнение должно перейти к внешнему циклу;то же самое не верно для break (относительно switch).Надеюсь, это поможет.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...