Pull to refresh

Comments 64

UFO just landed and posted this here
Тоже неплохо :)
Но я не собирался делать перевод 12-страничной статьи, а просто вспомнилось, и решил рассказать о её существовании
Сожалею, что не оправдал ожидания
Оправдали. Интересная статья. Интересный факт. Не принимайте близко к сердцу комментарий a_freeman — он увидел филологическую аналогию, остроумно её изложил, оказался первым, а дальше просто сработал прайминг. По сути это не более чем кривляние перед зеркалом.

Пришлось читать про прайминг что бы понять ваш комментарий, спасибо +1 к словарному запасу

В комментарии должна быть ссылка на исследования комментариев с примерами более короткого изложения мысли. А так рекурсия поломана...

Да, рекурсия поломана, ее надо восстановить.
если процессор, эмулирующий сам себя, быстрее себя, то что мешает запустить эмуляцию процессора в эмуляторе процессора на самом процессоре и получить еще большее ускорение? Запускаем рекурсию и получаем процессор, работающий с любой наперед заданной скоростью.
А тут вероятно все будет асимптотически сходиться к какому-то предельному значению скорости исполнения программы и каждая последующая итерация будет давать все меньший и меньший прирост скорости.

Хм, а ведь в этом есть доля истины. Это же и к нам можно применить, верно?)

Там какая суть: в одном варианте процессор просто исполнял бинарник, а в другом также исполнял этот бинарник, но уже тратил часть времени на анализ и оптимизации этого же бинарника.

Как итог суммарный выигрыш от оптимизации покрыл все расходы на анализ и эмуляцию, да ещё накинул сверху 20% производительности.

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

Но этот принцип можно использовать и для нашего мозга: что если не выполнять интеллектуальную работу, а сперва проанализировать её и оптимизировать?

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

А что касается рекурсии - это работать определенно не будет.

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

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

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

Не факт.
Первый раунд что-то оптимизировал, эти оптимизации сделали теоретически возможными дальнейшие оптимизации, но оптимизатор не пошёл дальше из-за ограничений по времени работы или по ресурсам.

а в совсем технические детали 20-летней давности смысла лезть нет.

Так ничего и не изменилось. JIT рекомпиляторы как работали тогда, так работают и сейчас.
А много народа знает, что в итоге можно получить плюс к производительности на одной и той же архитектуре?
Для меня 20 лет назад это было очень интересным фактом.
Но конечно — в рабочем плане бесполезным.

Мне кажется некорректным говорить, что эмулятор быстрее.

Эмулятор помог оптимизировать оригинальный код. Он получается и без эмуляции быстрее бы запускался

Бинарь, работающий в эмуляторе — работает быстрее, чем на голом железе.
Понятно, что чудес нет, и это всё из-за «неэффективности» рантайма на голом железе

Просто оригинальный компилятор фиговый, либо не умеет в PGO (и с архитектурой, которая слишком чувствительна ко всяким branch misprediction-ам).

Чисто «теоретически» — у компилятора меньше инфы, чем у эмулятора в рантайме. Да и PGO — ну оптимизировали на одних данных, а в реальности пошли другие.
Дальше просто — если накладные расходы эмулятора меньше, чем получаемый профит, то в итого получим плюс.
По вашей статье получается, что код внутри условного qemu может выполняться быстрее, чем на хост-машине. Я правильно понял?
Получается, что при определенных условиях — так.

если там JIT то он удаляет недостижимый код перетасовывая вызовы, меньше прыжков в коде, меньше холостых тактов мимо кода)

UFO just landed and posted this here
Например исходный компилятор не знает паттерн входящих данных и не может предсказать переходы. Этого хватит? В более общем случае — может быть разная микроархитектура процессоров, и что хорошо одному, то плохо другому.
UFO just landed and posted this here

В современных компиляторах тонны математики и они могут, но на них потрачены тысячи человекочасов и проработанны сотни теорий, а на тот момент такого не было.

