Pull to refresh

Comments 67

понравился последний вопрос, чему-то конечно научились в процессе, но в итоге время потеряли, при Сталине тоже передирали (например Ту-4), но не так бездарно

Ну, судя по внутреннему устройству ЕС-1045/46, ереванская контора мало чему научилась. Да, машина получилась быстрая, но железа в ней... Явно больше, чем следовало бы, и, опять-таки, приличное его недоиспользование. Хотя до такого откровенного идиотизма, как в 1030, там всё-таки не доходит: чему-то таки научились, но явно недостаточно.

Честно говоря, я б на месте профильного нашего руководства после разработки 1030 просто разогнал бы ереванский институт нафиг. Пускай лучше коньяк делают: он у них лучше получался :) Но, думаю, у армян был не только коньяк, но и сильное лобби где надо. Ведь позже они не дали казанцам сделать новую машину на смену 1033, пропихнув свою 1045.

трудно поверить, но был у них в свое время, коньяк вполне оценил, с моей точки зрения милые люди, дела ЕС меня тогда не касались, какое-то лобби у них конечно было, теперь какая разница, примерно как про походы князя Игоря на половцев :)

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

но железа в ней...

Но зато хорошее железо: в гараже стоит шкаф из ЦП ЕС-ки, отличная вещь. Очень тяжелая, но отличная.

Давеча откопал в своих загашниках книгу "Операционная система ОС ЕС". В.П. Данилочкин, В.В. Митрофанов и др. Финансы и статистика, 1988. 592 страницы мелким шрифтом. В книге есть немного про макроассемблер, но самое интересное - "Глава 2. Процедура начальной загрузки". Так, что если где-то завалалась ЕС ЭВМ в более-менее нераздербаненном виде - готов поучавствовать в её запуске. ;)

"Операционная система ОС ЕС"

Позволю себе небольшую цитату из этой книги:

Система виртульаных машин (СВМ ЕС) представляет собой следующий шаг в развитии концепции виртуализации системных ресурсов вплоть до понятия виртуальной машины (ВМ). В СВМ ЕС может одновременно функционировать до 10 000 виртуальных машин, каждая из которых функционально эквивалентна реальной ЭВМ. Таким образом, СВМ ЕС представляет возможности параллельной работы на одной реальной ЭВМ Ряд-2,3 различных системно-независимых программ и операционных систем, включая ДОС ЕС и ОС ЕС.

Принципы работы виртульной машины соответствуют принципам работы моделей ЕС ЭВМ Ряд-2, за исключением того, что в виртуальной машине не поддерживаются средства мультипроцессирования и прямого управления. Дня неё моделируются монитором виртуальных машин все технические средства реальной ЭВМ, включая пульт управления, процессор, основная память, каналы ввода-вывода, устройства управления и периферийные устройства, называемые в этом случае соответственно виртуальный пульт управления, виртуальные периферийные устройства и т.д.

Ряд-2 это почти все машины серии ЕС-1020, ЕС-1030, ЕС-1040 и ЕС-1060.

Удивительно то, что виртуализация и понятие гипервизора (монитора виртуальных машин), существовавшие в ЕС ЭВМ (IBM System/360/370) с 1970-х годов пришло в PC только в середине 2000-х и теперь преподносится как нечто очень новое, модное и молодежное.

Надо еще покопаться в старом хламе. Помню была книга по программированию СМ-ок (PDP), примерно тех же времён.

Насчёт Ряда 2 Вас уже поправили. 1020, 1022, 1030, 1032, 1033, 1040, 1050, 1052 -- Ряд 1, т. е. Система 360. Там поддержки виртуальной памяти не было, а значит, и СВМ работать не могла.

Что же до позднего появления виртуализации ПК, то здесь надо спасибо сказать фирме Интел, которая за всю свою историю не создала ни одного вменяемого процессора, если говорить про архитектуру: сплошное уродство на уродстве. Поэтому до появления специальных средств поддержки виртуализации создание системы виртуальных машин на интеловской архитектуре было невозможным технически. Ну а Системе 370 никаких дополнительных средств для этого не требовалось, хватало обычного механизма виртуальной памяти. Если специальные средства поддержки виртуализации имелись, они ускоряли работу и гипервизора, и виртуальных машин, но принципиально ничего не изменяли.

Ну а насчёт "нового и молодёжного" -- это не только виртуализация. Почти всё "новое" реально появилось впервые в 1960-70-х годах. Собственно, только Plug&Play, кажется, и стал реально новым, когда он появился на ПК в 1990-х.

Что же до позднего появления виртуализации ПК, то здесь надо спасибо сказать фирме Интел, которая за всю свою историю не создала ни одного вменяемого процессора, если говорить про архитектуру: сплошное уродство на уродстве.

А чем вам не нравятся процессоры Intel - те, которые x86? С учетом их цены, естественно. Например, виртуальный режим 8086/8088, позволявшший запускать несколько виртуальных копий DOS (реально позволявший, я такое делал под DESQView в самом начале 90-х), появился уже в 80386, ещё аж в 1985 году. Там же появилась и вполне полноценная страничная организация памяти, позволявшая реализовывать виртуальную память в духе IBM System/370. Так что сделать аналог той же IBM VM/SP (я, кстати, немало с ней поработал в молодости на EC1045, где, кстати, никаких аппартных средств поддержки виртуалихации кроме этой самой виртуальной памяти тоже не было) возможность была ещё тогда.

Причина позднего прихода виртуализации IMHO была в другом: памяти массовых ПК под нее, с учетом выросших потребностей прикланого ПО, откровенно не хватало. Памяти за разумные деньги ещё в начале 90-х не хватало даже для широкого распространения ОС с вытесняющей многозадачностью, типа разнообразных Unix или линейки систем от команды из DEC (RSX/VMS/Windows NT). Память была настолько ценным ресурсом, что побеждали системы с кооперативной многозадачностью: Windows на ПК, Netware на сервере и т.д. Потому что им памяти требовалось меньше. А вы говорите - "виртуализация"...

PS Ну да, в 80286 Intel вовсю поигралась с дhугой моделью организации памяти - сегментной (а потом тащила совместимость с ней аж до x64). Впрочем, тогда ещё не было очевидно, лучше ли она страничной или хуже, так что это IMHO было простительно.

Архитектура у интеловских процов ужасна. И в плане системы команд, особенно её кодирования (попробуйте, хотя бы, определить длину команды -- с учётом всех этих идиотских префиксов), и в плане системных вещей. В частности, виртуализацию на 80386 сделать невозможно технически, потому что дебилы-архитекторы половину системных по своей сути команд сделали непривилегированными. Установить значения системных регистров код пользователя не мог, а прочитать -- запросто, и это полностью убивает любую возможность виртуализации. Ну а в Системе 370 все такие команды были привилегированными -- потому виртуализация и оказалась возможной.

Что же до памяти, СВМ ЕС, она же VM/370, работала уже на машине с 512 килобайтами памяти. Ну а вытесняющая многозадачность была и в OS/360 MFT (минимальный объём -- 128 Кбайт), и в "бабке Винды" RSX-11M -- которой, если уж совсем минимальная конфигурация, хватало 32 Кбайт, а в полной её вовсю крутили на машинах с 248 Кбайтами. Да, все эти системы без графики, но, простите, первая версия Винды работала ещё на 8086/88, имея лишь 640 Кбайт ОЗУ -- т.е. пары мегабайт уж точно хватило бы и на графику невысокого разрешения, и на вытесняющую многозадачность. Ну а такой объём ОЗУ был, по сути, минимальным для компьютеров на 80386 -- память уже не была по-настоящему дефицитным ресурсом.

