Pull to refresh

Comments 48

Я предполагаю, что компилятор (опции компиляции) и железо играет даже больше, чем немалую роль. Виртуальная машина стековая, а на реальном железе вероятно, что часть стека виртуальной машины будет в регистрах храниться. Код машины обязательно надо посмотреть в ассемблерном виде после компилятора, чтобы достоверно знать, что происходит. Я еще думал, что «subroutined» будет медленнее из-за частых джампов к адресам процедур.
Я точно так же думал, что вызов процедур будет в лучшем случае так же быстр, как просто goto, но не быстрее. Но оказалось вот как на текущем железе/компиляторах.
В gcc есть оптимизация jump threading (-fthread-jumps). В gcc 5.1 она может трансформировать switched в threaded.
До GCC 5 у меня руки, увы, ещё не добрались. Но вообще очень интересно попробовать, конечно.
Попробовал. Вот сравнение данных от GCC 4.8.1-2ubuntu1~12.04 и GCC 5.1.0-0ubuntu11~12.04.2, всё прочее без изменений.

У вас графики в разном масштабе, сравнивать трудно. Я правильно понимаю, что за исключением аномалии ICC + predecoded, одни и те же программы оказались быстрее, когда были собраны GCC? Причем subroutined (самый быстрый из вариантов, которые еще не жертвуют читаемостью/портируемостью в угоду производительности) на ICC тормозит чуть ли не вдвое.
У вас графики в разном масштабе

Это было сделано отчасти намеренно, т.к. не ставилось задачи сравнивать результаты отдельных компиляторов, но только различные техники интерпретации. Ну и скрипт, запускающий Gnuplot, не хотелось затачивать на какой-то конкретный диапазон значений по Y-оси. Иначе вообще надо было бы все данные представлять на одной оси — тогда сравнивать было бы удобнее всего.

оказались быстрее, когда были собраны GCC?

Опять же, не совсем верно сравнивать компиляторы по тем данным и по той методике, которую использовал я. На уровнях -O2 они применяют совсем разные списки оптимизаций, т.е. -O2 одного может соответствовать -O1 другого, или наоборот. Кроме того, я много времени потратил на анализ производительности генерированного кода для GCC, а на ICC практически не смотрел, использовал его только для проверки корректности и портируемости.
Интерпретаторы — класс программ, анализирующих написанный на некотором входном языке текст по одной фразе (выражению, утверждению, инструкции, другой смысловой единице) за раз и немедленно выполняющих предписанные этой фразой действия. Этим они отличаются, например, от трансляторов, в которых фазы разбора входного языка и исполнения действий разнесены во времени.

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

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

Вторая причина — так написано в учебниках, как школьных по информатике, так и вузовских по теории трансляторов. Наверняка же у авторов тех учебников был повод написать именно так, а не иначе?

Причина третья — нам просто нужна общая категория, в которую попадут как интерпретаторы, так и компиляторы — как программ, обрабатывающих исходный текст другой программы, чтобы мы могли насладиться видом этой самой другой программы в работающем виде. И так получилось исторически, что эту самую общую категорию, включающую в себя как интерпретаторы, так и компиляторы — обозвали трансляторами.
Вторая причина — так написано в учебниках, как школьных по информатике, так и вузовских по теории трансляторов.

Почему-то не в тех, что приходилось держать в руках.

И так получилось исторически, что эту самую общую категорию, включающую в себя как интерпретаторы, так и компиляторы — обозвали трансляторами.

В параллельной вселенной — может быть.
Для любопытсва поискал нынешние учебные пособия для ВУЗ в гугле.

Вот, например, цитата из Молдованова О.В.
Языки программирования и методы трансляции: Учебное пособие. – Новосибирск/СибГУТИ, 2012
(взял первое же пособие, которое можно было полностью открыть, и которое содержит слово «интерпретатор»):

Кроме схожих между собой понятий «транслятор» и «компилятор», существует принципиально отличное от них понятие «интерпретатор». Интерпретатор, так же как и транслятор, анализирует текст исходной программы


Понятно, что существует два наполнения(смысла) слова «транслятор» — 1)как отдельный инструмент, видом которого является компилятор, но не интерпретатор, так и 2)блок, переводящий входную программу в некое представление, которое используется в дальнейшей работе. Это блок, обеспечивающий трансляциюсоставная часть как компилятора (который вид транслятора в смысле слова №1), так и интерпретатора.
Транслятор — то, что переводит из одного в другое (переводчик)