UFO just landed and posted this here
проект HP Dynamo. Это эмулятор процессора PA-8000, работающий на этом же процессоре PA-8000, но с технологией JIT. И реальные программы, запускающиеся в эмуляторе — в итоге работают быстрее, чем на голом процессоре.
Ээээ, а разве это не значит, что это по сути генерация и эмуляция более эффективного процессора с той же системой команд?

Нет, это получается вообще не более эффективный процессор - это более оптимальная программа.

По идее новая оптимизированная версия будет быстрее и на обычном процессоре

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

Подозреваю, что это все же и разные запуски

Я не уверен, возможно это теоретически реализуемо, но кажется, что эти оптимизации работают не внутри запущенного процесса, а при следующих запусках

Ну вы-бы статью прочитали. Они оптимизируют на лету. Там вообще сначала эмулятор работает
Тоже показалось, что это просто перекомпиляция с профилированием.

Черт, только с третьего раза правильно слово "Футамура" прочитал. А то все "Футурама" получалась, и это вызывало у меня легкое недоумение.

Черт, только прочитав ваш комментарий правильно прочитал «Футамура» :D И искал Футураму в статье по ссылке.
Ничего не понял.
Программистам из HP Labs стало интересно, а что будет, если написать оптимизирующий JIT компилятор под ту же платформу, на которой он работает
Компилятор который компилирует программу «на лету» по время исполнения. Тогда причём тут это:
Под эмулятором можно было запускать немодифицированные родные бинари
??? Это получается компиляция уже откомпилированных бинарников???
??? Это получается компиляция уже откомпилированных бинарников???

А что вас смущает?
Из известных широкой публике примеров — бинарная трансляция программ, скомпилированных под MacOS на Intel процессорах для их выполнения на MacOS на процессоре Apple M1. До того у них был аналогичный транслятор при миграции с PowerPC на Intel. Статья на Wikipedia.


Ну и JVM JIT тоже транслирует Java-байткод в инструкции процессора.

То что "трансляция" не равно "компиляция".

Все эти примеры условны

Не то чтобы это не ошибка, но допустимое обобщение

Машинные коды так же интерпретируются процессором, программы компилируются в байт-код, байт-код интерпретируется виртуальной машиной или компилируется нативный.

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

Вообще-то компиляция это один из видов трансляции.
Впрочем транслятор, описываемый в статье, часто называют JIT recompiler, для удобства. Хотя более правильно называть его Dynamic Recompiler- (dynarec).
Вообще-то микрокод тоже компилирует макроинструкции в микроинструкции Big Core от Intel. Там типо почти RISC, горизонтальный микрокод, все дела.

Это получается компиляция уже откомпилированных бинарников???

А что вас удивляет? Приблизительно каждый современный x86 процессор этим занимается. Тут, правда, есть терминологическая путаница, пошедшая со времён Java HotSpot, когда термин JIT-компиляция начали применять к, строго говоря, очень продвинутому, но транслятору. Просто то, что он умеет, было ранее характерно для компиляторов.

Разве компиляторы — не частный случай трансляторов?

Разве компиляторы — не частный случай трансляторов?

Частный, но не каждый транслятор является компилятором. Виноват, комментарий получился мутным.
Compilers ⊂ Translators.
Из того, что (HotSpot ∈ Translators), вовсе не следует, что (HotSpot ∈ Compilers).
То есть мы имеем дело с расширением термина, когда к подмножеству Compilers начинают относить и те члены множества Translators, которые, строго говоря, компиляторами не являются.

Не компиляция, а, скорее, оптимизация и последующее выполнение. Так то действительно, как были машинные коды, так и остались

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

Только я настроился почитать объёмную статью с geek porn и тут такой облом!
Пойду читать PDF.

Кажется, что дело не в том, что эмулятор быстрее, а в том, что он позволяет за счёт интроспекции оптимизировать программы

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

новые бинарники, построенные на основе информации о горячих путях, будут работать быстрее

Для этого придумали PGO.

