Как стать автором
Обновить

Комментарии 45

НЛО прилетело и опубликовало эту надпись здесь
Интересно, почему в подобных обзорах часто старательно обходят стороной Smalltalk-овскую ветку развития (Smalltalk-80, Squeak, Self, Strongtalk), которая привела к JIT-у, HotSpot-у и той Java, которую все и юзают? (А за статью "+" само собой)
Вот тоже интересно.
Там ещё, если мне не изменяет память, и система типов интересная.
Мне и самому интересно!
Если chaetal напишет топик о той ветке, с удовольствием почитаю :)
Я вот всегда думал, что LISP здесь играл не малую роль, а после статьи полез смотреть — и навскидку ничего не нашел… Удивился, но решил, что где-то ошибался… В общем, можно ссылочки по этому поводу?
Пожалуй воздержусь.
Жаль. Я вот за 5 минут обеда :) накопал только The Evolution of Lisp, где говорится про разработку Vlisp в середине 70-х. Наверное, можно ориентироваться на это как на отправную точку?
но с какого-то момента словосочетание «программа для ПК» стало обозначать «программу для x86».

А с играми ещё смешнее. С какого-то времени словосочетание «игра для ПК» стало обозначать «игру для Windows». Просто повсеместная эпидемия. Единственное место, из виденных мной, в которых «игра под Windows» обозначает «игра под Windows», это Стим. В остальных местах просто невозможно определить существует ли версия для Линукс.
Казалось бы, игровые интернет издания заинтересованы в правильной классификации, но на практике всё по другому.
Издания делаются журналистами, у которых из знаний в предметной области — только собственный опыт. Соответственно они одни из основных разносчиков идей: «игра для ПК» стало обозначать «игру для Windows». Так как другого опыта нет, и образования в данной сфере тоже нет.
Да я бы не сказал, что они не понимают этого. Видел много раз как на разных игровых сайтах поднимался этот вопрос. Руководство прекрасно понимает разницу, но под тем или иным предлогом оставляет всё как было.
Возможно сайт потребует большой переделки, естественно игровая база должна быть вся переработана из за появления новой платформы, может что-то ещё. В любом случае реальная причина это не непонимание разницы между Линукс и Windows.
Я уверен, что с точки зрения массового потребителя «игра для ПК» и при этом не для Windows — это блажь, потому что, насколько я могу судить из личного опыта, качество PC-игр на не-Windows платформах объективно уступает качеству тех же самых PC-игр на Windows-платформах.
Если игра, например, под XBOX ONE уступает по качеству той же игре на PS4 это основание исключить платформу XBOX ONE на игровом сайте? Думаю, владельцы XBOX ONE с вами не согласятся. Как не согласны и пользователи Линукс с таким положением вещей.
А вообще, по этому поводу вспомнился Карел Чапек:
Сытый. Эти разговоры о нищете страшно преувеличены. Не так уж все плохо.
Ключевое различие состоит в том, что если XBOX — это специализированная игровая система, то под Linux (например, Ubuntu) нормальное положение дел это когда новейшие AAA-игры запросто вешают всю ОС наглухо и адово тормозят. Помоему, исправить положение дел может только выпуск и популяризация «программно-аппаратных комплексов» типа стим-машин, специально приспособленных для игр, и только после этого в «версиях под линукс» действительно появится смысл — потому что производитель будет должен гарантировать, что линукс версии — это полноценные версии игр в полном смысле этого слова, по крайней мере, если они запускаются на специально предназначенных для этого системах.
Давайте подведём итог? Вы хотите сказать, что игры под windows должны называться играми для PC, я правильно вас понял?
Если нет, то о чём вообще спор? Речь шла лишь о том, что игры под Windows должны называться играми под Windows, а игры под Линукс должны называться играми под Линукс, ни больше ни меньше.
Давайте подведем итог. Господа минусующие по всей видимости считают, что объективно качество PC-игр на платформе Linux не уступает качеству PC-игр на платформе Windows на среднестатистическом железе. Ну ок. Дальше не имеет смысла что-либо обсуждать. Роза пахнет розой хоть розой ее назови хоть нет. Линукс игры в массы. Линуксы на десктопы. Вендекапец близок, товарищи.
Не знаю кто вас минусует (я не имею возможности ни минусовать, ни плюсовать), но со своей стороны могу сказать, что я много игр попробовал на одном и том же ноутбуке и под Виндой и под Линукс. Какого-то ощутимого падения качества или скорости работы в версиях под Линукс я не заметил.
Некоторые игры под Линукс не поддерживают русскую локализацию, например Civilization 5, но это же всё равно не повод игнорировать игровую платформу?

