2015-06-03 4 views
1

Я пытаюсь оптимизировать ветку (как переключатель ... case like) на своем максимуме, чтобы эмулировать X-процессор на процессоре x86. Я думал об этом: В памяти я загружать блоки x86 Opcodes с фиксированной длиной 0x100 байт, как это:Может ли этот сдвиг/скачок быть быстрее, чем switch ... case statement?

first block 
0 
...[my code, jump at 0x10000, nop nop nop until 0x9F...] 
0x9F 
second block 
0x100 
... 
019F 
third block 
0x200 
... 
0x29F 
... 
etc, etc 
... 
0x10000 

который будет конечным, начало в памяти $ 0 (+ возможно некоторое смещение) и конец в $ 0xFFFF (например, «rom» размером 0x10000). Теперь каждый раз, когда XC-процессор opCode извлекается и эмулируется, я сделаю это: сдвиньте его на 8 бит и перейдите в это место. Выполните это и продолжайте мой программный поток нормально. Мои вопросы: 1) Возможно ли быть настолько плотным с этими блоками opCode? 2) Это была обычная практика в прошлом?

+0

_ «Разве это возможно быть настолько плотным с этими блоками opCode?» _ Это нам трудно понять. Во всяком случае, коммутатор/регистр с случаями, идущими от 0..255, скорее всего, будет оптимизирован компилятором на косвенный скачок с номером случая как индекс в таблицу переходов. Вы можете изучить выход сборки из своего компилятора, чтобы убедиться, что вам стоит попробовать оптимизировать что-либо. – Michael

+3

Хотя это, безусловно, возможно, компилятор просто сохранит таблицу адресов прыжка, проиндексирует ее с помощью оператора switch и косвенно коснется. Ваш путь * может быть несколько быстрее, но он отнимает память (если только ваши заглушки на самом деле не нуждаются в 0x100 байтах), а * реальная * стоимость - это переход в любом случае, так как ему нужно предсказать предсказание цели, которое имеет тенденцию сосать. – EOF

+0

да, они наверняка нуждаются во всех блоках 0x100bytes от 0 до 0x10000, и это всего лишь фиксированный код 64kb, поскольку он действует как «rom». Но какой прогноз? Все это определено, поскольку они являются фиксированными блоками с одинаковой длиной. Но хорошо. Теперь я вижу, что я дезинформировал всех. Я отредактирую вопрос. Это «НРАВИТСЯ» для случая с переключателем. – venge

ответ

1

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

Если работа по эмулированию кода операции является справедливым размером, то этот разрыв трубопровода может не иметь большого значения. Я подозреваю, что это так; код операции «load register» имитируется, по существу, только считыванием памяти, что не так много работы.

Вы можете приобрести некоторые видимые улучшения, добавив специальные тесты непосредственно перед блоком коммутатора, которые проверяют два или три наиболее часто встречающихся кода операций (возможно, LOAD, CMP, JMP условный) [Если opcode = JMP then ...] Эти тесты CPU могут обычно хорошо предсказывают. Если вы это сделаете, измерьте, измерьте, измерьте.

Смарт-трюк заключается в том, чтобы амортизировать расходы на разрыв трубопровода по нескольким инструкциям, если сможете. Если машина имеет много однобайтовых кодов операций, вы можете рассмотреть возможность перехода к следующему двум байтам кода порядка 65536. (Теперь вам нужно закодировать много корпусов коммутаторов, но многие из них - это по сути то же самое. Интересно, может ли ваш компилятор справиться с этим?) В реферате это сокращает затраты на разрыв трубопровода в два раза. Для конкретной машины с очень регулярным набором инструкций это может не купить вас много.

Однако у вас может быть не много однобайтовых кодов операций, но вам может потребоваться декодировать один или несколько байтов для каждой команды. X86 подобен этому (префикс, код операции, MODRM, SIB, смещение ...). Большой случай переключения должен работать очень хорошо для этого.

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

Последняя рекомендация: измерить, измерить, измерить.

+0

Я бы попытался сохранить код как можно меньше, чтобы как можно больше хранить кеш. –

+0

Если у вас есть 65536 «строк кэша» интерпретатора опкодов, по 64 байта каждый, вы используете только около 1 Мб кеша. Это достаточно мало, поэтому большая часть из них останется в кеше, оставив много кеш-памяти на современных процессорах для других элементов кэширования. –

+0

_ CPU не может предсказать хорошо_ - предсказание заключается в том, что я должен найти первое смещение первого блока после компиляции всего эмулятора? Потому что, даже если прыжок косвенный, на мой взгляд он хорошо определен (прыжок (код операции << 8 + смещение)). Я мог бы быть совершенно неправ. – venge