Я так понимаю, что в статье ровно о нем и речь.

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

Просто статистику для оптимизаций не соберёшь, но результат быстрее для всех

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


Так делает библиотека ATLAS — Automatically Tuned Linear Algebra Software, но чуть хитрее.

Для тех, кого этот феномен заинтересовал, кейворд для дальнейшего чтения — "Проекции Футамуры".

Получается, что исходный компилятор недостаточно оптимизировал код. Шок контент: сжатый файл занимает меньше, чем несжатый.

P.S. Теперь осталось запустить jit-эмулятор в jit-эмуляторе и запустить программу в нём!
Он не мог оптимизировать лучше, потому что не имел динамической информации о том как исполняется программа.

А если запустить эмулятор эмулятора в эмуляторе...

UFO just landed and posted this here
Только тут это на лету, и профилирование идёт по текущим данным, а не каким-то там. То есть потенциально можно ещё больше профита получить

Потенциально да, но не факт, что этот "потенциал" перекроет оверхед от jit обвязки. Особенно на коротких дистанциях, когда времени на амортизацию мало.

Почему тогда, операционные системы в VBOX которые были запущены в VBOX не быстрее хоста? -
Потому-то эмуляция современная происходит с аппаратным ускорением. Впрочем насчет не быстрее это обычно баг. Если вы используете нативный nvme для запуска, а не файл вирутальной машины, то можно почти нагнать скорость. (Как я.) Впрочем моного что (скоро вообще все) делается на GPU, а не на CPU, а вот тут прямого доступа нет (пока что, в 21H1 вроде GPU начала тоже пропускаться).
UFO just landed and posted this here
С проекциями Футамуры не все так просто, на мой взгляд… Поясню:

0. Специализацией называется процедура конкретизации, допустим, 2^х образует множество решений в зависимости от х, но конкретизируя (специализируя) 2^x числом 3 в качестве значения х, мы получаем вид функции 2^3, как форму абстрагированной записи решения = 8, то есть специализация — это редукция когерентной суперпозиции состояний допустимых решений (редукция множества решений путем коллапса этого множества на детерминированном значении).

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

Поэтому,

1. Первая проекция Футамуры, скажем, на некотором коде высокого уровня по отношению к интерпретатору этого кода (специализация интерпретатора кодом) сформирует редукцию потенциального множества состояний интерпретатора до конкретного состояния — до программы, которая конвертирует конкретный высокоуровневый код в конкретный низкоуровневый, фактически жестко реализуя match'инг из двух множеств.

Иными словами, реализуя конструкцию вида:
IF <тако-то конкретный код> THEN <такой-то выходной>, где информация о выходном была взята специализатором из допустимых возможных состояний интерпретатора.

Первую проекцию называют «компиляцией».

2. Вторая проекция Футамуры предполагает специализацию специализатора кодом самого интерпретатора. Это предполагает следующее:

Код специализатора имеет два аргумента (два входа): что специализировать и чем специализировать. Эти аргументы заранее не определены и могут быть чем угодно, то есть специализатор в отличие от интерпретатора имеет два потенциальных и заранее неопределенных, но связанных множества (по определению специализации).

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

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

Фактически специализация — это индексирование. Таким образом, специализация специализатора на интерпретаторе порождает индексатор состояний интерпретатора.

Ладно, едем дальше…

3. Третья проекция Футамуры: специализация специализатора специализатором.

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

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

Простыми словами, мы получаем программу представляющую собой уравнение вида F(правила, код) -> низкоуровневый код, таким образом, что:

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

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

— но, подставляя правила и низкоуровневый код — мы можем получить допустимое подмножество исходного высокоуровневого кода в качестве решения (то есть мы вырождаем дизассемблер)
Вот уж глубокая идея. Естественно, если запускать оптимизированный код в эмуляторе и неэффективный нативный, то может. Но при прочих равных – нет.
Только эмулятор его на лету оптимизирует и изначально в (не)равных условиях с нативных.
Sign up to leave a comment.

Articles