И ещё один момент. PC это персональный компьютер. Это железо на которое может устанавливаться любая понравившаяся пользователю ОС. Это категория уровнем повыше нежели ОС. Так что даже пользователи Mac OC могут почувствовать себя несколько дискриминированными когда их, в данном случае, игровую платформу называют Mac OC (или просто Mac), а Windows — PC.
Ну, а про Линукс уже написали выше.
Так что даже пользователи Mac OC могут почувствовать себя несколько дискриминированными когда их, в данном случае, игровую платформу называют Mac OC (или просто Mac), а Windows — PC.
Такая уж сложилась традиция наименования.

Вот вам внеайтишный пример: «соединённые штаты» — это вид государственного устройства, и, например, официальное название Мексики c 1824 — Estados Unidos Mexicanos. Но так уж сложилось, что название «Estados Unidos» (без уточнений) применяется к одному конкретному государству. Чувствуют ли себя мексиканцы от этого дискриминированными?
Вот вам внеайтишный пример: «соединённые штаты» — это вид государственного устройства, и, например, официальное название Мексики c 1824 — Estados Unidos Mexicanos. Но так уж сложилось, что название «Estados Unidos» (без уточнений) применяется к одному конкретному государству. Чувствуют ли себя мексиканцы от этого дискриминированными?
Без понятия. В Мексиканцах плохо разбираюсь.
Что касается пользователей Mac, то для них неудобство, конечно, чисто эстетическое. Всё же они могут узнать выходила ли нужная игра под их платформу, а так же использовать фильтры для поиска игр для их платформы (естественно, на тех сайтах, где подобное предусмотрено). В случае с Линуксом всё намного печальнее. В итоге, за такой информацией я стал ходить на Стим, а страдают только сами владельцы игровых сайтов. И в условиях когда из за традиции начинает страдать бизнес, думаю, неистово держаться за традицию не самое лучшее решение.
Насколько мне известно, компания Apple не поддерживает добровольный выбор пользователем железа (из всего разнообразия PC-железа) на которое будет установлена Mac OS, в отличие от Windows и Linux. Если это не так прошу поставте мне еще минусов в назидание.
Перестаньте уже ныть про свои минусы.
Перестаньте говорить мне что мне перестать а что нет, ок да?
Intel генерирует исключение при делении на ноль; в процессорах ARM — возвращает в качестве результата ноль

Команда целочисленного деления имеется далеко не во всех ARM и появилась сравнительно недавно
Там где она есть, поведение контроллируется битом UFSR.DIVBYZERO (UsageFault Status Register)
Хотелось бы дополнить автора в вопросе, почему стек-ориентированные виртуальные машины проигрывают регистровым. Потому что они медленнее. Дело в том, что стек – это область памяти, которую адресуют регистры процессора. Чтение из стека или запись в него – это работа с памятью. Когда-то обращение к памяти занимало один такт процессора. В процессе развития технологий процессоры ускорялись быстрее, чем память. Для чтения/записи области памяти (и стека тоже!) уже требуется несколько тактов. И чем дальше, тем больше разница в скорости. А с регистрами процессор работает за 1 такт. Виртуальные стековые машины хорошо бы ложились на реальные стековые машины. Но таковых нет, а на реальные регистровые машины они ложатся хуже. Были интересные проекты стековых процессоров, где вершина стека хранилась бы не в памяти, а в регистрах. Т.е. обращение к стеку делалось бы уже за 1 такт. Но сколько перспективных архитектур проиграло конкуренцию x86 и ARM…

Коль автор так глубоко копнул в прошлое, то для восстановления исторический справедливости хотелось бы заметить, что в СССР тоже были подобные технологии.

  • Язык программирования Алмо
  • Система программирования Сигма
  • Универсальный машинно-ориентированный язык программирования Эпсилон
  • Система программирования Бета