попробуйте, хотя бы, определить длину команды -- с учётом всех этих идиотских префиксов).

Но с другой стороны, это позволяло сэкономить на длине команды: в System/360 1-байтовых команд не было, а в 8086 - были. Но таки да: шестнадцатеричный дамп памяти на ЕС читать было сильно легче.

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

Ну, VMWare же сделали ;-) Правда есть нюанс - как именно: если на то, что в режиме пользователя программа видит CR0-3 хоста, можно наплевать, то привилегированный режим пришлось поддерживать в режиме интерпретации :-Q

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

Что же до памяти, СВМ ЕС, она же VM/370, работала уже на машине с 512 килобайтами памяти.

Я же не зря уточнял: "с учетом выросших потребностей прикладного ПО". 512 КБ к началу 90-х - хватало уже не для каждой программы даже на однозадачной DOS. А в 90-х и 640К стало достаточно не только лишь всем, в результате куча программ начала использовать DOS extender, тот же культовый Doom требовал целых 4МБ, и школьников из небогатых российских семей это немало огорчало.

Собственно, требования к памяти были ключевым аспектом в конкуренции ОС того времени. Именно они - это то, что не позволило стать лидером на ПК не только ОС с вытесняющей многозадачностью: Unix и Windows NT (хотя последняя, казалось бы - тоже Windows), но и кооперативной, но менее компромисной OS/2 2.0 - у которой, при всех ее достоинствах было минимальное требование 4МБ, в то время как под Windows 3.x можно было вполне продуктивно работать уже на 2МБ.

Ну, и немного про позабытое.

Ну а насчёт "нового и молодёжного" -- это не только виртуализация. Почти всё "новое" реально появилось впервые в 1960-70-х годах. Собственно, только Plug&Play, кажется, и стал реально новым, когда он появился на ПК в 1990-х.

А как насчет механизмов асинхронного ввода/вывода на уровне ядра - и не просто, а с учетом файловой системы? Отложенных процедур обработки прерыванийв NT? Порта завершения ввода-вывода в коммерческих Unix и NT? Структурной обработки исключений, в том числе - и в режиме ядра?

А вот что на ЕС ЭВМ PnP не было - это никаким недостатком не было: если бы PnP была, то не полчилось бы калымить на генерациии ОС :-)

Но с другой стороны, это позволяло сэкономить на длине команды: в System/360 1-байтовых команд не было, а в 8086 - были

Подобную "экономию" надо рассматривать в комплексе. Наличие или отсутствие однобайтовых команд само по себе абсолютно неважно, важна совокупная длина программы, решающей некую задачу. Причём важна и в наши дни: хотя ёмкость ОЗУ можно считать условно бесконечной, ёмкость кэша первого уровня весьма невелика и является одним из основных факторов, ограничивающих производительность.

Ну, VMWare же сделали ;-) Правда есть нюанс - как именно: если на то, что в режиме пользователя программа видит CR0-3 хоста, можно наплевать, то привилегированный режим пришлось поддерживать в режиме интерпретации :-Q

Я понятия не имею, как сделали VMWare и на что они опирались. Но если там приходилось что-то интерпретировать в массовом порядке, это по определению не могло быть эффективным решением -- ну а в VM/370 гостевая система практически не теряла в производительности, если у неё не было собственной виртуальной памяти (если была, то для эффективности нужна была уже специальная поддержка, а без таковой тормоза таки возникали -- но не сказать, чтоб очень большие).

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

Вряд ли виртуализация всей машины сразу предполагалась в Системе 370. Если на то пошло, в Системе 360 команды, считывающие "системные" данные, тоже были привилегированными -- т.е. IBM с самого начала шла правильным путём, полностью запретив пользователю доступ к тому, что его не касается. А в Интел -- просто дебилы, а "не предполагалось". Были б у них мозги -- не было бы, в частности, префиксов для команд, поскольку это исключает возможность быстрого и дешёвого декодирования команды. Не говоря уже о том, что архитекторы должны думать наперёд, а здесь у них даже отмазы в стиле "мы первопроходцы, до нас никогда этого не делали" не было.

Я же не зря уточнял: "с учетом выросших потребностей прикладного ПО". 512 КБ к началу 90-х - хватало уже не для каждой программы даже на однозадачной DOS. А в 90-х и 640К стало достаточно не только лишь всем, в результате куча программ начала использовать DOS extender, тот же культовый Doom требовал целых 4МБ, и школьников из небогатых российских семей это немало огорчало

Не смешивайте одно с другим. Для вытесняющей многозадачности и виртуальных машин памяти много не надо. То, что она требуется определённым прикладным программам, ни разу не увеличивает потребность в памяти для самой ОС.

RSX-11M в полноценном варианте могла работать на машине с примерно 128 Кбайтами ОЗУ (сама система под себя и драйверы пожирала меньше 100 Кбайт). Запустить, например, компилятор Паскаля на такой машине уже не получилось бы: он требовал под себя 64 Кбайта и не полез бы в оставшуюся память (полноценной виртуальной памяти на большинстве PDPшек не было из-за технических ограничений процессора и MMU, лишь на некоторых моделях, в частности, на PDP-11/70, такое можно было сделать). А вот ассемблер или Фортран в оставшийся объём вписывались и работали бы.

Так что не объём памяти был препятствием для нормальных ОС на ПК, а кривая архитектура процессора и компьютера в целом. И, кстати говоря, если RSX-11M хватало под себя условных 100 Кбайт ОЗУ (с вытесняющей многозадачностью и всем таким прочим, включая драйверы устройств), то почему убогой MS DOS и без драйверов (ввод-вывод управлялся кодом BIOS) нужно больше? Не говорит ли это как о качестве системы команд, так и о качестве кода системы?

А как насчет механизмов асинхронного ввода/вывода на уровне ядра - и не просто, а с учетом файловой системы? Отложенных процедур обработки прерываний в NT? Порта завершения ввода-вывода в коммерческих Unix и NT? Структурной обработки исключений, в том числе - и в режиме ядра?

RSX-11M была насквозь асинхронной и на уровне ядра, и на прикладном уровне -- в отличие от всяких Унихов, где асинхронного ввода-вывода для пользователя просто нет. (Да, в POSIX включили, но это не ОС, а стандарт, и далеко не везде эти расширения поддерживаются; в Линухе сравнительно появился IO_URING или как его там -- но, как по мне, оно какое-то... переусложнённое и неудобное).

Механизм отложенной обработки прерываний в NT -- практически 1:1 копия механизма VAX/VMS, а тамошний -- расширение механизма RSX-11M.

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

Структурная обработка исключений -- это вообще про языки высокого уровня (C/C++), которые-то как раз развивались и далеко ушли от раннего Паскаля или Фортрана с Коболом. Я говорю о новых вещах на уровне, в первую очередь, железа, а там с подлинно новым весьма туго, особенно если говорить о концептуальном уровне, а не о реализации. Понятно, что современные суперскалярные процессоры куда круче микроархитектурно, чем первые -- но сама концепция впервые появилась и была воплощена именно в 1960-е.

Даже если взять ту же PCI Express: конечно, ничего аналогичного в 1970-е не было, однако сама идея синхронной последовательной передачи данных по одной линии вместо асинхронной по параллельной не только была в умах, но и на практике такие линии были -- только использовались не в качестве системных шин, а чисто для связи с определённой периферией. Или, скажем, GPU. Их не было, а вот матричные процессоры уже были -- а GPU, по большому, счёту, матричным процессором и является. Точней, несколькими матричными процессорами с кучей локальной памяти и некоторыми узкоспециализированными аппаратными блоками (для отсечения и отбраковки пикселей, не попадающих в кадр, например -- это можно делать и программно, но специализированная аппаратура справляется, понятно дело, быстрей), но базовая идея (широкий SIMD и всё такое) -- та же самая.

