Какой дизайн виртуальной машины самый быстрый для x86?

StackOverflow https://stackoverflow.com/questions/435141

  •  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.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top