Какие-то из них были «универсальным ассемблером», другие – системой, частью которой являлось универсальное промежуточное представление. Подробнее: Экскурс в историю разработок языков программирования и компиляторов в СССР
Спасибо за подробный комментарий!

Дело в том, что стек – это область памяти, которую адресуют регистры процессора.
Это не обязательно так; стек может частично (как в Itanium) или полностью (как в 80x87) быть внутри процессора.

Успех/неуспех процессоров, да и любых технологических новшеств, далеко не всегда определяется их техническими характеристиками.
Это не обязательно так; стек может частично (как в Itanium) или полностью (как в 80x87) быть внутри процессора.
Тут получается «вилка»: либо мы не используем, грубо говоря, половину «регистров процессора» (= быстрой дорогой внутрипроцессорной памяти, отведённой под стек) только из-за того, что стек дотуда «не дорос», либо перед каждой операцией что-то подгружаем из ОЗУ и после каждой операции что-то выгружаем в ОЗУ. Первое расточительно и из-за этого медленно, второе просто медленно.
Ваши рассуждения в равной степени применимы и к традиционной архитектуре, с «неподвижными» регистрами: либо используем не все (расточительно), либо не поместились в регистры и пользуемся памятью (медленно).
От того, что регистры по ходу действия «скользят» вверх-вниз, ничего принципиально не меняется.
При традиционной архитектуре мы можем когда угодно использовать какие угодно регистры. При стековой — только те, до которых дорос стек, и то только на хранение, а на запись — только верхушку стека. Про «скользящие» регистры — идея интересная, не помню, чтоб была реализована аппаратно, но, может быть, кем-то когда-то воплощено, просто малоизвестно.
При традиционной архитектуре мы можем когда угодно использовать какие угодно регистры. При стековой — только те, до которых дорос стек, и то только на хранение, а на запись — только верхушку стека.

Не знаю, как в FORTH, а в JVM, которая стековая, помимо стека есть ещё и так называемые локальные переменные (по сути — регистры), есть операция swap, есть серия операция dupx. Думаю, непрактично делать пуристическую машину, работающую строго на одном принципе.

Кроме того, тут проблема ещё и в том, что работаем мы пусть и с вершиной стека, но хранить-то нужно весь стек! Кстати, эта проблема имеет аналог в регистровых машинах — в них оптимизаторы стараются как можно ближе «сдвинуть» места определения переменной (def) и места её использования (use).

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

Не уверен, что правильно уловил суть высказывания. Если имелся в виду маппинг большого числа регистров виртуальной машины на малое число регистров реальной — то аппаратная реализация не нужна. Если всё же про маппинг стека, то это сводится к предыдущей задаче, потому что в ряде случаев стековую машину можно легко сконвертировать в регистровую (по крайней мере, это верно для байт-кода Java и MSIL).
Если что, я писал не про виртуальные машины, а про аппаратные реализации.
Не знаю, как в FORTH, а в JVM, которая стековая, помимо стека есть ещё и так называемые локальные переменные (по сути — регистры)
Локальные переменные используются меньше, чем стек, нерационально использовать под них более быструю память, чем под стек.

В FORTH, насколько я помню из книжек 20-летней давности, под каждую переменную резервируется место в словаре, и она глобальная. И стек там не помню чтоб по кадрам делился как в JVM.
Не уверен, что правильно уловил суть высказывания.
Идея была про чисто аппаратную реализацию «прозрачного» маппинга верхушки стека на регистры при том, что глубина стека может быть какой угодно большой и располагаться он может где угодно — в регистрах, в кэше или в ОЗУ, или распределён по всем этим трём областям памяти.
При стековой — только те, до которых дорос стек

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

Про «скользящие» регистры — идея интересная, не помню, чтоб была реализована аппаратно

Itanium же.
Хотелось бы дополнить автора в вопросе, почему стек-ориентированные виртуальные машины проигрывают регистровым. Потому что они медленнее. Дело в том, что стек – это область памяти, которую адресуют регистры процессора. Чтение из стека или запись в него – это работа с памятью.