Но с другой стороны, это позволяло сэкономить на длине команды: в System/360 1-байтовых команд не было, а в 8086 - были.

Может, в 16-битном режиме это было ещё полезно. В 32 уже сомнительно. В 64 однозначно нет. И сейчас почему-то слишком много однобайтовых кодов занято тем, что ещё в 32 битах надо было за редкостью загнать в 2-4 байта: навскидку по таблице: hlt, cmc, movs, cmps, pusha, popa, push seg, pop seg, lahf, sahf, stos, lods, scas, in, out, ins, outs, far ret, cli, sti, cld, std, stc... только тут уже 36 кодов. Или убрать вообще (xlat и для 8086 был избыточен; в 64 битах слишком мало убрали). А чтобы новое ввести, придумывают всякие EVEX2 префиксы (начала) на 4 байта.

Почему при смене разрядности не перекодировать всё, всё равно ведь старый декодер не смог бы исполнять код в другом режиме? Что Intel, что AMD думали чем угодно, но не головой. AMD простительно - они нищие были и это была отчаянная попытка выжить на минимуме ресурсов. Но Intel сами себе подгаживали чуть менее чем всегда.

Я взял как-то код одного полностью самописного (C++, без ASIO и прочих, на своём движке) прокси и сравнил объёмы кода под разные 64-битные архитектуры. ARM/64 с его тотальной 4-байтностью команд толще x86 на 1%. RISC-V толще процентов на 10 чем x86 без compressed (2-байтовых) команд, но тоньше на четверть при разрешении compressed. SystemZ раза в полтора толще x86, за счёт того, что 64-битные операции все загнаны в 6-байтовые команды - тут уже проблема совместимости, они её так решили.

Совершенно согласен. Можно вспомнить обратный Интелу и ИБМ пример. Когда в ДЕК лепили VAX, "идеологически" они сделали его систему команд расширенной версией PDPшной, но кодировку полностью делали с нуля, поэтому оригинальные PDPшные программы VAX мог исполнять только в специальном режиме совместимости или как там его. Соответственно, не пришлось уродовать кодировку ради этой самой двоичной совместимости, которая реально нафиг никому была не нужна. То же самое можно было бы сделать в 80386: вот вам 16-разрядные режимы (реальный, защищённый 16-разрядный и V86) для старого софта, а вот полностью новый, с новым кодированием команд -- для 32-разрядного. Ну а 64-разрядный от AMD в этом смысле выглядит, пожалуй, даже хуже: полной совместимости они не сохранили (в частности, выпилили поддержку V86 и некоторые команды), а система кодирования стала ещё уродливей.

z/Architecture в этом плане нечто среднее. Кодировка осталась простой, не смотря на все эти расширения, но вот её эффективность, естественно, стала значительно хуже. Как по мне, стоило бы делать полностью новую кодировку, сохранив лишь "идеологию" системы команд, ну а совместимость обеспечивать за счёт режима совместимости (использовать, скажем, биты управления разрядностью адреса в PSW: если 24- или 31-разрядный режим -- работать с кодировкой, как в ESA/390 и более ранних, если 64-разрядный -- в полностью новой кодировке).

Ну а 64-разрядный от AMD в этом смысле выглядит, пожалуй, даже хуже: полной совместимости они не сохранили (в частности, выпилили поддержку V86 и некоторые команды), а система кодирования стала ещё уродливей.

По этому поводу уже высказался не один раз - нищие, которые поставили всё на один кон и выиграли, и при этом имели минимум ресурсов - обвинять их бесполезно. В конце концов, могло быть и хуже (вспоминаем IA64).

Как по мне, стоило бы делать полностью новую кодировку, сохранив лишь "идеологию" системы команд, ну а совместимость обеспечивать за счёт режима совместимости

Ну цена в полтора раза по длине для пользователей SystemZ не страшна.

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

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

Эм.... А с ней что не так?

Полная sequential consistency. В PoO ни слова про модель памяти в плане синхронизации между разными процессорами (ядрами). Барьерных команд нет, барьеры из какого-нибудь C просто компилируются в ноль команд. Значит, реального OoO нету - даже такого с TSO, как в x86.

Программисту удобно, да. Процессору - торможение.

Вообще-то, насчёт синхронизации всё, что нужно, сказано.

Никакого "автоматического" поддержания синхронизации кэшей и буферов между процессорами нет вообще, о чём документация вполне ясно и определённо говорит. Это хорошо для железа и его масштабируемости (не нужно делать схемы для поддержания согласованности кэшей), но создаёт определённые проблемы для программиста: нет гарантии, что, если ты записал нечто в память, об этом точно узнает код на другом процессоре.

Барьеров под таким названием таки нет, но фактически они есть -- это операция сериализации. Она всегда выполняется при любом прерывании, а также при выполнении некоторых команд. В частности, полную сериализацию делают CS, CDS и BCR 15,0.

Поскольку полная сериализация -- операция тяжёлая (запись всех изменённых строк кэша в ОП, после чего аннулирование всего кэша, чтобы изменения в памяти, внесённые другими процессорами и подсистемой ввода-вывода, были получены данным процессором), в z/Architecture ввели ряд дополнительных команд, выполняющих частичную сериализацию -- в пределах их операндов (по сути, они записывают изменённую строку кэша, содержащую операнд, в память, после чего аннулируют её).

В общем, не знаю, с чего Вы взяли, что этого нет.

фактически они есть -- это операция сериализации

В книге "Принципы работы системы IBM/370" этот термин перевели как "временная отмена совмещений". Получилось длиннее, но, на мой взгляд, точнее.

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

Вообще-то, насчёт синхронизации всё, что нужно, сказано.

Можно указания на конкретные утверждения в Principles of Operation?

В общем, не знаю, с чего Вы взяли, что этого нет.

Если всё так, как вы говорите, то из-за специфической терминологии IBM, которую я просто не опознаю. И потому прошу точные ссылки.

Ага, спасибо. Таки проблема была в терминологии - просто не по тем словам искал (а serialization слишком широкое и по нему и не попробовал). Описанная синхронизация грубовата - в частности, acquire/release пометки, типичные для остальных, не реализованы - но менее жёсткая, чем у x86, который не только дофига полностью сериализующих операций ввёл, но и у него каждая запись release и каждое чтение acquire в отношении одной команды к другим.

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

В "Сериализации ЦП" в замечаниях по программированию есть мелкий примерчик, приведу его изложение здесь, чтоб не заставлять лишний раз доку листать :)

Процессор 1 выполняет команды:

MVI A,X'00'
BCR 15,0

Т.е. он записывает нуль в байтовую переменную A, после чего выполняет принудительную сериализацию (BCR 15,0), что гарантирует, что запись в A будет полностью завершена, и нуль попадёт в соответствующий байт физической памяти.

Второй процессор выполняет команды:

G CLI A,X'00'
  BNE G

Т.е. он сравнивает содержимое A с нулём и при неравенстве повторяет этот цикл.

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

Соответственно, этого примера достаточно, чтобы утверждать: архитектура не требует аппаратной поддержки согласованности содержимого кэшей. Понятно, что для программиста это менее удобно: в многопоточной программе нужно самому выполнять правильные телодвижения. Но это обеспечивает масштабирование системы: не требуется связь процессоров по схеме "все со всеми". Поэтому сделать мэйнфрейм с буквально 1М процессоров принципиально возможно, а ПК -- нет, ПКшные процы и сейчас-то занимаются, главным образом, согласованием своих кэшей.