Интерпретатор — то, что переводит по мере выполнения (как переводчик по слуху при интервью иностранцев, который переводит по фразе)

Компилятор — то, что переводит единым куском (как переводящий книжки)

ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B0%D0%BD%D1%81%D0%BB%D1%8F%D1%82%D0%BE%D1%80
Интерпретатор — то, что переводит по мере выполнения (как переводчик по слуху при интервью иностранцев, который переводит по фразе)

В таком случае можно сказать, что транслятор является составной частью интерпретатора, вкупе со второй частью, обеспечивающей выполнение (неважно, есть ли промежуточный код, или испольуются прямые методы трансляции).
Перечитайте статью в википедии. Транслятор вообще любой переводчик из одного языка в другой. Интерпретатор — частный случай транслятора когда перевод осуществляется символ за символом — то есть выполнение по мере разбора. Компилятор, когда переводит целиком.
Статья в википедии — не единственный источник. Я привел выше для примера выдержку из российского учебного пособия, где четко указывается, что интерпретатор — не транслятор. Ниже — иное трактование из советской/российской литературы. Так учили и меня в середине 2000х.

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

Это лишь одна из трактовок. Другая — транслятор — составная часть интерпретатора, его блок. Или вообще отдельный инструмент. Видом которого интерпретатор не является.

Интерпретатор — частный случай транслятора когда перевод осуществляется символ за символом

В таком случае перевод во что мы осуществляем?

Терминология — очень, очень разная.
Я так понял, что автор опирается на книжку по редкому языку, которая ссылается на статью Ершова в части определений. Не очень понятно, где именно у Ершова даны эти определения oberon2005.oberoncore.ru/classics/ae1977.pdf не могли бы вы уточнить? Я там вижу в начале слово трансляция, причем оно не определяется, а рассказывается о его свойствах.

С моей классификацией согласны русская и английская википедии, и точно помню, что в советском учебнике информатики было так же.

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

Понятия не имею, на что опирается автор. Но именно такую классификацию (с выделением интерпретатора) я встречал повсеместно. Так же как и оба упомянутых мною смысла слова «трансляция».

Мне кажется, также, что сами термины имеют англоязычные корни и английские значения слов ближе к моей версии.

Я бы не сказал, что interpret — частный случай translate, если уж говорить об английских значениях.

Терминология в этой части очень неустойчивая. Например, я говорил ниже о встреченном описании ЦП как интерпретатора машинных команд.

И да — вы не ответили:
В таком случае перевод во что мы осуществляем?
> Я бы не сказал, что interpret — частный случай translate, если уж говорить об английских значениях.

Translator — переводчик ( slovari.yandex.ru/translator/en-ru )
Interpreter — устный переводчик ( slovari.yandex.ru/interpreter/en-ru )

> В таком случае перевод во что мы осуществляем?

В машинный код (в форме вызова предзаписанных его кусочков) так же как устный переводчик представляет целевой языке в виде звуковых волн.

Процессор, вероятно, переводит машкод в электрические сигналы на его выводах.
Interpreter — устный переводчик

О нет, это только один из смыслов, это бытовое понятие interpreter как устный переводчик. Но это следствие другого смысла — а это «толкователь». Т.е. переводчик — человек, который устно растолковывает, о чем там интурист говорит(те же яндекс-словари):

interpret [ɪnˈtɜːprɪt] Прослушать
Глагол

объяснять, толковать, интерпретировать

В качестве примеров применения «интерпретировать» приведена фраза:
«Его замечания были неправильно интерпретированы».

Т.е. интерпретатор — это толкователь. Обратите внимание на другие переводы: «раскрывать замысел, содержание (пьесы, музыкального произведения); передавать (настроение, переживания) ». «истолковывать, объяснять, расценивать ».

В машинный код (в форме вызова предзаписанных его кусочков) так же как устный переводчик представляет целевой языке в виде звуковых волн.

О нет, мы не получаем в интерпретаторе машинного кода. Мы получаем действия, соответствующие интерпретированной программе.

Вся разница как раз в этом — транслятор выдает нам код (область видимости — дело десятое). Интерпретатор же не создает кода, он выполняет действия.
Вы можете взять распечатку-выход (результат работы) транслятора и поиграть в процессор/ВМ — выполнить трассировку и получить результат работы программы. С результатом работы интерпретатора это невозможно — его р. — результат программы.

