Таблица лидеров - JIT скомпилировано (чем ниже, тем лучше)
- es1024 - 81,2 балла (включая работающий компилятор!)
- Кит Рэндалл - 116 очков
- Элл - 121 очко
Таблица лидеров - Интерпретируется (чем ниже, тем лучше)
- Мартин Бюттнер - 706654 балла (где-то около 2 часов).
- криптих - 30379 баллов (97 секунд)
Ваша миссия, если вы решите принять ее, - написать наименьший возможный интерпретатор байт-кода / VM. ВМ / интерпретатор использует небольшую архитектуру CISC (операции могут различаться по размеру) с языком, указанным ниже. После завершения вы должны напечатать значение 3 регистров ЦП, чтобы доказать, что выводится правильный вывод (3 126 900 366).
составитель
Если вы хотите сделать свои собственные тесты, компилятор размещен ниже. Не стесняйтесь размещать свои тесты с вашим ответом.
"ВМ" Технические характеристики
Виртуальная машина имеет 3 32-битных целочисленных регистра без знака: R0, R1, R2. Они представлены в шестнадцатеричном виде как 0x00, 0x01 и 0x02.
Следующие операции должны поддерживаться:
Формат: [имя] [... операнды ...], [шестнадцатеричный код операции] [... операнды повторяются ...]
- LOAD [регистр] [4-байтовое значение], 0x00 [регистр] [4-байтовое значение]
- PUSH [регистр], 0x02 [регистр]
- POP [регистрация], 0x03 [регистрация]
- ADD [регистр, 1 байт] [регистр, 1 байт], 0x04 [регистр] [регистр]
- SUB [регистр, 1 байт] [регистр, 1 байт], 0x05 [регистр] [регистр]
- MUL [регистр, 1 байт] [регистр, 1 байт], 0x06 [регистр] [регистр]
- DIV [регистр, 1 байт] [регистр, 1 байт], 0x07 [регистр] [регистр]
- JMP [строка кода, 4 байта], 0x08 [номер строки кода 4 байта]
- CMP [регистр, 1 байт] [регистр, 1 байт], 0x09 [регистр] [регистр]
- BRANCHLT [строка кода, 4 байта], 0x0a [номер строки кода 4 байта]
Некоторые заметки:
- Вышеприведенные математические операции складывают значения двух регистров вместе, помещая вывод в первый регистр.
- CMP, оператор сравнения, должен сравнивать значения двух регистров и сохранять выходные данные в некотором внутреннем флаге (это может зависеть от реализации) для будущего использования в инструкциях ветвления.
- Если BRANCH вызывается до CMP, если не вызывается BRANCHEQ, «VM» не должна переходить.
- PUSH / POP неудивительно толкать или выталкивать числа из стека.
- Операторы Jump и Branch переходят к определенной операции (строке кода), а не к двоичному адресу.
- Операции ветвления не делают сравнения. Скорее, они берут вывод из последнего сравнения для выполнения.
- Операторы Branch и Jump используют систему индексации номеров строк, начинающуюся с нуля. (Например, JMP 0 переходит на первую строку)
- Все операции должны выполняться над числами без знака, которые переполняются до нуля и не генерируют исключение при переполнении целых чисел.
- Деление на ноль не допускается и, как таковое, поведение программы не определяется. Вы можете (например) ...
- Сбой программы.
- Завершите выполнение виртуальной машины и верните ее текущее состояние.
- Показать сообщение «ERR: Деление на 0».
- Завершение программы определяется как то, когда указатель инструкции достигает конца программы (можно предположить, что программа не пустая).
Выход Выходные данные должны быть именно такими (включая новые строки)
R0 3126900366
R1 0
R2 10000
Очки
Очки рассчитываются по следующей формуле:Number Of Characters * (Seconds Needed To Run / 2)
Чтобы избежать различий в оборудовании, приводящих к разному времени, каждый тест будет выполняться на моем компьютере (i5-4210u, 8 ГБ ОЗУ) на сервере Ubuntu или в Windows 8, поэтому старайтесь не использовать какое-то безумно-экзотическое время выполнения, которое компилируется только на Dual G5 Mac Pro с ровно 762,66 МБ свободной оперативной памяти.
Если вы используете специализированную среду выполнения / язык, пожалуйста, оставьте ссылку на нее.
- Для заинтересованных сторон я разместил тестовый код (написанный на C #) здесь: http://pastebin.com/WYCG5Uqu
Тестовая программа
Идея пришла отсюда , поэтому мы будем использовать несколько измененную версию их программы.
Правильный вывод для программы: 3 126 900 366
В С:
int s, i, j;
for (s = 0, i = 0; i < 10000; i++) {
for (j = 0; j < 10000; j++)
s += (i * j) / 3;
}
В коде: [R0 является представителем s, R1 из j, R2 из i]
LOAD R0 0
LOAD R2 0 <--outer loop value
LOAD R1 0 <--inner loop value
--Begin inner loop--
PUSH R1 <--push inner loop value to the stack
MUL R1 R2 <--(i*j)
PUSH R2
LOAD R2 3
DIV R1 R2 <-- / 3
POP R2
ADD R0 R1 <-- s+=
POP R1
PUSH R2
LOAD R2 1
ADD R1 R2 <--j++
POP R2
PUSH R2
LOAD R2 10000
CMP R1 R2 <-- j < 10000
POP R2
BRANCHLT 3 <--Go back to beginning inner loop
--Drop To outer loop--
LOAD R1 1
ADD R2 R1 <--i++
LOAD R1 10000
CMP R2 R1 <-- i < 10000
LOAD R1 0 <--Reset inner loop
BRANCHLT 2
В двоичном / шестнадцатеричном виде:
0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x02 0x00 0x00 0x00 0x00
0x00 0x01 0x00 0x00 0x00 0x00
0x02 0x01
0x06 0x01 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x03
0x07 0x01 0x02
0x03 0x02
0x04 0x00 0x01
0x03 0x01
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x01
0x04 0x01 0x02
0x03 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x27 0x10
0x09 0x01 0x02
0x03 0x02
0x0a 0x00 0x00 0x00 0x03
0x00 0x01 0x00 0x00 0x00 0x01
0x04 0x02 0x01
0x00 0x01 0x00 0x00 0x27 0x10
0x09 0x02 0x01
0x00 0x01 0x00 0x00 0x00 0x00
0x0a 0x00 0x00 0x00 0x02
Бонусные баллы (эффекты применяются мультипликативно) Например, если вы подходите для всех трех, это будет ((символы * 0.50) * 0.75) * 0.90
- Уменьшение на 50%, если интерпретатор на самом деле является JIT-компилятором
- Уменьшение на 25%, если применяется какой-либо цикл развертывания / значимой оптимизации.
- Уменьшение на 10% при расширении виртуальной машины
- BRANCHEQ [строка кода, 4 байта] (ветвь, если равно - код операции 0x0b)
- BRANCHGT [строка кода, 4 байта] (ветвь, если больше - код операции 0x0c)
- BRANCHNE [строка кода, 4 байта] (ветвь, если не равно - код операции 0x0d)
- ЗАГРУЗИТЬ [регистр 1] [регистр 2] (переместить значение регистра 2 в регистр 1 - код операции 0x01).
Disallowed
- Прекомпиляция тестового примера в программу запрещена. Вы должны либо принять байт-код из STDIN или из файла (неважно, какой).
- Возврат вывода без запуска программы.
- Любой другой способ обмануть требование виртуальной машины.
источник
CMP
Проверяет ли меньше или равенство? И что происходит с его результатом?MUL
иDIV
также не указаны. Должны ли они быть подписаны или не подписаны? Что происходит при переполнении умножения?Ответы:
C, 752 (589 + 163 для определения флагов) * 0,5 (JIT) * 0,9 (расширения) * (оптимизация 0,75) * (0,64 секунды / 2) = 81,216
Принимает код (
LOAD R0
и т. Д.), Без завершающих символов, одного пробела, без пустых строк в середине, без комментариев и т. Д. Требуется завершающий перевод новой строки.Затем он преобразуется в байт-код 80386 и выполняется.
Загрузка
0
в регистр заменяетсяxor
вводом самого регистра вместоmov
ввода0
в регистр, который на три байта короче в сгенерированном байт-коде и может быть очень незначительно быстрее.Компилировать с:
Требуется POSIX-совместимая ОС.
Входные данные читаются из STDIN (использовать
./bytecode < file
для передачи из файла).Результирующий байт-код для тестовой программы:
Ungolfed:
источник
C, оценка = 854 байта × (~ 0,8 с / 2) × 0,5 [JIT] × 0,9 [расширения] = ~ 154 байта сек
Компилировать
gcc vm.c -ovm -m32 -w
на POSIX-совместимой ОС x86.Запустите с
./vm < program
, гдеprogram
находится двоичный файл программы.Идем на скорости. Программа выполняет довольно простой перевод входной программы в машинный код x86 и позволяет процессору делать все остальное.
Например, вот перевод тестовой программы.
ecx
,esi
Иedi
соответствуютR0
,R1
иR2
, соответственно;bh
содержит флаги состояния;eax
иedx
скретч-регистры; стек вызовов соответствует стеку виртуальной машины:Ungolfed
Показать фрагмент кода
источник
CJam,
222187185 байт * (слишком медленно / 2)Я просто хотел посмотреть, насколько коротким я могу получить виртуальную машину с байт-кодом, написав ее на CJam. Меньше чем 200 байтов кажется довольно приличным. Хотя это чертовски медленно, потому что сам CJam интерпретируется. Чтобы запустить тестовую программу, нужны годы.
Чтобы запустить его, загрузите интерпретатор Java по этой ссылке на sourceforge , сохраните код
vm.cjam
и запустите его сПрограмма ожидает байт-код на STDIN. Я еще не нашел способа передать двоичные данные в программу без добавления в PowerShell завершающего переноса строки и преобразования
0x0a
в него0x0d 0x0a
, что действительно раздражает. Код включает 4 байта, чтобы исправить это (D-);
), который я не включил в общее количество, потому что это не то, что программа должна делать, если она фактически получала сам байт-код на STDIN, а не какую-то странно закодированную версию , Если кто-то знает решение для этого, пожалуйста, дайте мне знать.Слегка разгульный
Завтра я добавлю правильное объяснение.
Короче говоря, я храню все регистры, указатель инструкций и флаг сравнения в переменных, чтобы я мог оставить стек CJam свободным для использования в качестве стека виртуальной машины.
источник
python / c ++, оценка = 56,66
1435 символов * .234 / 2 секунды * .5 [JIT] * .75 [Оптимизация] * .90 [Дополнительные инструкции]
Компилирует входную программу в c ++, запускает на ней gcc, затем запускает результат. Большую часть времени проводят внутри GCC.
Единственная оптимизация, которую я делаю, - сводить операции стека к явным переменным, если это разрешено семантически. Это очень помогает, примерно в 10 раз лучше время выполнения скомпилированного кода (около 0,056 сек для фактического запуска полученного двоичного файла). Я не уверен, что делает gcc, что дает вам такое улучшение, но это хорошо.
Конечно, можно играть в гольф еще.
источник
Lua 5,2 (или LuaJIT), 740 байт
Первая попытка, только минимально игра в гольф. Эта версия работает (по крайней мере, в тестовой программе) и реализует дополнительные коды операций, но не поддерживает математические требования без знака и не особенно быстра. В качестве бонуса, тем не менее, это виртуальная машина, работающая на виртуальной машине, и написана так, что она может быть интерпретирована (запущена с PUC-Lua) или подобна JIT (запущена с LuaJIT; все еще интерпретируется, но интерпретатор теперь JITted).
РЕДАКТИРОВАТЬ: Гольф лучше, все еще большой.
РЕДАКТИРОВАТЬ: Исправлена серьезная ошибка, и теперь ограничивает арифметику в
unsigned long
диапазоне. Как-то удалось удержать размер от выхода из-под контроля, хотя, но он все равно дает неправильный ответ.РЕДАКТИРОВАТЬ: Оказывается, результат был правильным, но результат не был. Перешел на печать с
%u
вместо,%d
и все хорошо. Также отключены табличные регистры для переменных, чтобы несколько улучшить размер и скорость.РЕДАКТИРОВАТЬ: Используя
goto
заявление Lua 5.2 (также доступно в LuaJIT), я заменил интерпретатор на «JIT-to-Lua», генерируя код, который запускается непосредственно самой Lua VM. Не уверен, действительно ли это считается JIT, но это улучшает скорость.Вот оригинальная, читаемая версия.
источник
<
в своих циклах вместо<=
, поэтому окончательная инструкция перехода была исключена. Он все еще получает неправильный ответ, но теперь на это уходит несколько минут. :)C #
15051475 байтЭто моя версия интерпретатора, написанная на C #, я думаю, что она может быть оптимизирована / улучшена, но я не знаю где;)
версия для гольфа:
редактировать
убраны некоторые ненужные
public
иprivate
модификаторы:назовите его
executable.exe filename
гдеfilename
файл, содержащий код для интерпретацииМоя "тестовая программа":
Интерпретатор развлекается с более длинными именами переменных, классов, ...
источник