Что касается грубости механизма, без acquire/release. Так оно и есть, но тут уже дело в древности архитектуры: принципы сериализации ничем не отличаются от таковых в Системе 370 (были ли они чётко прописаны в 360, я не помню, а смотреть лень), появились лишь новые команды, более подходящие для определённых случаев (скажем, когда полная сериализация не нужна, а нужно обеспечить её лишь в пределах небольшого участка памяти). Понятно, что разработчики какого-нибудь там RISC-V в этом плане находятся в выигрышной позиции: могут учесть весь опыт прошедших десятилетий (хорошо ли учли, понятия не имею, но это уже другой вопрос).

приведу его изложение здесь, чтоб не заставлять лишний раз доку листать :)

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

(Кстати, PoO меня сбивает с толку ещё и такой мелочью, но очень досадной, как двухколоночная печать на странице. Читать такое с экрана неудобно, и это ещё один фактор к утяжелению с непривычки. Вся остальная документация от всех прочих имеет одну колонку на странице... зачем IBM так делает?)

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

Ну если совсем формально - для остальных архитектур тут тоже нет совсем идеальных гарантий в общем случае. В данном случае им "помогает" зависимость по управлению (control dependency) - сама операция следующего чтения зависит от того, состоялся ли переход на новую итерацию... но это уже особенности конкретных ISA. В C/C++ это вообще сверхжёстко: там определена гарантия sequenced-before через операции store-release и load-acquire на том же адресе (можно усилить до мьютекса); если разные адреса - то гарантий нет. Я не знаю, на какой архитектуре это может не соответствовать, но предполагаю что-то с NUMA.

Ещё вот что удивляет. Представим себе код типа

extern int f;
std::atomic<int> mx;
void sf_unlock() {
  f = 1;
  mx.store(0, std::memory_order_release);
  f = 2;
}

Если я ставлю условие записи relaxed или release, команда синхронизации (BCR 14, 0) не вставляется после неё записи 0 в mx. Только если я усиливаю до acq_rel или seq_cst, её добавляет компилятор. Разве не надо было по описанному ставить её на любую атомарную операцию, даже при relaxed? Прав gcc (кросс 9.5.0 из Ubuntu), что так делает, или нет?

_Z12sf_unlock_arv:
        lgrl    %r1,f@GOTENT
        mvhi    0(%r1),1
        larl    %r2,.LANCHOR0
        mvhi    0(%r2),0
        bcr     14,0 ; <-- отсутствует если ниже acq_rel
        mvhi    0(%r1),2
        br      %r14

И почему `bcr 14, 0`, если в доке её действие определено только "When the fast-BCR-serialization facility is installed"? Считается, что эта опция гарантированно есть?

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

Ну не так грубо, но заметные затраты на это таки есть. NUMA придумывали как раз чтобы сократить затраты на такую синхронизацию между доменами (для современного x86 это означает - между камнями). Подход S/370 в этом плане изначально максимально масштабируем, да, но цена в виде полного сброса кэша на чём-то сложнее одиночной атомарной операции (даже не CAS) мне не нравится.

Понятно, что разработчики какого-нибудь там RISC-V в этом плане находятся в выигрышной позиции: могут учесть весь опыт прошедших десятилетий (хорошо ли учли, понятия не имею, но это уже другой вопрос).

Учли по максимуму, насколько я могу понять. По крайней мере на уровне устоявшейся теории на ≈2011 год.

Читать такое с экрана неудобно, и это ещё один фактор к утяжелению с непривычки.

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

И почему `bcr 14, 0`, если в доке её действие определено только "When the fast-BCR-serialization facility is installed"? Считается, что эта опция гарантированно есть?

BCR 15,0

15 - это битовая маска, которая означает переход по любому коду условия. Это не опция, эта особенность была ещё в System/360, просто нормально описана уже в 370, когда кеши и конвейер стали более распространенными.

BCR 15,0

Нет. Цитирую ту же PoO:

Serialization is performed by CPU reset, all interruptions, the entering or leaving of the transactional-execution mode, and the execution of the following instructions:
- The general instruction BRANCH ON CONDITION (BCR) with the M1 and R2 field containing 1111 binary and 0000 binary, respectively.
- When the fast-BCR-serialization facility is installed, the general instruction BRANCH ON CONDITION (BCR) with the M1 and R2 fields containing 1110 binary and 0000 binary, respectively.

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

Так вот - gcc компилирует с 14, а не с 15! Я показал его вывод. И вот потому у меня там возник вопрос - почему он считает, что fast-BCR-serialization facility гарантированно есть? Возможно, какой-то другой документ утверждаёт её обязательность, но не гуглится.

К слову, с экрана телефона двухколнонный набор читать удобнее.

Вы имеете в виду, что колонка у́же и потому помещается? Но переходить между колонками ещё больше неудобно, чем на устройстве с мышкой. Так что принципиально лучше не становится.

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

Пару недель назад на другом форуме обсуждал этот вопрос, так посмотрел и как сейчас книги печатают, и документация от всех прочих (начиная с Intel). В технических текстах сплошь одна колонка, ширина до 130 символов никого не смущает. В художественных одна колонка, ширина 50-70 символов. Две колонки - норма в научных публикациях и в словарях (и то не всех). Я как-то не думаю, что PoO это научная публикация или словарь ;\

Обе команды - изначально NOP (регистр 0 в цели перехода),

Не совсем так. В качестве NOP изначально рекомендовали использовать "BCR 0,R2" либо "BC 0, address". Операции "BCR R1,0" просто не выполняли переход, но это не означает что они не делали чего-то ещё.

Возможно, какой-то другой документ утверждаёт её обязательность.

Да, и это гуглится:

z/OS V2R3

Minimum server levels and machine facilities that are required for z/OS

45

Distinct-operands, fast-BCR-serialization, and other facilities.

Т.е. эта опция заложена в целевую платформу компилятора.

Две колонки - норма в научных публикациях. Я как-то не думаю, что PoO это научная публикация ;\

Ну в данном случае мы имеем дело в нормами публикации в корпорации, созданной в начале XX века. Я уже не говорю о том, что IBM сама по себе изрядная "консерва".

BCR 15,0

15 - это битовая маска, которая означает переход по любому коду условия. Это не опция, эта особенность была ещё в System/360, просто нормально описана уже в 370, когда кеши и конвейер стали более распространенными.

Нет, речь именно о BCR 14,0 -- расширении, добавленном в z/Architecture.

Ну если совсем формально - для остальных архитектур тут тоже нет совсем идеальных гарантий в общем случае

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

Считается, что эта опция гарантированно есть?

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

цена в виде полного сброса кэша на чём-то сложнее одиночной атомарной операции (даже не CAS) мне не нравится

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

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

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

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

Там чуть сложнее, потому что
1) В вариантах общего протокола MESI может быть не аннулирование, а создание копии изменённой строки.
2) Формально каждое чтение acquire, то есть следующие чтения должны выполняться с нуля после него (запросом в контроллер доступа к памяти), но делают до какой-то степени предвыборку с оповещением об обновлении со стороны кэша.
Но для простоты (эквивалентно с поправкой на эффективность) можно сказать и так.

У большинства других популярных ISA точно так же.

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

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

Появляется при переходе от z10 к z196. Наверно, таки где-то объявлена обязательной. Но тут тогда в доке gcc ошибка, сказано, что умолчание - z900, а при явном указании z900 компилируется 15, а не 14.

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