Процессор, вероятно, переводит машкод в электрические сигналы на его выводах.

Тогда ЦП — это по-вашему, транслятор?
> Тогда ЦП — это по-вашему, транслятор?

его можно рассматривать как

Вот еще источник: раздел терминологии в гостовской ЕСПД:

Интерпретатор — Программа или техническое средство, выполняющие интерпретацию. Примечание — Большинство интерпретаторов осуществляют интерпретацию программы путем последовательной интерпретации ее предложений.

Транслятор — Программа или техническое средство, выполняющие трансляцию программы.

Трансляция программы — Преобразование программы, представленной на одном языке программирования, в программу на другом языке и в определенном смысле равносильную первой

Компиляция — Трансляция программы с языка высокого уровня в форму, близкую к программе на машинном языке.
ЕСПД по большей части просто перевод стандартов ANSI/ISO (не помню точно, межд. стандарты). Помню, в предисловии сказано, что такой-то и такой-то стандартны разработаны «путем адаптации стандартов ANSI/ISO»
Интерпретатор — Программа или техническое средство, выполняющие интерпретацию. Примечание — Большинство интерпретаторов осуществляют интерпретацию программы путем последовательной интерпретации ее предложений.
Сепулькарии — устройства для сепуления.

Серьезно, где определение интерпретации? Без него определение интерпретатора неполное.
Это уже не нужно, потому что дано определение «трансляции» как переводу в другой язык. И оно не допускает вашей трактовки интерпретатора как частного случая.
Допускает. Тут же не написано, что выходная программа должна существовать как последовательность байт еще до своего же запуска?
Нет, не допускает — выход транслятора — программа «на языке». Интерпретатор не имеет выхода в виде программы на языке. Выход(вывод) интерпретатора — результат работы самой интерпертируемой программы.

Однако, это допускает одну из упомянутых мною трактовок — транслятора как составной части интерпретатора. (В этом случае, действительно, мы можем иметь внутреннее представление в виде какого-то промежуточного кода, который не выходит за пределы интерпретатора).
> результат работы самой интерпертируемой программы.

Программа-интерпретатор написана в терминах какого-то языка: машинного кода, API ОС другого. Соответственно она транслирует искодный язык в выходной. А действия получаются в результате интерпретации этих вызовов нижележащими слоями.
Соответственно она транслирует исходный язык в выходной.

Никоим образом. Определение ниже (пролистайте) четко говорит, что переводит только транслятор, интерпретатор — реализует программу непосредственно.

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

Частный случай транслятора — компилятор, выходные данные которого — программа на машинном языке.

Интерпретатор же — программа, входными данными которой также является текст программы на языке программирования, а выходными данными — результат работы интерпретируемой программы. И никакой программы ни на каком ЯП.

Например:
Транслятор.
Входные данные(Basic):
PRINT «HELLO WORLD»
Выходные данные©:
printf(«hello world\r\n»);

Или, опять же транслятор:
Входные данные(Basic):
PRINT «HELLO WOLRD»
Выходные данные(Assembler):
push offset hw_string
call PrintToConsole
call ExitProgram

Или, компилятор (частный случай транслятора):
Входные данные(Basic):
PRINT «HELL WORLD»
Выходные данные(Машинный язык):
0E 1B 20 FF FE FF FF 81 7B 4D

И, теперь, внимание:
Интерпретатор.
Входные данные(Basic)
PRINT «HELLO WORLD»
Выходные данные…
......


HELLO WORLD.
Интерпретатор.
Входные данные(Basic)
PRINT «HELLO WORLD»
Выходные данные…


HELLO WORLD