Не поверите, регистровые виртуальные машины обладают тем же самым недостатком. А всё из-за того, что реальные процессоры содержат разные наборы регистров с различной разрядностью и, возможно, назначением (как в ia32). Сделать виртуальную машину которая являлась бы подмножеством различных железок — это создать очень ограниченную систему, которая бы не использовала бы всех возможностей целевой платформы. Регистровые машины обычно вводят или достаточно большой лимит на регистры, либо не вводят его вовсе. Так что интерпретатору подобной машины чаще всего приходится держать значения регистров в памяти.

Однако, серьёзные виртуальные машины занимаются вещами более серьёзными, чем интерпретация. Обычно, они либо в режиме AOT, либо JIT, генерируют машинный код для целевой архитектуры. И вот тут дискуссия переходит в другую плоскость. Во-первых, достаточно умному бэкэнду, что ему дали — регистровое представление или стековое, в итоге он сгенерит одинаково быстрый код. Если ему не нравится одно, всегда можно сконвертировать его в другое. Во-вторых, нас начинает волновать производительность компилятора, а вот тут уже не всё однозначно. С точки зрения бэкэнда и то и другое одинаково плохо, а хорошо — это умные слова вроде SSA и PDG, но они уже гораздо меньше похожи как на регистровые, так и на стековые машины. В этом смысле шустрее всего должен быть LLVM. К слову, та же JVM внутри себя генерит SSA-представление из байткода и в дальнейшем работает только с ним, т.е. выполняет ту работу, которую за LLVM делает фронтэнд. Вопрос о том, на кого переложить больший объём работы (компилятор или виртуальную машину), зависит от целей проекта и требований к нему. Я так понимаю, LLVM проектировался с целью достичь быстрой генерации машинного кода, а JVM — с целью упростить работу создателям компилятора.
Я так понимаю, LLVM проектировался с целью достичь быстрой генерации машинного кода, а JVM — с целью упростить работу создателям компилятора.

Я понял чуть иначе: что среди целей Java-байткода были компактное представление и возможность эффективной интерпретации (без AOT и JIT) на маломощных платформах. А перед LLVM-IR таких целей не стояло: только эффективная компиляция, и ничего другого.
Знакомясь с LLVM IR, я обратил внимание, что LLVM IR имеет скудные средства для работы с механизмом стека. Если x86 содержит в себе достаточно полный набор инструкций для манипуляций со стеком, то LLVM IR ближе к языкам типа C и я не увидел в нём операций явного изменения указателя стека.

Между тем, Есть языки, которые активно используют стек. Самый известный их представитель – это язык FORTH. В программах на FORTH очень часто пишут «PUSH» и «POP». Было бы интересно посмотреть реализацию FORTH на LLVM.

Существует проект, который поставил своей целью преобразовывать код x86 в LLVM. Называется он McSema, финансируется DARPA. Задумка хорошая: всё, что нажито непосильным трудом для x86, сделать доступным на других платформах с помощью LLVM. Но как в LLVM так же манипулировать стеком, как это делает x86? Конечно, LLVM IR, будучи Тьюринг-полным языком, может решить любые задачи. Но преобразование x86 -> LLVM IR -> x86 дало бы в некоторых случаях, как мне кажется, к немалому увеличению кода на выходе. Будет ли программа на FORTH после преобразования x86 -> LLVM IR -> x86 столь же лаконична, как ранее?

Я задал эти вопросы Andrew Ruef, соавтору проекта. На это он мне ответил:

The way we do this in LLVM is by representing the machine state as a structure with registers as field members. For example:

struct RegisterState {
  uint32_t eax;
  uint32_t ebx;
  …
  uint32_t esp;
}; 

So each translation routine is a function that produces a transformation on struct RegisterState. This is what xchg esp, eax would look like:

void doXchg_ESP_EAX(struct RegisterState *state) {
  uint32_t tmp = state->eax;
  state->eax = state->esp;
  state->esp = tmp;
  return;
}

We use LLVM as a language to express the effects of instruction on a machine state. We model the machine state as an object in LLVM. We do this for registers and memory. The model doesn’t have a special type of memory, which matches the x86 model where there is no distinction between global memory, heap memory, and stack memory in the address space.