Тут всё относительно. При кэшах толщиной в дофига (сколько там на типичном топовом процессоре x86 или ARM, L3 уже под сотню мегов может подходить?) сброс и перезаполнение это очень серьёзно, может и не оправдываться... там ведь, сколько б мы тут все ни ругали x86, настолько глупые решения не делают.

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

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

При кэшах толщиной в дофига (сколько там на типичном топовом процессоре x86 или ARM, L3 уже под сотню мегов может подходить?) сброс и перезаполнение это очень серьёзно, может и не оправдываться...

Тут есть вариант. Предварительные соображения такие. 1) "Кэш толщиной в дофига" -- это третий и последующие уровни, и такие кэши общие для нескольких процессоров (ядер). 2) У мэйнфреймов вся память -- это память, без всяких побочных эффектов и т.д. и т.п., поэтому нет нужды индивидуально настраивать процессоры, что вот эту область кэшируем как WB, эту -- как WT, а вот эту вообще не кэшируем.

Можно поддерживать согласованность лишь внешних кэшей большого объёма, но никак не согласовывать кэши первых двух уровней, принадлежащих конкретному процессору. Тогда сериализация будет требовать очистки лишь этих кэшей и, во-первых, станет намного дешевле из-за их объёмов, а во-вторых, никак не будет сказываться на работе соседних процессоров, потому что общий большой кэш не затронут (его содержимое отражает реальное содержимое памяти из-за наличия механизма согласования).

Ну и, наконец, я бы добавил команду сериализации области памяти -- дополнил бы нынешние команды "атомарной сериализации", выполняющей её лишь для своих операндов. Тогда поток, окончив формировать что-то для других потоков, может сериализировать лишь область общих данных, не затрагивая всё остальное -- а это потенциально может оказаться сильно быстрей. Естественно, старое ПО по-прежнему использует команду BCR 15,0, и всё работает -- просто менее эффективно (примерно как PTLB и IPTE: очистка всего TLB вообще, которую ввели вместе с динамической переадресацией, и очистка конкретного элемента таблицы страниц, которую добавили в поздних 370).

Можно поддерживать согласованность лишь внешних кэшей большого объёма, но никак не согласовывать кэши первых двух уровней, принадлежащих конкретному процессору.

Вот я совсем не представляю себе, как делать в таком варианте "согласованность лишь внешних кэшей большого объёма". Или мы показываем, где сериализуем, или нет. Кэши - они будут локализованы для той памяти, которую обслуживают, или для того процессора, что её использует?

В системах прочих вендоров - для краткости, пусть они будут "MESI-based" - можно решать вопрос иерархией NUMA доменов произвольного количества уровней иерархии и оптимизацией путём переноса используемой памяти как можно ближе к исполнителю. Да, при этом при перемещении между процессорами требуется миграция в памяти, потребуется доработка MM слоя ОС на тему даже одинаковые куски памяти (как код libc) копировать, чтобы сокращать расстояние до хранилища, и ещё наверняка есть много тонкостей - но это всё подъёмно. Вопрос в количественных характеристиках - выше при этом производительность или нет, по сравнению с вариантом, что мы тут обсуждаем для S/370 с потомками. Мне всё-таки кажется, что выше, иначе бы кэши по 100MB не делали.

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

Это потребует переделки компиляторов и тотальной доработки кода, мне кажется. Нынешняя release consistency, например, основана на том, что когда store-release сериализует операции, она сериализует все операции, которые были заказаны до неё, неважно, относилось это к разделяемому участку памяти или нет. А если мы требуем это только для конкретных адресов - описать-то теоретически можно, но количество ошибок при таком, мне кажется, будет зашкальным. Представим себе, что под мьютексом модифицируется мапа (hashtable-based) со ссылками на значения-объекты, а объекты тоже сложной структуры... там никто не сможет вычислить, что именно надо помечать на синхронизацию, будет тотальная команда "синхронизировать всё". Что store-release соответственно и делает.

А точечные атомарные операции и так есть - по описанию, всякие ASI, NI, LAA и прочие это уже делают. Странно, что для CAS нету такого варианта... ну так и в x86 не завезли.

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

Именно. "Критерий истины - общественная практика."(с) А разговоры о техническом совершенстве - это то же самое, что разговоры в пользу бедных.

Когда в ДЕК лепили VAX, "идеологически" они сделали его систему команд расширенной версией PDPшной, но кодировку полностью делали с нуля,

Знаете, а Intel тоже примерно так и сделал переход с 8080 на 8086: сохранил, насколько это было возможным, мнемоники команд и операндов, но бинарные коды сделал другими. Задумка была, вероятно в том, чтобы облегчить перевод ассемблерных программ. Ну, а для поддержки специального режима для бинарной совместимости, ресурсов, видимо, не хватало: у DEC для VAX был целый шкаф, а у Intel - один чип, с весьма ограниченным в то время числом транзисторов. И дальше, видимо, желание экономить имело существенную причину: транзисторов в чипе стало больше, но экономить их всё-таки приходилось. Ну, а отказываться от бинарной совместимости на тот момент был явно не вариант: программы для ПК продавались в бинарном виде.

Так что рулит не эстетика, а экономика.

Насчёт перехода с 8080 на 8086 -- да, транзисторный бюджет был сильно ограничен. Но это не относится к 80286 -> 80386, там другой блок декодирования не слишком бы раздул процессор по сравнению с его уже имеющимся размером. Ну а про AMD64 вообще молчу.

Может, в 16-битном режиме это было ещё полезно.
Дык, для него и делали. 32-бита и более были тогда далекой перспективой. И специализированные однобайтовые команды, которые можно заменить более длинными универсальными (и которые уже на 80486 работали медленнее, чем универсалььные аналоги) - тоже для него, 16-битного.

Ну, а дальше бинарную совместимость поддерживать пришлось: потому что массовые программы стали, к большому огорчению Столлмена, тиражируемыми программными продуктами, распространявшимися в бинарном виде. Потому и сохраняли возможность работы со старыми режимами процессора, и, видимо, из-за этого не стали усложнять декодер команд, чтобы он, в целом, работал примерно одинаково во всех режимах (но это неточно, это- всего лишь мое предположение). И, для тех, кто хочет сказать, что сейчас все равно эти команды перекодируются в микроооперации, напомню, что идея перекодировки пошла в массы лет на десять позже, чем 32-битный режим.

Совместимость приходится сохранять и сейчас. К примеру, хотя 16-битные программы Windows ушли из широкого применения, и стало возможным выкинуть многие навороты с сегментацией от 80286, но полноценную поддежку для FS и GS AMD пришлось сохранить, потому что на наличие этих сегментных регистров и их работоспособность в защищенном режиме опирается структурная обработка исключений (SEH) в Windows (причем, не только в пользовательском режиме, но и в ядре).

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

попробуйте, хотя бы, определить длину команды -- с учётом всех этих идиотских префиксов

Префиксы не сильно мешают, если их сделать грамотно. Грамотно - это, например, так (конструирую наобум, для примера): представим себе, что команда состоит из нескольких чанков, все кроме последнего - префиксы. Каждый чанк, в установленном порядке бит, начинается с: 0 - 2 байта, 10 - 4 байта, 110 - 6 байт, и так далее (в принципе неограниченно). Дальше 4 бита: 0000...1101 - обычные команды, неизвестная команда вызывает исключение. 1110 - распределяется вендором (но длина задана раньше и мы можем на ней основываться). Если 1111 - дальше ещё два бита: 00 - префикс, неизвестный вызывает исключение; 01 - префикс, неизвестный игнорируется; 10 - главный чанк, неизвестное значение выполняется как NOP; 11 - ну, оставим в резерв для чего-то особого.