Чорт, мне придется пересмотреть свои взгляды. Я думал, это Hello, world появляется так же при некотором посредничестве операционной системы, аппаратуры и прочего, с которыми интерпретатор общается на каком-то языке. Оказывается, интерпретатор все делает сам :)
Вероятно, вы хотите сказать, что команда «распечатать строку» — выходные данные, которые интерпретируются процессором, но это неверно, вот почему:
Допустим, в теле интерпретатора есть последовательность 12 34 56h, которая означает «вывести на печать строку по адресу xyz». Эта последовательность на машинном языке не является выходными данными интерпретатора, потому что выходные данные — это те, что сформированы в ходе работы программы. (Даже если это полная печать своего, программы, тела. Или выдача части. Здесь же часть не выдается, она непосредственно выполняется ЦП. Выполняемое тело и части тела интерпретатора не являются выходными данными самого интерпретатора). Последовательность же 12 34 56h не сформирована в ходе работы интерпретатора, она сформирована компилятором в ходе компиляции самого интерпретатора. И, что важно, исполняется ЦП непосредственно (см. ниже определение машинного языка — для выполнения программы на оном не требуется дополнительных средств в виде к. т. или и. Т.е. машинный язык выделяется отдельно, как предел, и не допускается толкование ЦП как интерпретатора. ЦП определен как «техническое средство»).
Т.е. машинный язык выделяется отдельно, как предел, и не допускается толкование ЦП как интерпретатора. ЦП определен как «техническое средство»).


То есть как только мы используем виртуальную машину у нас компилятор перестает быть компилятором при этом ничуть не изменившись?
Кстати, еще одна проблема.

Компиляция — Трансляция программы с языка высокого уровня в форму, близкую к программе на машинном языке.
Ограбление века! У таких языков, как C# и Java, пропали компиляторы! :)
Это ещё что. Ведь есть такая вещь, как source-to-source compiler. Даже первый компилятор С++ транслировал свой вход в Си. При этом Страуструп явно подчёркивает в книге "Дизайн и эволюция языка С++", что это был не тупой макропроцессор, а именно компилятор.
Да нет, не пропали. Они же компилируют в машинный код виртуальной машины.
Тогда приведите еще и определение машинного кода. Иначе слишком широкое понятие получается.
Пожалуйста:
27. Машинный язык — Язык программирования, предназначенный для представления программ в форме, позволяющей выполнять ее непосредственно техническими средствами обработки информации.
Примечание. Для выполнения програм­мы на машинном языке не требуется примене­ние трансляторов, компиляторов и интерпрета­торов

Очевидно, что в случае с Java «техническим средством», которое непосредственно выполняет что-то, является виртуальная машина. Потому что мы вполне можем разработать ЦП, вполне себе такой чип, который будет выполнять Java байт-код. У нас же просто ВМ-эмулятор.

Кстати, нашел там и определение «интерпретации», которое я не привел выше:

Интерпретатор — Программа или техническое средство, выпол­няющие интерпретацию

Интерпретация — Реализация смысла некоторого синтаксически законченного текста, представленного на конкретном языке

Выше в одном из коментариев я использовал «выполнение действий», это и есть «реализация смысла» в терминах ЕСПД.

Тут можно затеять терминологический спор. Обычно они довольно длительны и бесплодны. Хочу лишь обозначить своё мнение по паре пунктов.

«Транслятор» и «компилятор» — термины-синонимы разных эпох. Первый компилируемый язык назывался FORTRAN — от Formula Translator. Потом мода сменилась, и стали использовать новое слово. Характер работы транслятора/компилятора при этом неважен — он может быть in-time, ahead-of-time, с машинного кода, языка высокого уровня, байткода.

Интерпретация и трансляция различаются. Но в чём?
В качестве первого признака можно брать размер входного блока. Для интерпретаторов он маленький, и они не могут видеть всю программу и, скажем, проводить оптимизации. Транслятор же за раз «переваривает» кусок побольше, и поэтому видит больше контекста и может провести более осмысленные преобразования. Однако не всегда легко выделить границу между малым и большим. Для системных ВМ граница — это машинная инструкция; а для языковых?
Вторым признаком можно считать разделение по времени процессов анализа входа и генерации выхода. Классические компиляторы пишут свой вывод на диск, и запуск результата может быть отложен на длительное время. Just-in-time трансляторы хранят результирующий код в ОЗУ, однако тоже могут использовать его значительно позже момента генерации. С другой стороны, предекодирование в интерпретации очень похоже — ведь результат работы предварительного распознавания может быть и вовсе не использован, если гостевая программа никогда не пойдёт по соответствующей ветке исполнения.
Третий признак — это наличие при работе динамического кода. Если чисто статический анализ кода симулятора позволяет достичь все ветки программы, ответственные за симуляцию, то это интерпретация. Если же часть кода появляется только после запуска программы, то это трансляция. Применимость этого признака зависит от силы использованного статического анализатора. Опять же, в некоторых языках возможность использовать что-то вроде eval является встроенной (тут и семейство Lisp, и обычные Perl/Python). Симулятор, написанный на таком языке, изначально динамичен сам по себе и с трудом поддаётся формальному анализу.