Между тем, Есть языки, которые активно используют стек. Самый известный их представитель – это язык FORTH.
Ну и где сейчас FORTH?

Но преобразование x86 -> LLVM IR -> x86 дало бы в некоторых случаях, как мне кажется, к немалому увеличению кода на выходе. Будет ли программа на FORTH после преобразования x86 -> LLVM IR -> x86 столь же лаконична, как ранее?
Конечно, нет. А зачем?

Если вас устраивает имеющийся код для x86, зачем его перегонять в LLVM-IR и обратно?

Например, в LLVM встроен бэкенд, генерирующий из LLVM-IR код на Си. Но никто не ожидает, что компиляция Си -> LLVM-IR -> Си произведёт такой же лаконичный код, каким был исходный. Кому нужно перегонять код на Си в код на Си?
По поводу x86 -> LLVM IR -> x86. Есть немало унаследованного 16-битного кода, который не работает на 64-битных машинах. Есть у меня одна такая программка: нужная, но исходников к ней нет. Если преобразовать её с помощью McSema в 32-битный код x86, то было бы здорово.

FORTH как таковой меня не интересует. Я его привёл лишь как пример. Но меня несколько разочаровывает LLVM тем, что в нём мало возможностей для манипуляций со стеком. По этой причине я не могу его использовать в одном проекте. А жаль.
Есть немало унаследованного 16-битного кода, который не работает на 64-битных машинах. Есть у меня одна такая программка: нужная, но исходников к ней нет. Если преобразовать её с помощью McSema в 32-битный код x86, то было бы здорово.

Насколько я понимаю, для такого преобразования достаточно дизассемблировать старый код и сассемблировать его 32-битным ассемблером; и McSema ни к чему.
достаточно дизассемблировать старый код и сассемблировать его 32-битным ассемблером
Значит, у меня не было сильного непреодолимого желания :)
Ну и где сейчас FORTH?


Может быть, правильнее спросить, где сейчас мы?
Между тем, Есть языки, которые активно используют стек. Самый известный их представитель – это язык FORTH. В программах на FORTH очень часто пишут «PUSH» и «POP». Было бы интересно посмотреть реализацию FORTH на LLVM.

Как ни странно, инженерный гений способен разруливать подобные ситуации выдавать изящные и красивые решения подобных задач. Например, JavaScript, который весь такой динамический и с прототипным ООП, смогли-таки эффективно исполнять на железе с «традиционной» архитектурой (на самом деле, это смогли ещё для self, с которого перенесли опыт). Кстати, вроде бы постепенно в V8 выбрасывают crankshaft и внедряют LLVM, хотя я не очень пристально слежу за последними изменениями. Ну да, для реализации прототипов замутили скрытые классы, а для генерации быстрого кода делают speculative optimizations и OSR. Уверен, подобные финты ушами, если очень нужно будет, смогут придумать, если это будет экономически оправданно, и для Lisp, и для Self.

Кстати, ещё один пример, когда совсем чуждую концепцию умудряются эффективно реализовать на традиционном железе — это чистые функциональные языки вроде Haskell. Там концепция ещё более чуждая, чем Self/JavaScript, и ничего, придумывают всякие страшные штуки вроде spineless tagless g-machine и т.д.
Работа со стеком в большенстве случаев легко превращается в регистровую. Во время компиляции стековый код интерпретируется и в стеке компилятора хранятся имена регистров. Проблема возникнет в случае, если в одной точке программы заполненность стека может быть разная — например, в одном цикле в стек пишется, а в другом — читается.
Мне интереснее, можно ли реализовать LLVM для Intel iAPX 432. Там нет операций работы с указателями, обращаться можно только по индексу в массиве. А LLVM часто получает непосредственный указатель на элемент массива или структуры.
Есть еще интересный универсальный код в системе AS/400, которая была реализована на процессорах с различной системой команд (правда одной фирмы и со специальной аппаратной поддержкой).
Хотелось бы про него узнать хоть какие-нибудь подробности.

К сожалению, гугл не обнаруживает никакой информации, кроме того, что «в системе AS/400 был интересный универсальный код».
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации