Какой дизайн виртуальной машины самый быстрый для x86?
-
10-07-2019 - |
Вопрос
Я внедрю виртуальную машину в x86, и мне интересно, какой дизайн даст лучшие результаты. На чем я должен сосредоточиться, чтобы раздавить сок? Я буду реализовывать всю виртуальную машину в сборке x86.
У меня не так много инструкций, и я могу выбрать их форму. Инструкции проецируются непосредственно в синтаксис smalltalk в блоках. Я выдал инструкцию, о которой думал:
^ ... # return
^null # return nothing
object # address to object
... selector: ... # message pass (in this case arity:1 selector: #selector:)
var := ... # set
var # get
Тип виртуальной машины, о которой я думал:
mov eax, [esi]
add esi, 2
mov ecx, eax
and eax, 0xff
and ecx, 0xff00 # *256
shr ecx, 5 # *8
jmp [ecx*4 + operations]
align 8:
operations:
dd retnull
dd ret
# so on...
retnull: # jumps here at retnul
# ... retnull action
ret:
# ... ret action
#etc.
Не начинайте спрашивать, зачем мне нужна еще одна реализация виртуальной машины. Интерпретирующие процедуры - это не просто материал, который вы берете в руки, когда он вам нужен. Большинство виртуальных машин, которые вы предлагаете в других местах, ориентированы на переносимость и снижение производительности. Моя цель - не мобильность, моя цель - производительность.
Причина, по которой этот интерпретатор вообще нужен, заключается в том, что блоки smalltalk в конечном итоге не интерпретируются одинаково:
A := B subclass: [
def a:x [^ x*x]
clmet b [...]
def c [...]
def d [...]
]
[ 2 < x ] whileTrue: [...]
(i isNeat) ifTrue: [...] ifFalse: [...]
List fromBlock: [
"carrots"
"apples"
"oranges" toUpper
]
Мне нужна реальная выгода от интерпретирующих подпрограмм, то есть выбора контекста, в котором следует читать программу. Конечно, хороший компилятор должен в большинстве случаев компилировать очевидные случаи, такие как: 'ifTrue: ifFalse' или 'whileTrue:' или пример списка. Потребность в переводчике не просто исчезает, потому что вы всегда можете столкнуться с ситуацией, когда вы не можете быть уверены, что блок получит ожидаемое вами лечение.
Решение
Я вижу, что здесь есть некоторая путаница с переносимостью, поэтому я чувствую необходимость кое-что прояснить. Это мои скромные мнения, так что вы, конечно, можете возразить против них. Р>
Полагаю, вы столкнулись с http://www.complang.tuwien.ac .at / forward / threading / , если вы серьезно относитесь к написанию виртуальной машины, поэтому я не буду останавливаться на описанных методах.
Как уже упоминалось, нацеливание на ВМ имеет ряд преимуществ, таких как уменьшение размера кода, уменьшение сложности компилятора (часто это приводит к более быстрой компиляции), переносимость (обратите внимание, что смысл ВМ - это переносимость языка , поэтому не имеет значения, если сама виртуальная машина не является переносимой).
Учитывая динамический характер вашего примера, ваша виртуальная машина будет напоминать JIT-компилятор больше, чем другие более популярные. Итак, хотя С.Лотт упустил из виду в этом случае, его упоминание о Форте очень на месте. Если бы я проектировал виртуальную машину для очень динамичного языка, я бы разделил интерпретацию на два этапа;
<Ол>Этап производителя, который обращается к потоку AST по требованию и преобразует его в более значимую форму (например, берет блок, решает, должен ли он быть выполнен сразу или сохранен где-то для последующего выполнения), возможно, представляя новые виды токенов. По сути, вы восстанавливаете контекстно-зависимую информацию, которая может быть потеряна при разборе здесь.
Этап потребителя выбирает сгенерированный поток из 1 и выполняет его вслепую, как и любой другой компьютер. Если вы сделаете это как Forth, вы можете просто протолкнуть сохраненный поток и покончить с этим, вместо того, чтобы перемещаться по указателю инструкции.
Как вы говорите, просто подражая тому, как этот проклятый процессор работает по-другому, вы не добьетесь нужного вам динамизма (или любой другой функции, стоящей, черт побери, например, безопасности). В противном случае вы будете писать компилятор.
Конечно, вы можете добавлять произвольно сложные оптимизации на этапе 1.
Другие советы
Если вы хотите что-то действительно быстрое, попробуйте использовать LLVM . Он может генерировать собственный код для большинства процессоров из описания программы высокого уровня. Вы можете использовать свой собственный язык ассемблера или сгенерировать структуру llvm, пропуская этап сборки, в зависимости от того, что вам удобнее.
Я не уверен, что это лучше для вашей проблемы, но это определенно то, что я бы использовал, если бы я выполнял некий критичный по производительности запуск кода, который не может быть скомпилирован с остальной частью программы.
Суть интерпретатора заключается в переносимости, в большинстве случаев. Самый быстрый подход, который я могу придумать, - это генерировать код x86 в памяти напрямую, как это делают компиляторы JIT, но тогда, конечно, у вас больше нет интерпретатора. У вас есть компилятор.
Однако я не уверен, что написание интерпретатора на ассемблере даст вам наилучшую производительность (если только вы не гуру ассемблера и ваш проект не очень ограничен по объему). Использование языка более высокого уровня поможет вам сосредоточиться на более совершенных алгоритмах, скажем, для поиска символов и стратегий распределения регистров.
вы можете ускорить процедуру отправки с помощью некодированной инструкции, установленной на:
mov eax, [esi]
add esi, 4
add eax, pOpcodeTable
jmp eax
который должен иметь накладные расходы < 4 цикла для каждой отправки на процессоре & Gt; Pentium 4.
Кроме того, по соображениям производительности лучше увеличивать ESI (IP) в каждой примитивной подпрограмме, поскольку высока вероятность того, что увеличение может быть сопряжено с другими инструкциями:
mov eax, [esi]
add eax, pOpcodeTable
jmp eax
~ 1-2 цилиндра над головой.
Я должен спросить, зачем создавать виртуальную машину с акцентом на производительность? Почему бы просто не написать код x86 напрямую? Ничто не может быть быстрее.
Если вы хотите очень быстро переводить язык, посмотрите Forth . Их дизайн очень аккуратный и легко копируется.
Если вам не нравится JIT, и ваша цель - не переносимость. Я думаю, что вы можете заинтересоваться проектом Google NativeClient . Они делают статический аналитик, песочницу и другие. Они позволяют хосту выполнять инструкции RAW x86. Р>