Это даёт однозначную расшифровку с учётом всего-всего (даже если не знаем, что это было) и возможность, в частности, добавлять команды, которые игнорируются младшими моделями. В истории уже несколько таких случаев: куча разных барьеров - последовательные процессоры без конвейера и OoO исполнения просто игнорируют; endbr для защиты от return-oriented hacking; инвалидация кэшей; префетч; хинты всех видов; возможно, ещё что-то забыл. Ну а простой вариант разбора длины команды даёт гарантию совместимого расширения и резкое упрощение декодера.

половину системных по своей сути команд сделали непривилегированными.

Там не половина, там сильно меньше - штук 7. Но да, и их достаточно, чтобы попортить жизнь.

Мотивация авторов x86 почти во всех решениях... мнэээ... сомнительна. И я пока не увидел, кто её такой задавал (конкретное имя) - не святой же дух приносил им эти решения...

Мешают префиксы, мешают. Простой и быстрый декодер сделать попросту невозможно: нужно сразу анализировать много байтов, полученных из памяти (из кэша), а это дорого в плане железа. Ну а для суперскалярных процессоров -- ещё сложней. В этом плане мэйнфреймы -- почти прямая противоположность: хоть длина команды и переменная, устанавливается она элементарным анализом двух старших битов старшего байта, а соответственно, выбрав из памяти, скажем, 16 байт, можно "лёгким движением руки" разобрать их на команды.

Ну так и в S/360 с потомками декодер по полусловам будет в большинстве случаев находить ошибочно начала команд, и только когда будет известно, от какой позиции разбирать, станет понятно, какие разборы были правильными, а какие ошибочными. Никакой разницы. Это только всяким MIPS со всегда 4 байта на команду хорошо, но эта система нерасширяема.

Вот если бы применяли ещё подход в стиле utf-8, то там можно было бы с любой позиции начинать анализировать. Но такой архитектуры я ещё не видел.

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

Разница громадная. Декодер в S/360 способен сразу определить длину текущей команды, а соответственно, с минимальной задержкой определить адрес следующей, из неё -- её длину и адрес следующей и т.д. -- и всё это с очень малыми затратами "железа". А в IA-32 затраты на то же самое будут кошмарными (собственно, они такие и есть). Попробуйте нарисовать схему декодера для S/360 и даже для 8086 -- сразу разницу увидите.

Декодер в S/360 способен сразу определить длину текущей команды

Ну так я образчик такого подхода и предлагал.

А в IA-32 затраты на то же самое будут кошмарными (собственно, они такие и есть).

На самом деле не настолько. Его уже достаточно хорошо вылизали. Есть форумы, где вопрос "насколько неровность кодировки в x86 дороже ARM?" (ARM берётся, потому что много где, включая Apple) обжевали со всех сторон. Усреднённый вывод - всё это максимум пара процентов от суммарных затрат процессора. И это, да, даже с тем кошмаром, что в x86. Ну а если что-то поаккуратнее - так и с микроскопом не будет рассмотреть.

Если вы твёрдо знаете, что это "кошмарные" выливается в какие-то конкретные цифры, а не просто личные впечатления "ужас-ужас!" - поделитесь ссылкой. Мне будет крайне интересно.

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

Вы по-прежнему обсуждаете возникновение системы команд x86 в том виде, в котором она возникла? Тогда о каком кэше речь идет? Не было его тогда на ПК и других системах того же уровня. Что было - так это буфер превыборки команд (6 байт в 8086 или 4 - в 8088), безо всякой дальнейшей синхронизацией с памятью. На этом буфере были основаны популярные тогда трюки обнаружения работы под отладчиком в пошаговом режиме путем перезаписи следующих после команды байтов: при нормальной работе процессор эту перезапись не увидит, а в пошаговом режиме, после возврата из прерывания - на нее наткнется. И до суперскалярных процессоров была целая вечность (даже 80486, где появилась конвейерная обработка, появился более чем на 10 лет позже). И скорость доступа к памяти в общем случае тогда процессор не ограничивала: на выполнение каждой команды тратилось довольно много тактов, а тактовая частота была невысокой. Так что кэш на ПК - это уже для эпохи 80486 актуально, когда все решения уже были приняты и программы написаны и куплены пользователями.

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

2) Суперскалярность появилась в 1960-х годах.

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

А я - о том, почему Intel делала в таком виде систему команд 8086 - 80486.
А про AMD64: такое кодирование реально чему-то существенно мешает, с учетом текущей реальности: того, что декодер по факту выполняет трансляцию команды в набор микрооопераций (для чего все равно желательно извлечь всю команду), и с учетом того, что декодер фактически выполняется в отдельном параллельном потоке выполнения? Есть серьезные подозрения, что - нет, т.е. существенных помех для декодирования унаследованной структуры кооманд нет.
И ещё одно соображение в духе принципа "всё действительное разумно": AMD не была 00-е компанией, определявшей архитектуру х86. Такой компанией была Intel, которая имела возможность все исправить, если бы увидела, что решение от AMD неэффективно. Но - не воспользовалась. Возможно - потому что другие соображения, типа упрощения модификации компиляторов, оказались важнее? Я точного ответа на это вопрос не знаю, если честно, но a priori я предполагаю, что решение принимали не дураки.

  1. Суперскалярность появилась в 1960-х годах.

Где? Ещё на CDC или уже на Cray? В любом случае - не важно, где именно, от суперскалярных хоть в какой-то мере x86 (Pentium AFAIK) их отделяет примерно четверть века, а я речь завел про него. А на практике, кэш начал масово требоваться ЕМНИП для конвейерного 80486 - чуть пораньше, но тоже уже в 90-х. И я - об этом.

такое кодирование реально чему-то существенно мешает

Да, мешает. Ещё раз повторяю: попробуйте нарисовать схему декодера даже для 8086 и схему для, скажем, современной z/Architecture со всеми её 64-разрядными расширениями (а не для исходной Системы 360). Разница в сложности будет во много раз. (Кстати, Валерий Черепенников @vvvphoenix в какой-то из своих статей про его будни в Интеле написал, как он быстро слепил на Верилоге декодер для ARM, но для Интела не осилил даже за намного большее время.)

Ну а 1% и всё такое прочее, про что Вы говорили, -- это по сравнению с процессором в целом, со всеми его гигантскими кэшами и т.д. и т.п. Так рассуждать некорректно: дело не в количестве транзисторов как таковом (крупный кэш всё равно больше места займёт), а в скорости работы. Чтобы запускать за такт несколько команд (суперскалярное выполнение), надо их успевать декодировать с соответствующей скоростью -- и для большинства архитектур это много проще, чем для IA-32/AMD64, и в первую очередь как раз из-за префиксов, делающих длину команды совершенно неопределённой. Пока не проверишь все префиксы, ты не можешь приступать к собственно команде, а заодно не узнаешь, где же начинается следующая команда (которую ты тоже должен декодировать в этом же такте, и следующую за ней тоже, если хочешь запускать 3 команды за такт) -- а количество префиксов ограничено лишь максимальной длиной кода команды, т. е. 15 байтами. (Там ещё дополнительные сложности есть; например, некоторые байты, воспринимаемые в 16/32-разрядных режимах как код команды, в 64-разрядном будут восприниматься как префикс, но это уже мелочи.)

Где? Ещё на CDC или уже на Cray?

На CDC. А первое внеочередное выполнение, правда, только для вещественных операций, -- одна из Систем 360, точно не помню, какая (но, кажись, 1969 год).