Собственно, как я и пытался показать в статье, трудно провести формальную чёткую границу между интерпретацией и трансляцией.
Собственно, как я и пытался показать в статье, трудно провести формальную чёткую границу между интерпретацией и трансляцией.


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

Например, встречается представление программы-интерпретатора как состоящей из двух частей — «собственно» интерпретатора и транслятора, который переводит, скажем, ЯВУ во внутреннее представление, которое затем используется второй частью для выполнения. Т.е. здесь транслятор является блоком, составной частью программы-интерпретатора. И никоим образом и. не выделяется как подвид т. — ведь он — составная часть и. Хотя то же самое можно описать как «компилятор в промежуточный код + виртуальная машина».

Встречается представление о процессоре, как интерпретаторе машинного кода.

Встречается представление о компиляторе как исключительно о трансляторе в машинный код. Наравне с этим возможна компиляция в код виртуальной машины.

Встречается отнесение компилятора к подвиду транслятора.

Встречается выделение транслятора как исключительно программы, не выдающей машинный код и т.д.

Таких коллизий — тьма. Кроме того, есть различие в классификации между советской/российской и американской «школой» и, соответственно, разница в терминологии как следствие традиций.
Таких коллизий — тьма.

В точку!

Т.е. здесь транслятор является блоком, составной частью программы-интерпретатора.

А бывает и наоборот — т.н. трассирующий транслятор, в котором интерпретатор является частью транслятора. При первом проходе по некоторому гостевому коду производится его интерпретация и запоминается трасса — какие инструкции в каком порядке были исполнены. При последующих проходах того же кода группа уже известных инструкций может быть транслирована в один блок — новую сервисную процедуру, состоящую из сервисных процедур трассы плюс оптимизации. То есть фактически теперь интерпретатор будет на этом месте в гостевой программе всё так же вызывать сервисную процедуру, только она будет новая, не существовавшая раньше.
Мне думается, что многие приведенные в статье варианты все равно сводятся к одной топологии потока управления:

1) выбрать одну из многих веток кода и осуществить на нее переход (косвенный или условный)
2) исполнить код из ветки
3) перейти к п. 1

И вот на этапе 1) у нас всегда будут ошибки предсказателя переходов, потому что код, работающий в ВМ, в общем случае заранее неизвестен и потому непредсказуем. Попытки заменить условные переходы косвенными или наоборот могут лишь незначительно повлиять на производительность, так как в обоих случаях ошибки предсказания неизбежны.

Улучшить ситуацию можно путем увеличения количества веток. Так, если ВМ имеет всего 2 возможные команды (А, Б) — то комбинаций из двух команд, идущих подряд, может быть 4. Если теперь сделать 4 ветки, в каждой из которых исполнялись бы все возможные комбинации из двух команд (АА, АБ, БА, ББ) — то непредсказуемый косвенный переход исполнялся бы не каждый раз при выполнении команды ВМ, а один раз на 2 команды. Проблема с этим подходом в том, что кол-во веток растет экспоненциально при увеличении количества комбинируемых команд ВМ. n = a^m, где n — кол-во веток, a — кол-во команд ВМ, m — длина комбинации.

Ну или использовать транслятор/компилятор.
Мне думается, что многие приведенные в статье варианты все равно сводятся к одной топологии потока управления

Любая интерпретация к этому сводится. Разница между схемами — в накладных расходах, не связанных с работой собственно сервисных процедур, и тут, я надеюсь, что стало понятно из статьи, есть варианты.

Попытки заменить условные переходы косвенными или наоборот могут лишь незначительно повлиять на производительность

Кому-то это незначительно, а кому-то ускорение в два раза при смене техники интерпретации очень даже значительно.

Улучшить ситуацию можно путем увеличения количества веток.

Именно это я имел в виду в тексте: … эту идею можно развить и дальше — помочь предсказателю переходов правильно запоминать историю исполнения троек, четвёрок и т.д. за счёт соответствующего «разбухания» кода.
При этом, однако, будет расти давление на инструкционный кэш хозяина, и будет некоторый предел.

Ну или использовать транслятор/компилятор.

Из моего скромного опыта, писать и отлаживать транслятор куда сложнее, чем интерпретатор. Поэтому часто имеет смысл вложиться в него. Тем более что одна техника может вполне сосуществовать с другой в одной и той же модели.
Фантастическая статья. Огромное спасибо. Вопрос — не пробовали делать псевдо-translated, копируя в буфер кода куски, ограниченные парами меток? Идея в том, чтобы оставить код переносимым, отдав компиляцию компилятору:

register cpu_t * pcpu asm("r15");

start_push:
// реализация на си, которая трогает только pcpu
end_push:


По идее, если код position independent и опирается только на r15, его можно скопировать в другое место, и если r15 содержит cpu_t *, код сработает.

Или выглядит нереально?

Спасибо!


Идея в том, чтобы оставить код переносимым, отдав компиляцию компилятору

Конечно же, это очень соблазнительно.


Вопрос — не пробовали делать псевдо-translated, копируя в буфер кода куски, ограниченные парами меток?

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


  1. Не существует портабельного (да и вообще хоть какого-то надёжного) способа определить длину тела функции в Си. Адрес начала функции получить тривиально, а вот конец — нет. Использование адресов меток вместо функций может быть и помогло; но я боюсь, что компилятор начнёт переставлять метки, объединять их и т.п., особенно на уровнях оптимизаций, отличных от -O0. Может быть, какие-то компиляторные барьеры расставить удастся, не знаю.


  2. Тела функций не получается конкатенировать, так как в коде процедур точек выхода может быть несколько. Очень часто компилятор преобразует код вида

    void f() {
        if (a) {
            code1;
        } else {
            code2;
        }
    }

в следующий машинный код:


f:
cmp a, 0
je alternative
code1
ret ; <--- exit 1
alternative:
code2
ret ; <--- exit 2

В результате имеем две точки выхода из функции, тогда как нам вообще не требуется выходить из неё. А надо нам передать управление на инструкцию, следующую после её конца.


То есть, в промежуточном коде надо как-то найти все инструкции ret и заменить их на jmp single_exit. Что, согласитесь, нетривиально, учитывая различную длину ret и jmp.


Да и вообще, у каждой функции будет в общем случае различный пролог и эпилог, которые, опять же, нашей задаче только мешают. Может быть, указать компилятору, чтобы не генерировал пролог и эпилог функций? Но для GCC на x86 это не работает.


В общем, если компилятор не "приручен", то его вывод будет с трудом подвергаться дальнейшим трансформациям.


У меня студент в качестве задания сейчас пишет улучшенный вариант translated, который будет использовать предкомпилированные блоки кода для сервисных процедур. Посмотрим, получится ли что-нибудь стоящее.

Да, похоже — нереально. :(
Расскажете об итогах нового translated?

Код inline-транслятора для ВМ готов: https://github.com/grigory-rechistov/interpreters-comparison/blob/master/translated-inline.c .


Производительность он пока что показывает хуже, чем оригинальный translated. При этом профиль VTune у них очень сильно отличается. У нового транслятора вылезли интересные эффекты вроде 4kB-aliasing и machine clears. Более подробный анализ я провесит пока что не успел, но, похоже, придётся оптимизировать операцию push, т.к. она вылезла вверх в профиле. Но надо разбираться.

Вы описываете «проблемы» линковщика, можно так не заморачиваться и оставить решение линковщикам…

Оставить только линейный код, без if(...) & call(...)
iter_result_t line_code_0001_on_false(vm_enveron & _env){...c_lines… return iter_result_t(result);} // без каких-то явных if
iter_result_t line_code_0001_on_true(vm_enveron & _env){...c_lines… return iter_result_t(result);} //…
… много-много подобных кусков кода…

Всю логику и последовательность исполнения отдать комбинации 4ой и 5ой версии из вашей «семерки»
компилируются только if & call, и затем запускается все сразу.

Я понимаю, что желательно обеспечить общий контекст регистров во всем сгенерированном коде, но эта задача не для связки c-asm, хотя, можно декомпилировать объектный файл и использовать макро (с заменой регистров), используя битовую карту на 14 общих регистров, что все равно потребует создания пролога и эпилога для слишком часто используемых регистров, но при отсутсвии jmp&j* это проще, чем бегать по коду…
Sign up to leave a comment.