Какой дизайн виртуальной машины самый быстрый для x86? - PullRequest
4 голосов
/ 12 января 2009

Я внедрю виртуальную машину в 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:' или пример списка. Потребность в переводчике не просто исчезает, потому что вы всегда можете столкнуться с ситуацией, когда вы не можете быть уверены, что блок получит ожидаемое вами лечение.

Ответы [ 6 ]

4 голосов
/ 12 января 2009

Я вижу, что здесь есть некоторая путаница с переносимостью, поэтому я чувствую необходимость кое-что прояснить. Это мои скромные мнения, так что вы, конечно, можете возразить против них.

Полагаю, вы натолкнулись на http://www.complang.tuwien.ac.at/forth/threading/, если вы серьезно относитесь к написанию ВМ, поэтому я не буду останавливаться на описанных методах.

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

Учитывая динамический характер вашего примера, ваша ВМ будет напоминать JIT-компилятор больше, чем другие более популярные. Итак, хотя С.Лотт упустил из виду в этом случае, его упоминание о Форте очень на месте. Если бы я проектировал ВМ для очень динамичного языка, я бы разделил интерпретацию на два этапа;

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

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

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

Конечно, вы можете добавить произвольно сложные оптимизации на этапе 1.

2 голосов
/ 12 января 2009

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

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

1 голос
/ 20 января 2010

вы можете ускорить процедуру отправки с помощью некодированной инструкции, установленной на:

mov eax, [esi]
add esi, 4
add eax, pOpcodeTable
jmp eax

, который должен иметь служебную нагрузку <4 цикла для каждой отправки на процессоре Pentium 4. </p>

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

mov eax, [esi]
add eax, pOpcodeTable
jmp eax

~ 1-2 цилиндра над головой.

1 голос
/ 12 января 2009

Точка переводчика - это переносимость, большую часть времени. Самый быстрый подход, который я могу придумать, - это генерировать код x86 в памяти напрямую, как это делают компиляторы JIT, но тогда, конечно, у вас больше нет интерпретатора. У вас есть компилятор.

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

0 голосов
/ 12 января 2009

Если вам не нравится JIT и ваша цель - не переносимость. Я думаю, что вы можете заинтересоваться проектом Google NativeClient . Они занимаются статической аналитикой, песочницей и другими. Они позволяют хосту выполнять инструкции RAW x86.

0 голосов
/ 12 января 2009

Я должен спросить, зачем создавать виртуальную машину с акцентом на производительность? Почему бы просто не написать код x86 напрямую? Ничто не может быть быстрее.

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

...