Кэш на ПК -- да, на некоторых 80386 уже был, но не на всех. На 80486, вроде б, был уже встроенный кэш в несколько килобайт? Но, опять-таки, в мире кэш появился намного раньше. У нас недокэш точно был на БЭСМ-6 -- этим отчасти компенсировалось отсутствие у неё регистров общего назначения как таковых (индексные регистры для адресации массивов плюс единственный аккумулятор для собственно вычислений); в более-менее массовых количествах он стал использоваться в поздних ЕСках и СМках (ЕС-1036, ЕС-1046, СМ-1420 с процом СМ-2420.01, но, вроде, не с исходным СМ-2420, и т.д.) -- это примерно середина 1980-х (сами машины появились раньше, но пока их произвели в достаточно товарных количествах...). У буржуинов, понятно, он появился ещё раньше.

А про AMD64: такое кодирование реально чему-то существенно мешает?

​1) Развитию. Вон например ENDBR32, ENDBR64 оказались специфичными вариантами совсем другой команды при NOP-комбинации. А если бы такой не было? А переиспользование префиксов под совершенно другие назначения, чем их исходное? Всё это от бедности оставшихся ресурсов.

​2) Таки удорожает декодер (даже если это 0.5% от общей цены) и удлиняет сами команды (вон для APX придумывают 4-байтные префиксы только чтобы иметь возможность втиснуться в уже заполненное пространство). Сейчас и польза от него сомнительна.

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

Сейчас ссылку не найду, но писалось, что после принципиального краха IA64 и победы AMD64 - Intel захотела сделать своё в традиционном OoO-стиле, но перекодировав с нуля в экономном варианте... и тогда им MS сказала, что они это принимать не будут и Intel пойдёт лесом. И тогда у Intel не осталось варианта кроме как принять разработку AMD (названную сначала EM64T, а потом "гордо" Intel 64) - вылетать с рынка было бы неприятно.

но a priori я предполагаю, что решение принимали не дураки.

Угу, только соображения тут были уже не технические.

Где? Ещё на CDC или уже на Cray?

https://en.wikipedia.org/wiki/Tomasulo's_algorithm : "and was first implemented in the IBM System/360 Model 91’s floating point unit." - 1968.

Хотя полноценный вариант я бы отсчитывал от precise exceptions, а это, согласно вики, 1984. На CDC, хоть и позднем. В микропроцессорах это 1990, POWER, и только через три года в сильно урезанном варианте у Intel.

А на практике, кэш начал масово требоваться ЕМНИП для конвейерного 80486

Конвейер был уже в исходном 8086, на 6 байт следующих команд. Я не знаю, почему вы говорите про 80486. Там могли что-то существенно улучшить, но он точно не был первым.

Если речь про кэш памяти, то он был внешним средством для некоторых систем на 286, а в процессоре стал появляться в 386. Причина - время на смену строки в DRAM модуле составляло около 70 нс, а старшие 386 были уже 33МГц и выше, и кэш, хотя бы минимальный, стал полезен. Дальше его только расширяли и углубляли. Сейчас смена строки в лучших модулях DDR5 это около 30нс, процессор за это время может успеть выполнить полторы сотни тактов - без кэша тупо не будет работы.

Конвейер был уже в исходном 8086, на 6 байт следующих команд.

Не было там настоящего конвейера, там был буфер предвыборки (6 байт в 8086, 4 байта в 8088, если правильно помню) -- но это ещё не конвейер как таковой, ведь выполнение разных стадий разных команд во времени не совмещалось -- не считая как раз предвыборки нескольких следующих байтов без каких-либо дополнительных действий с ними. В настоящем же конвейере одна команда выбирается, другая декодируется, третья выполняется, для четвёртой записываются результаты...

Если на то пошло, "буфер предвыборки" был и на ЕС-1022, и на ЕС-1033, но конвейерными они ни разу не были. Просто они, в отличие от ЕС-1030, не "забывали" "лишнее полуслово", прочитанное из памяти при выборке предыдущей команды (если в одном прочитанном слове лишь одно полуслово относилось к текущей команде, а следующее было следующей командой или её началом, при начале выборки следующей команды использовалось именно это полуслово, а не производилась повторная выборка из памяти -- т.е., по большому счёту, то же самое, что в 8086/88).

Впрочем, тогда ещё не было очевидно, лучше ли она страничной или хуже, так что это IMHO было простительно.

Ну да, в пдп-11 к тому времени уже 10 лет была страничная организация и оси под это (тот же ранний unix), а этим было "не очевидно".

Просто там какие-то програмисты-фрики-перфекционисты решали, по-видимому: слухи, что якобы архитектуру 8086 разрабатывал программист, ужаснейший iapx432 который может показаться прикольным тоже только фрику-программисту, ну и сами "замечательные" сегменты вместо страниц в 286. Что там ещё, "замечательный" стек FP-регистров в 8087 например. По последнему можно вспомнить как Дейкстра, что ли, очень восхищался архитектурой burroughs, чисто стековой.

Ну, справедливости ради, на PDP-11 была не совсем страничная организация в том смысле, как обычно её понимают: там MMU работало по-другому (и это правильно, учитывая, что машинка была 16-разрядная, а не 32, как IBMовские мэйнфреймы). С точки зрения лёгкости управления отображением памяти и его влияния на производительность машины PDPшная схема лучше, но она не масштабируется с увеличением разрядности адреса.

Но таки да, виртуальную память со страничной организацией уже вовсю обкатали, как минимум, на Системе 370, где соответствующие ОС (OS/360 SVS и затем MVS и VM/370) появились в самом начале 1970-х, и уже было понятно, что оно работает эффективно, но требует некоторых улучшений. В частности, в поздних Системах 370 появилась команда IPTE -- INVALIDATE PAGE TABLE ENTRY -- чтобы запрещать доступ к определённой странице и одновременно чистить TLB от копий именно запрещаемого элемента (до этого была только команда PTLB, которая очищала весь TLB, что, естественно, не способствовало сохранению производительности).

Так что Интел могла бы уже на 8086/88 предусмотреть управление памятью в стиле PDP-11: для 16-разрядных адресов это куда эффективней со всех точек зрения, чем уродливые сегменты. Собственно, в Z8000 нечто аналогичное было сделано; правда, реализовано было с помощью дополнительной микросхемы, но, как по мне, это вполне разумно: сам проц 16-разр, ну а раз всё "на борт" впихнуть нельзя, оставьте решение за разработчиком компьютера. Никого ж не смущало, что FPU долго ещё был отдельной микросхемой, так почему для MMU это не разрешить?

Но таки да, виртуальную память со страничной организацией уже вовсю обкатали, как минимум, на Системе 370, где соответствующие ОС (OS/360 SVS и затем MVS и VM/370) появились в самом начале 1970-х

Сегментная архитектура памяти, тем не менее, была популярна, по крайней мере, у теоретиков: вспоминаю переводную книгу (перевели ее в начале 80-х, оригинал, скорее всего, был издан в конце 70-х) где вовсю рассматривали и сегментную организацию памяти, и кольца защиты из проекта Mutics - всё то, что Intel реализовала в 80286. То есть, этот процессор был сделан по тогдашней моде, и никто не знал, что правильный путь - другой: линейно адресуемая виртуальная память, своя для каждого процесса, Unix way, кароче. Зато сейчас все умные.

С IBM тоже не всё просто. Виртуальная память была ещё в старшей модели (67, в СССР ее аналогов AFAIK не было), и именно от нее растут ноги у VM/370 (это - наследник экспериментаьной ОС для этой модели). А массовая линия развития ОС от IBM была другой - режим SVS OS/370, где память хоть и была виртуальная, но - с единым адресным пространством для всех параллельно выполняющихся программ которым отводились свои разделы, как и в OS/360. В СССР это была ОС ЕС 6. А потом уже появилась MVS - фактически, новая система но под тем же именем, скопировать которую в СССР ниасилили.

Ну и, в копилочку о про "прозорливость" IBM и ограничение доступа к привилегированным частям системы (сразу не написал, напишу сейчас). Там тоже было не всё так однозначно: к примеру, вся память на System/360 (и, соответсвенно, ЕС ЭВМ Ряд 1) была доступна для чтения любой программой, ключи защиты блокировали только возможость доступа. Я сам в институте так на ЕС-1022 хакерствовал: писал в составе группы товарищей программу, которая из фонового раздела читала и печатала содержимое памяти раздела переднего плана, в котором работала программа, управлявшая терминалами, с целью найти способ работать с этими терминалами (способ, кстати, был найден, но это уже совсем другая история).

В СССР это была ОС ЕС 6. А потом уже появилась MVS - фактически, новая система но под тем же именем, скопировать которую в СССР ниасилили.

Не то чтобы не осилили скопировать саму систему... От сотрудников НИЦ ЭВТ я слышал такую версию: MVS намного более требовательна к железу, намного сильнее трясет его по сравнению с VM, и соответственно выявляет больше аппаратных сбоев. Надёжность нашей техники справедливо сочли недостаточной и сделали ставку на СВМ.

К слову сказать, на рубеже 80/90-х IBM также продвигали VM.

Ну, не Unix way, поскольку на Системе 370 (даже если не рассматривать полуэкспериментальную модель 360/67) виртуальная память появилась раньше (MMU к PDP-11 не сразу прикрутили).

вся память на System/360 (и, соответсвенно, ЕС ЭВМ Ряд 1) была доступна для чтения любой программой, ключи защиты блокировали только возможость доступа

Вообще, ключи защиты памяти могут блокировать доступ полностью (запрещая чтение). Если Ваша программа, работая не с нулевым ключом, могла читать чужую память, -- это проблемы ОС, а не железа. Речь, вероятно, о ДОС ЕС (DOS/360) -- как там с защитой, я понятия не имею. В ОС ЕС этот номер не прошёл бы.

А потом уже появилась MVS - фактически, новая система но под тем же именем, скопировать которую в СССР ниасилили

На чём её было гонять? На самой массовой машине Ряда 2 в лице ЕС-1035 с её памятью в 1 или 3 Мбайта и убогой производительностью? Ну а ЕС-1060 и старше, которые были более-менее быстры и имели приличный объём ОП, было крайне мало. Так что не думаю, что "ниасилили" -- не на чем эксплуатировать было.

В ОС ЕС этот номер не прошёл бы.

Увы, именно в ней. Несмотря на то, что аппаратные средства защиты от чтения ОП имелись, ни OS/360, ни OS/VS, ни ОС ЕС ими не пользовались. Причина в том, что для уточнения деталей программам приходится читать те или иные поля из системных блоков: CVT, TCB и др. Это была официальная политика, и выпускалась документация где и что можно найти. Вот в SVS залезть в адресное пространство другого задания не получится, но это Ряд-2 и динамическая переадресация.

Хм... Надо будет как-нить на Херкулесе попробовать.

Но переадресация в SVS особо не поможет: это ж, по сути, MVT, только с размещением разделов в виртуальном адресном пространстве, общем на всех, а соответственно, таблицы сегментов и страниц, по идее, общие. Но вообще без понятия, как точно они это реализовали

Хм... Надо будет как-нить на Херкулесе попробовать.

Я совершенно серьёзно, для меня в своё время был культурным шоком факт, что ОС ЕС не запрещает залезать в чужие области памяти. По всей вероятности, дело в том, что аппаратные средства защиты от чтения появились позже самих ключей защиты памяти, и ко времени выхода OS/360 были не на всех машинах.

Но вообще без понятия, как точно они это реализовали

Это документировано. SVS использует два ключа защиты памяти: 0 для системных областей и 1 для всех пользовательских. Поэтому защитить от доступа к чужим разделам можно только через таблицы переадресации, которым всё равно, чтение это или запись.

Уточнение: для разделов V=R используются другие ключи защиты, тут как раз полное моделирование MVT для тех программ, которые почему-то отказываются жить с виртуальной памятью.

Я совершенно серьёзно, для меня в своё время был культурным шоком факт, что ОС ЕС не запрещает залезать в чужие области памяти.

Может от версии зависеть. Мне говорили, что в поздних 6.1 защита есть, а в ранних (только не помню уже, ранние 6.1 или вообще 4.1) не было. Тот, кто сообщал, использовал это в хакерских целях (лазил к соседям портил код в рамках одного Primus;)) и обломился при обновлении версии. Но для меня это всё на уровне слухов.

Тут слишком много всякого смешалось. Защита от записи в чужие области была и в 4.1, но в поздних 6.1 убрали дырку в системном вызове FREEDBUF, которая позволяла получить нулевой ключ защиты любой программе. (И, соответственно, давала возможность обходить защиту от записи).

P.S. Я писал раньше как "работала" эта дырка.

Собственно, в Z8000 нечто аналогичное было сделано; правда, реализовано было с помощью дополнительной микросхемы

Что интересно, на ПК такое тоже было - EMS называлось. А потом появились драйверы - EMM386 и QEMM - которые реализовывали такое с использованием встроенного в 80386 режима v86.

Насколько помню, EMS -- это банки памяти, доступные по адресам ниже 1 Мбайта и поэтому пригодные для использования и на компах с 8086/88. Та память, которая выше 1 Мбайта, а значит, 80286+, -- это XMS. Но из-за отсутствия в 80286 режима V86 в ДОСе использовать её было невозможно (не считая первых 64 Кбайт без 16 байт из-за железной ошибки, позволявшей в реальном режиме получать адреса 10'0000-10'FFEF.

В 1990х (от массового распространения процессоров 386+ и до миграции под Windows) было важно - и массово использовалось программами - что XMS драйвер предоставлял EMS API для программ, которые продолжали работать в реальном режиме, но требовали больше памяти. mvv-rus@ говорит именно об этом. Реализацию EMS в виде отдельных плат памяти я ни разу не видел, а вот 386, 486 и дальше - уже были вполне доступны.

У нас была пара плат EMS для XTшек -- вполне себе работало. Ну и на АТшке 286-й, есно, тоже

Да, и кстати, главное в Вашем сообщении проигнорировал. EMS -- не аналог того, что было в Z8000, поскольку в последнем было полноценное внешнее MMU -- с защитой памяти, режимами доступа и всё такое. Т. е. связка "микропроцессор Z8000 + MMU из его микропроцессорного комплекта" давала возможность построить компьютер с виртуальной памятью, что у Интела появилось лишь в 80286 (в данном случае неважно, сегментная организация, страничная...).

Что там ещё, "замечательный" стек FP-регистров в 8087 например. По последнему можно вспомнить как Дейкстра, что ли, очень восхищался архитектурой burroughs, чисто стековой.

Тогда вообще многие считали, что за стековыми машинами будущее. Видимо, вытекало из каких-то теоретических разработок. На осознание непригодности этого подхода, по сути, ушли все 1980-е.

Так, что если где-то завалалась ЕС ЭВМ в более-менее нераздербаненном виде - готов поучавствовать в её запуске. ;)

Хорошо бы для начала найти копию ОС ЕС. System/360 выкладывали и в дистрибутивах, и в собранном виде, а вот с ОС ЕС глухо...

Неуместные пассажи в статье и некоторых комментариях :-(

Sign up to leave a comment.

Articles