Пользователь
0,0
рейтинг
3 марта 2014 в 11:42

Железо на службе у алгоритма (продолжение)

Борис Бабаян о прошлом, настоящем и будущем вычислительной техники


Борис Бабаян


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

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


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

Но к чему в итоге пришли языки высокого уровня? Все нынешние языки высокого уровня на две группы можно поделить: в одну входят языки типа Java, в другую – типа C. Java – это строгий контроль типов, но очень неэффективный. И динамики там нет. Никто даже не думает на Java операционную систему писать. По моим нормам, это не универсальный язык высокого уровня. Другая группа – С-подобные языки. Общее мнение таково, что это не языки высокого уровня. Скорее, вариант ассемблера.

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

Теперь, что же нужно для того, чтобы получить эти преимущества? Очень немного. Это легко сделать.

Прежде всего, в языках и аппаратуре вводится два типа дескрипторов – пространственные и временнЫе. Пространственный дескриптор – это, фактически, указатель на данные. Он может, например, содержать размер массива и адрес начала массива. Типичные операции с таким дескриптором – индексирование (доступ к конкретному элементу массива) и взятие подмассива. Всего существует очень небольшое количество операций над указателями на данные.

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

Теперь временнОй дескриптор – это указатель на функцию, право запускать какую-то процедуру. Указатель на функцию содержит адрес перехода. Во времена Эльбруса был популярен upper-level addressing, то есть статически вложенные процедуры.
Upper-level addressing
Бабаян считает, что сейчас upper-level addressing также необходим, т.к. он позволяет структурировать глобалы. Такое структурирование должно благоприятно сказываться на защите данных
Поэтому тогда указатель на функцию включал ещё и ссылку на глобальные данные той процедуры, внутри которой эта функция была описана. То есть, если процедура А описана внутри процедуры Б, то процедура А, вообще говоря, имеет доступ до всех данных, которые доступны процедуре Б. Поэтому в дескрипторе функции А, во-первых, есть адрес перехода и, во-вторых, ссылка на глобальные данные функции Б. Причём на глобальные данные конкретной инкарнации Б. Сейчас я это поясню. В результате цепочки рекурсивных вызовов, процедура Б может иметь несколько живущих одновременно инкарнаций. У каждой инкарнации будут свои собственные глобальные данные. За счёт этого указатели на функцию А, порожденные внутри разных инкарнаций Б, будут разными указателями. Они будут ссылаться на данные, соответствующие различным копиям охватывающей процедуры.

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

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

То есть у каждой процедуры есть контекст. Мы это даже называли «контекстной защитой».

Теперь допустим, что процедуре в какой-то момент времени понадобилось новый массив завести. А это одно из самых привилегированных действий в современных системах. В Эльбрусах для этого использовался контекст операционной системы. В нём содержались указатели на функции. В том числе указатель на функцию «дай новый массив». То, что «get space» называлось в операционной системе Burroughs.

Когда запускается процедура типа «get space», контекст вызвавшей её процедуры закрывается в стеке и открывается контекст самой «get space». В этом контексте, вообще говоря, существует гигантский дескриптор. На всю виртуальную память. А пользователь при запуске «get space», например, запросил массив на 25 слов. У «get space» есть таблица, в которой прописано какие места заняты, а какие свободны. Она находит свободное место и делает непривилегированную операцию: взять подмассив. В результате, получается дескриптор на ту память, которую процедура хочет дать новому массиву. Дополнительно ещё таблица корректируется соответствующим образом.

После возврата из «get space», у вызвавшей её процедуры расширился контекст. И для этого не потребовалось никакого привилегированного действия.

Конечно, необходимо помнить, что каждая процедура ответственна за семантику своих действий. Если в процедуре «дай память» есть какие-то ошибки по распределению памяти, то память будет плохо распределяться. Но это семантика самой процедуры. А проверить семантику процедуры на аппаратном уровне невозможно.

Далее смотрите… Вроде бы, из сказанного следует, что дескрипторы нельзя менять. Но это неверно. Дескрипторы можно менять, если нужно. Опять же без привилегированного режима. Смотрите, каким образом. В машине есть операции, которые позволяют менять дескрипторы. Однако они зашиты внутрь специальных процедур, в контексте которых указан особый параметр, позволяющий пользоваться этими операциями. Таких процедур очень мало, и указатели на них есть только в контексте небольшого количества системных процедур. Никаким другим процедурам «ручное» изменение дескрипторов недоступно!

Например, у процедуры «дай память» имеется указатель на функцию, способную изменять дескрипторы. Дело в том, что в действительности мы не пользовались гигантским дескриптором, о котором я говорил выше. Я упомянул о нём только в качестве иллюстрации. На практике большой дескриптор очень неудобен, т.к. приводит к большим виртуальным адресам. Вновь создаваемые дескрипторы будут занимать много места. К тому же «get space» всё равно может через аппаратуру любой указатель выдать, т.к. ей доступна вся память. Почему бы не разрешить ей напрямую создавать дескрипторы?

Это позволит уменьшить их размер. Пусть “get space” сама, выделив какую-то память, нарисует нужный дескриптор. Кроме неё всё равно никто не сможет этого сделать, т.к. только у неё есть необходимый для этого контекст.

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

Теперь смотрите, какие возможности даёт контекстная защита. Отладка просто невероятно упрощается. Предположим, у вас есть гигантская программа, о которой заведомо известно, что в ней нет ошибок. Допустим, это теоретически доказано. Тут я могу преувеличивать, но допустим. Пусть теперь пользователь, который не знает о том, что программа абсолютно корректна, добавляет к ней какую-то новую процедуру, которая не отлажена. Эта процедура может давать правильный результат, но при этом портить чужие данные. Для пользователя это будет выглядеть как ошибка во всей огромной программе. Более того, раз процедура, которую он добавил, даёт правильный результат, то пользователь, возможно, вообще решит, что ошибка находится в основной программе, а не в его процедуре. Получается, он должен быть готов отлаживать уже отлаженный код. И это относится ко всем современным архитектурам. Дополнительно ухудшает эту ситуацию то, что расширяемый код может быть огромным, а писавшие его люди уже давно могли уволиться. Пойди отладь такую систему… В Эльбрусе такого просто не могло быть. В Эльбрусе вы просто запускаете программу и ставите остановы на всех выходах из отлаживаемой процедуры. Затем на выходах проверяете результаты её работы. Если они вас удовлетворяют, об отлаживаемой процедуре можно забыть. Она больше никакого вреда вам не сделает. Это резко упрощает программирование.

Из-за того, что в памяти всё типизировано, отлаживаться можно просто проверяя дампы памяти. Когда мы сдавали машину Эльбрус два, при этом присутствовал Лёня Райков, руководитель проекта в НИЦЭВТ. А НИЦЭВТ – это организация, которая машины IBM копировала. Мы тогда только-только сделали операционную систему; часто сто терминалов работают; часто там ошибки какие-то возникают. Наши ребята брали дамп памяти. Он структурированный, там типы. Программисты сидели два часа, шли исправлять операционную систему. Лёня Райков негодовал! Потому что у них, когда ошибка в IBMовской операционной системе обнаруживается, несколько месяцев нужно добираться до этой ошибки.

Посмотрим, что нужно для того, чтобы поддержку дескрипторов в машине обеспечить. Самое трудное здесь – это на каждые 64 бита один дополнительный бит выделить для тегов.
Схема работы с тегами в Эльбрусе-3
Один бит позволяет указать, является ли объект стандартными пользовательскими данными (набор битов, который трактуется по-разному, в зависимости от операций, которые над ним совершаются), либо имеет внутреннюю структуру (благодаря которой объект может трактоваться, например, как «пустышка», указатель на данные, указатель на функцию, …)
Если в памяти реализован ECC-код, то это просто. В Эльбрусе-3 ECC-код есть, поэтому там никаких сложностей не было. Но сейчас во многих машинах нет ECC-кода. Тогда нужно чего-то придумывать. Если, конечно, мы хотим прикрутить к ним поддержку дескрипторов.

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

После всего этого остается добавить чистку памяти. Я сейчас поясню, что это такое.

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

Сложнее разобраться с указателями на почищенную память. После того, как память освобождена, все указатели на неё нужно найти и уничтожить. Иначе, после того, как освобожденная память будет снова переиспользована, с помощью этих указателей можно будет добраться до чужих объектов.

Как мы решили эту проблему? Мы мусорщик не вводили, это сложно. У нас операционная система всегда распространяет память нарастающим итогом, не переиспользует. Если память заканчивается, тогда операционная система собирает в ней дыры. Делается это одним проходом по всей памяти в фоновом режиме, пользовательские приложения при этом не останавливаются. Если во время этого прохода обнаруживается, что какой-то дескриптор смотрит на дыру, его забивают. Всё! Далее вся память просто компактируется, адреса в дескрипторах модифицируются нужным образом.

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

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

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

Всё! Помимо тех трудностей, которые я описал, никаких сложностей нет. То есть поддержку объектов ничего не стоит сделать! Но этого до сих пор ни в одной машине нет!
Современная инкарнация Эльбруса-2
Буквально несколько дней назад, когда я уже не собирался вносить значительных изменений в статью, набрёл на проект CRASH, осуществляемый в наши дни британской компанией BAE Systems по заказу американской оборонки. Цель проекта – разработка компьютера общего назначения и операционной системы к нему. Детали проекта, по понятным причинам, не разглашаются, однако поверхностное описание машины практически воспроизводит описание Эльбруса-2. Например: «…under the auspices of the DARPA CRASH program, we are designing a novel tagged hardware architecture, an information flow-aware programming language, and a new “zero kernel” operating system based on the principles of least privilege and mutual suspicion». Из приведенного описания видно, что машина будет тегированной. Также фраза «zero kernel», возможно, говорит об отсутствии привилегированного режима в операционной системе. В описании языка есть фраза «dynamically typed», т.е. язык будет динамическим по типам.

Так что вполне вероятно, что уже в ближайшее время нас ждет инкарнация идей Эльбруса-2 в современных условиях
Просто катастрофа, понимаете? Такого свойства, как программирование высокого уровня – до сих пор не существует.

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

Теперь давайте посмотрим на временнУю компоненту. Как я уже говорил, начиналось все с бит-последовательной машины. Потом аппаратура потихонечку всё расширялась и расширялась. Ну первое, что сделали – битовую арифметику заменили на словную. Когда я пришел в ИТМиВТ – а я тогда на Физтехе учился – там велась дискуссия. Сергей Алексеевич Лебедев её затеял. Утверждалось, что машины должны быть параллельными. Тогда параллелизм рассматривался внутри арифметических операций. Никто не думал ещё о том, что можно сами операции в параллель выполнять. Значит, ввели параллелизм внутри операций. После этого улучшить архитектуру можно было только за счёт ускорения логики, стоящей за выполнением арифметики. Другого не было дано. Потому что время выполнения программы всё еще состояло из суммы времён исполнения входящих в неё арифметических операций.

И тогда начались работы по ускорению арифметики. Вот я могу похвастаться: моя первая научная работа – в 54-ом году я это сделал, на четвертом курсе Физтеха – это введение carry-save арифметики. Сейчас те, кто арифметикой занимаются, очень хорошо это знают. Это умножение, деление и квадратный корень без переноса разрядов. Довольно очевидная и простая вещь. До сих пор является основой всех вещественных операций. Есть два базовых алгоритма. Один из них я представил в 55-ом году на физтеховской конференции. Первая западная открытая публикация на ту же тему вышла в 56-ом году. Надлер опубликовал.
Публикация
Вероятно, имеется в виду следующая публикация: Nadler, M., «A High-Speed Electronic Arithmetic Unit for Automatic Computing Machines,» Acta Technica (Prague), No. 6, pp. 464-478, 1956
То есть я на год раньше публично представил эту вещь. Однако статья по результатам моей работы вышла, к сожалению, только в 1958 году в первом выпуске «Трудов конференции МФТИ».

Второй базовый алгоритм carry-save арифметики указывает, каким образом действия первого алгоритма должны выполняться в системах счисления с основанием больше двух. Например, в четверичной или восьмеричной. Введение многоразрядной арифметики позволяет сократить общее количество операций. Эта идея и ее реализация принадлежит Робертсону.
Робертсон
Карточка автора в библиотеке Иллинойского университета: http://archives.library.illinois.edu/archon/?p=collections/controlcard&id=1000. Ссылка на отчет о посещениях вычислительных центров в СССР: http://dl.acm.org/citation.cfm?id=368342&dl=ACM&coll=DL&CFID=293666434&CFTOKEN=68165159
Он в 58-ом году приезжал в Москву, и мы с ним общались очень много.

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

Дальше, конечно, Крэй ввел конвейер. Всего один конвейерный модуль, но тем не менее это тоже параллелизм. Очень серьезный шаг был сделан.

Мы тоже этим озаботились. После того, как мы ввели carry-save арифметику, у нас появились специализированные умножители, специализированные делители, сумматоры, логика. Всё разные устройства. Мы посмотрели, что в машине порядка 10-ти устройств и подумали: ёлки-палки, их же надо уметь в параллель пускать. А система команд последовательная. Более того, у нас тогда ошибочное представление было, что для поддержки языков высокого уровня необходим простой компилятор. Поэтому у нас была обратная польская запись. Безадресная система команд, на стеке основанная. Сначала данные загружались на верхушку стека, затем производилась безадресная операция, результат снова оказывался на вершине стека… Всё шло через стек. Очень много операций получалось, по сравнению, например, с трёхадресной системой команд. Поэтому мы задумались: неплохо бы уметь все эти операции в параллель исполнять. Это, конечно, позволило бы догнать и перегнать трёхадресные машины, которым мы сильно проигрывали из-за раздутой польской записи. Однако основной мотивацией было другое. Мы думали об общей производительности, думали о том, как занять уже имевшиеся у нас многочисленные вычислительные устройства. Это 72 год был.

К 78-му году мы сделали первый в мире out-of-order супер-скаляр.
первый в мире out-of-order супер-скаляр
Первый в истории out-of-order суперскаляр – тема, вокруг которой сломано много копий. Эльбрусовцы непринужденно заявляют о своём первенстве, а в ответ им обычно летит «а вот в Википедии написано…» (далее обычно идет ссылка на IBM’овскую машину 360/91).

Википедия – штука, конечно, хорошая, но эльбрусовские инженеры всё-таки профессионалы. Поэтому мне пришлось обстоятельно разобраться в данном вопросе. Помогал мне в этом Сергей Шишлов, инженер по аппаратуре компании Интел. Огромное ему за это спасибо!

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

1) IBM 7030 (Stretch) (1961) – возможно, первая конвейерная машина, но не суперскалярная, как и последующие ее модификации
2) CDC 6600 (1964) – одна из первых конвейерных машин и первая машина с out-of-order. Однако она не была суперскалярной, т.к. команды декодировались по очереди. Тем не менее, их исполнение уже могло производиться в параллель с другими операциями. Улучшенная модификация CDC 7600 также не позволяла выдавать по несколько команд в такт
3) IBM ACS-1 (1960-е) – проект машины со стабильным исполнением нескольких команд в такт и ограниченной возможностью out-of-order. Вероятно, первая попытка создания суперскалярной out-of-order машины. Однако проект, начавшийся в 1961 году, был закрыт приблизительно в 1969 году из-за внутренней конкуренции с IBM 360, и машина выпущена не была. Идея суперскаляра была внесена в ACS-1 приблизительно в 1965 году и запатентована
4) IBM 360/91 (1967) – упомянута в Википедии как out-of-order суперскаляр. Эта машина действительно была out-of-order, но не была суперскалярной. Её декодер был способен выдавать не более одной инструкции в такт. Также машину нельзя назвать первым out-of-order’ом (из-за CDC 6600). Однако в ней впервые был реализован современный механизм поддержки out-of-order исполнения – переименование регистров
5) Floating Point Systems AP-120B (1976) – первая машина, способная исполнять несколько операций за такт в режиме VLIW. Процессор не поддерживал out-of-order исполнения, к тому же VLIW-машины традиционно не принято считать суперскалярными
6) Коммерческие RISC-архитектуры появились позднее 1980 года. Исследовательская RISC-машина IBM 801 (1975) не была суперскалярной. Первый суперскалярный RISC, IBM RS/6600, вышел в 1989 году. В нем поддерживалась ограниченная возможность out-of-order-исполнения
7) Cheetah (1980-е) – университетский проект суперскалярного процессора
8) GS-1000 (1986) – процессор, исполнявший две соседние команды в параллель
9) America (1987) – университетский проект суперскалярного процессора с out-of-order исполнением. Проект дал рождение термину «суперскаляр»
10) Astronautics ZS-1 (1987) – университетский проект суперскалярного процессора с возможностью out-of-order исполнения
11) RS/6600 (1989) – видимо, первый после Эльбруса коммерческий суперскалярный out-of-order процессор
12) Intel i860 (1989) – первый супескаляр компании Intel. Мог исполнять до двух команд в такт. Не поддерживал out-of-order-исполнения

Дополнительно интересно отметить, что механизмы восстановления при спекулятивном исполнении Reorder Buffer и Checkpoint появились в литературе в 1988 году и в 1986 соответственно.

Таким образом, Эльбрус, исполнявший до двух инструкций в такт и сданный госкомиссии в 1980 году, видимо, действительно является «первым в мире [выпущенным] out-of-order суперскаляром». Более того, получается, что он вообще стал первым в мире суперскаляром. По крайней мере, опровергнуть такое утверждение мне не удалось
Правда, он имел всего 2 конвейера. Сейчас суперскаляр дорос уже до 6 конвейеров. У Power 8 их вообще десять. Но это RISC. Там проще сделать большое количество конвейеров.

Интел сделал out-of-order машину в 95-ом году. Очень большое расстояние получается. 78-ой год и 95-ый. 17 лет! Ужас!

Такой получилась борьба за параллелизм.

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

Теперь посмотрите на нынешний микропроцессор суперскалярный. Это что-то невероятное. Ужасная вещь. Сначала компилятор превращает изначально параллельный алгоритм в линеечку. Потом суперскалярная машина из этой линеечки снова извлекает параллелизм. А затем, из-за того, что языки последовательные, retirement unit выстраивает параллельно исполненные инструкции снова в линейку.
Retirement unit
Выдержка из “Intel Architecture Optimization Reference Manual”: In-Order Retirement Unit. For semantically-correct execution, the results of instructions must be processed in original program order. Likewise, any exceptions that occur must be processed in program order. When a μop completes and writes its result, it is retired. Up to three μops may be retired per cycle. The unit in the processor which buffers completed μops is the reorder buffer (ROB). ROB updates the architectural state in order, that is, updates the state of instructions and registers in the program semantics order. ROB also manages the ordering of exceptions
Вот такой зигзаг получается! Ну разве можно это терпеть? Это же просто ужасно. Но люди терпят.

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

В RISCах декодер, конечно, гораздо проще, потому что там фиксированная длина команды. Но там мы всё равно упираемся в ограничение уже следующего порядка, в renaming. Renaming связывает аргументы и результаты различных операций друг с другом, т.е. фактически обрабатывает операции друг за другом по цепочке, что, конечно же, ограничивает распараллеливание. Однако даже если преодолеть каким-то образом ограничения renaming’а, на передний план выйдут задержки bypass-логики.
Bypass
Bypass-логика отвечает за передачу результата от операции, которая его выработала, к операции, которая его потребляет, в обход регистрового файла. Сложность проектирования такой логики растет квадратично с увеличением количества одновременно исполняемых операций. Например, если «в полете» может находиться одновременно 6 операций, надо обеспечить 36 связей между конвейерами, т.к. потенциально значения могут передаваться с любого конвейера на любой
Но если даже с этим справиться, то всё окончательно упрётся в branch predictor. Точность предсказания переходов резко падает при увеличении уровня вложенности ветвлений.

Таким образом, у суперскаляра есть лимит, предел производительности. Мы это поняли после второго поколения наших суперскаляров, после Эльбруса два. Хотя на момент второго Эльбруса суперскаляр был хорошей идеей. Мы на нём обыграли IBM, в том смысле, что обыграли наших конкурентов из НИЦЭВТ, сделавших EC-1060 – полную копию IBM System/360 (с процессором 3033, который не был суперскаляром).

Мы их обыграли в два раза. Но это было соревнование не с НИЦЭВТ, а с IBM, потому что в НИЦЭВТ сделали clock-precise копию американской машины, то есть повторили её с точностью до такта. И матобеспечение у них всё шло IBMовское. А у нас всё своё.

Однако, как бы ни была красива идея суперскаляра, мы поняли, что у него есть предел. Надо что-то придумывать взамен. Мы тогда много думали на эту тему с Володей Волконским и придумали «цуги» — параллельные потоки исполнения внутри одного ядра.
Владимир Волконский
Волконский Владимир Юрьевич – сотрудник МЦСТ, заместитель генерального директора по науке – направление «Системы программирования», Кандидат технических наук, старший научный сотрудник Заслуженный машиностроитель РФ, Награжден орденом «Знак Почета».

Информация взята отсюда: http://www.mcst.ru/nashi_uchenye
Мы думали: ну, сделать 32 цуга – и будет у нас настоящий параллелизм! Но я допустил тогда ошибку. Я подумал, что это очень сложно, а ведь есть подход попроще – подход широкой команды. Ну мы и решили его попробовать. Ведь мы тогда минусов не знали этой широкой команды…

В итоге, мы сделали широкую команду. Но не такую, как в Itanium. Itanium – это тоже полное, так сказать, недоразумение. Потому что по ширине он такой же, как суперскаляр: 6 команд. И при этом использует статическое планирование. Почему он должен быть быстрее, чем суперскаляр? Не понимаю.

Мы в Эльбрусе три применили подход кластеров.
Кластеры
Во VLIW-архитектурах нет renaming’а, поэтому на передний план выходят задержки bypass-логики. Эти задержки ограничивают возможности по наращиванию ширины машины. Частично это ограничение преодолевается за счет объединения вычислительных устройств в кластеры. После такого объединения, временнЫе затраты на взаимодействие устройств друг другом разделяются на bypass-задержки внутри кластеров и расходы на межкластерный обмен данными. Введение кластеров оправдано, если машина используется для решения задач, которые хорошо масштабируются на большое количество вычислительных устройств и не требуют при этом частых межкластерных взаимодействий.

Вероятно, применение кластеров в Эльбрусе-3 является первым в истории
Из-за того, что в широкой команде нет динамического преобразования из последовательного в параллельное, там обслуживание единого instruction pointer уже не является расточительством.

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

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

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

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

Вот я и дошел до сегодняшнего времени. Рассказал и время как менялось, и пространство как менялось. Давайте теперь более внимательно посмотрим на нынешнее время.

Что нужно менять? Почему всё плохо? Ну, во-первых, я уже сказал, что суперскаляр очень сложен. Но самое плохое, что он имеет предел производительности. Его предел — 6 конвейеров – больше нельзя. В Power 8 их 10, но это RISC. Там это легче дается, в x86 – посложнее. Но это «посложнее» сейчас с лихвой компенсируется высоченной квалификацией интеловского коллектива. Квалификация в этом деле имеет огромное значение.

Теперь смотрите, к чему этот лимит привёл. Начинают думать как расширять машину, как логическую скорость подымать. Ну как это можно сделать? Рост частоты сейчас ограничен. Значит, вводится векторная арифметика.

Это ужасно. В свое время Крэй придумал вектора. Крэй гениальный человек был. У него тогда всего одно плавающее устройство было. Он ввел регистры векторные. И через одно устройство каждый такт одну и ту же операцию запускал на разных регистрах. Т.е. его вектора шли не «поперек» устройства, как сейчас, а «вдоль». У него всего один конвейер был. Я читал его статью. Он говорит, что его любят за быструю скалярную скорость, а вектора бесплатно даются.

И посмотрите сейчас на векторную арифметику. Ничего себе бесплатно! Гигантские векторные устройства! В памяти там чёрт-те что творится от этого. Использование векторов очень маленькое. Если в векторизуемом цикле условный переход возникает, то это просто ужасно. Потому что приходится вводить маски и фактически два раза проходить весь цикл.

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

Вектора – это усложнение аппаратуры, причем это усложнение во многих случаях просто не используется. Оно не очень-то популярное.

Второе, что можно придумать для повышения логической скорости – это multi-thread. Но это тоже плохо, потому что если у вас задача одна, вы должны вручную ее разделить на потоки. Мы хорошо это прочувствовали, когда работали с графикой. В то время Интел над Larrabee работал. В Larrabee поставили колоссальную цель – программируемая графика. Но там была гигантская ошибка. Они решили сделать эту программируемую графику на x86, где не поддержан явный параллелизм.

Давайте взглянем на то, как делается растеризация. Растеризация – это множество очень маленьких кусочков вычислений, сильно параллельных друг с другом. Вот объекты, мы их проецируем на плоскость. Типичная работа такая: берется очередной объект, далее для каждого пикселя смотрим: на него уже что-то спроецировано? Если спроецировано, то заслоняется ли спроецированный ранее объект текущим? Если да, то, значение пикселя переписывается. Если текущий объект не выходит на более близкий план, то он игнорируется.

Для того чтобы распараллелить такой алгоритм, нужна синхронизация. Потому что множество объектов будут пытаться в параллель спроецироваться на конкретный пиксель. Но объектов может быть много, а синхронизация идёт недопустимо долго. Потому что синхронизация через память идёт. Как правило, семафоры берутся из общей памяти.

Это стало одной из причин, по которым провалился проект Larrabee.

А что в идеале нужно для графики? Нужна адекватная аппаратура для реализации явного параллелизма.

То есть явный параллелизм – это что такое? Рассмотрим цикл с невероятным количеством повторений. Если поддержан явный параллелизм, то итерации этого цикла можно выполнять в любом порядке. А их очень много. Вот мы доходим до синхронизации. Делается запрос в память.

Если явного параллелизма нет – в Larrabee, например – то поток, ожидающий синхронизации – которая, кстати, требует 200 тактов – ничего не делает все это время и при этом занимает ресурсы!
То, что в действительности нужно, — это отдать ресурсы потока, синхронизирующегося в данный момент времени, другому потоку, который может исполняться. Такой поток всегда найдется, потому что итераций навалом. Необходимо, правда, иметь способ эффективно передавать ресурсы от одного потока другому. Но мы это уже умеем делать.
Почему не сделать в суперскаляре?
Возникает законный вопрос: нельзя ли подобную штуку прикрутить к современному суперскаляру? Ответ Бабаяна – нет. Хотя бы потому, что при явном параллелизме отсутствует единый program order, т.е. разные части программы можно исполнять независимо друг от друга. В суперскаляре существует сквозной порядок завершения инструкций (retirement). Чтобы поддержать его в условиях явного параллелизма, понадобились бы невероятно большие буфера


При таком подходе использование арифметики – в графике, в основном, плавающая арифметика – будет стопроцентным! Быстрее просто невозможно будет сделать при заданной ширине машины. Это потому что явный параллелизм.

То есть фактически что потребовалось? Потребовался явный параллелизм. Вот это последовательное представление программы и в памяти, и во времени стало обузой. Из-за линейного пространства у нас нет type-safety, потому что нет поддержки объектов. В пространстве все упорядочено.

А к чему приводит упорядоченность во времени? К тому, что языки тоже сделаны упорядоченно. Вообще разработчики языков понимали, что явный параллелизм – это важная вещь, и они вводили параллельные языки. Это Алгол 68, это Ада. Там явный параллелизм итераций. Явный параллелизм – значит, должна быть обязательно синхронизация. Вот разработчики сделали свой язык, параллельный язык с синхронизацией. А потом компилятор транслирует его на последовательную машину. Никакой пользы от того, что язык параллельный, нет. Только головная боль программистам. Потому что когда в языке есть явный параллелизм, иногда dead-lock’и получаются. Поэтому такие языки не прижились. А сейчас они крайне важны!

Значит, еще раз, чтобы проследить логику: современный суперскаляр очень сложен и к тому же имеет предел скорости. Чтобы преодолеть этот предел вводятся векторизация и multi-threading. А то, что действительно нужно – это явный параллелизм.

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

Такая ситуация сохранялась до тех пор, пока не появилась 360-ая машина от IBM. С появлением 360-ой, компания IBM фактически заявила: «вот, раньше для каждой машины свои программы писали. Новая система команд — новое поколение, новые программы. Давайте сделаем так, чтобы 360-ая имела одну и ту же систему команд для многих моделей. Есть быстрые, есть медленные модели, но компилятор транслирует код всего один раз, и этот код может исполняться на всех моделях». Это то, что мы сейчас имеем.

Раньше такой подход, может быть, выглядел хорошо. Но сейчас-то он просто ужасен! Вот смотрите, какая беда здесь возникает. Компилятор транслирует программу и оптимизирует её. Т.е. оптимизирует использование ресурсов в данном алгоритме. Чтобы выполнить оптимизацию хорошо, компилятору необходимо знать две вещи. Во-первых, он должен хорошо знать алгоритм. Досконально должен знать: какова его структура, какой там параллелизм… Второе, что должен знать компилятор – это ресурс: сколько уровней кеша, как они устроены, сколько регистров…

Теперь смотрите, как дела сейчас обстоят. Компилятор как следует алгоритм не знает. Потому что языки испорчены старыми машинами. Они все последовательные. Если в алгоритме есть параллелизм, пользователь сам вынужден линеаризовать его в языке. Причем линеаризует он вовсе не самым оптимальным образом. Для одной модели нужно так линеаризовать, для другой — иначе. Значит, программист уже портит программу. Ну ладно, он испортил, дальше компилятор транслирует её на железо на какое-то. Он не знает, на какой именно модели будет исполняться программа. Тут я немного упрощаю ситуацию, т.к. с помощью опций компиляции (как минимум в теории) можно получить код, оптимизированный под любые особенности аппаратуры. Однако практика специализации кода под конкретные модели не является общепринятой. Поэтому компилятор должен хорошо скомпилировать программу и для старой модели, и для современной, и для будущей. Этого он не может сделать. Поэтому компилирует как попало. Сегодня компиляция для суперскаляра – это шаманство. Как вообще люди разрабатывают оптимизирующие компиляторы? Я восхищаюсь их работой. Переставляют что-то местами – и вдруг всё начинает быстрее работать.

Но смотрите, что например, невозможно при таком подходе. Невозможно такую простую оптимизацию сделать, как подкачка кода. Это мне Володя Пентковский объяснял. Он работал в командах, которые реализовывали предварительную подкачку.

Когда подкачка эта отлаживается на конкретной модели, программа исполняется быстрее. Через два-три года появляются другие модели – и эта же программа может работать на них медленнее, чем на старой машине. Мне один коллега байку рассказывал: когда ему необходимо было выйти к пользователям, у которых программа медленно работала, он прежде всего программную подкачку отключал. Программа обычно быстрее начинала работать. Это всё потому, что подкачка model-dependent. Компилятор не может её делать эффективно.

Таким образом, что мы имеем? Компилятор не совсем хорошо знает алгоритм и совсем не знает аппаратуру. Поэтому аппаратуре приходится самой выкручиваться. В частности, в ней запрограммирована работа кеша. Но как запрограммирована эта работа? Железо не может делать предподкачку, оно качает код только с оглядкой назад. По типу least recently used. Всё это может идти в разрез с тем, как данные используются алгоритмом.

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

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

Посмотрим теперь на ещё одну очень важную вещь. Первые машины были с шинами. И память общая была. Если что-то пишется – прямо пишется в систему памяти. Локальных кешей не было. Обращения к памяти от разных процессоров не различались. Да и доступ к памяти выполнялся быстрее, чем арифметическая операция.
Быстрая память и медленная арифметика
Например, в машине CDC 6600 умножение занимало 10 тактов, а доступ к памяти – не более двух. Бабаян шутит, что в те времена и регистры-то не были нужны, т.к. можно было постоянно обращаться к памяти без значительного увеличения накладных расходов


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

Простой подход здесь – это ввести локальную память вместо кеша. Тогда промахов в кеш почти не будет. Локальная память должна обслуживать только конкретное ядро.

Ведь сейчас же как делается? Идёт программа, работает какое-то ядро. Оно с какой-то памятью работает. И не известно: есть ли на другом ядре параллельная работа с той же памятью или нет. На всякий случай, приходится предполагать, будто такая работа ведётся. Приходится memory-model выдерживать. Это сильно замедляет машину. Это глупо!

Имей мы явный параллелизм, всё можно было бы сделать гораздо эффективнее. Если программист в языке явного параллелизма сказал, что с какой-то конкретной частью вычислений никто в параллель на общей памяти не работает, то ядро может никуда наружу не обращаться. Может работать с прямо адресуемой локальной памятью. Нынешние кеши – это дикая дань старому. Уже давно технология изменилась, а все равно имитируется bus structure.

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

Посмотрим на ещё один архаизм – retirement. С введением явного параллелизма, его нужно будет убрать. Не нужна единая последовательность инструкций. Полная линеаризация сильно затрудняет достижение эффективности. Конечно, как-то придётся решить зависимость между чтением из памяти и записью в память, но это другое дело. Я считаю, что такие зависимости должны быть явно указаны программистом, а их эффективное отрабатывание – уже дело компилятора и аппаратуры. В частности, если зависимость не лежит на критическом пути алгоритма, то с ней и делать особо ничего не надо. Зависимая операция просто должна дождаться выполнения той операции, от которой она зависит. С зависимостями, через которые проходит критический путь, всё немного сложнее. Если здесь упорядочить все зависимые операции, то можно сильно потерять в производительности. Поэтому нужно использовать data speculation и грамотное планирование, которое будет стараться разнести зависимые операции на как можно большее расстояние друг от друга.

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

Я много уже сказал. Но какой, в итоге, у меня облик самой прогрессивной машины будущего?

Вот смотрите. Есть алгоритмы. Надо сделать так, чтобы и пространственная, и временнАя компоненты в программах были полностью эквивалентны алгоритму. Для этого вводится язык явного параллелизма. Над ним надо поработать. В этом языке обязательно присутствует type safety. Это первое.

Второе – вводится простой перекодировщик. Упаси бог какие-то оптимизации на этом этапе делать. Это задача компилятора, а перекодировщик должен просто перевести программу в двоичное представление.

Третье – созданный перекодировщиком дистрибутив доносится прямо до компилятора, который к железу придвинут. Никакие другие варианты компиляторов не нужны. Потому что они будут только скрывать алгоритм от компилятора, придвинутого к аппаратуре. Если всё сделать так, как я сейчас сказал, то этот единственный компилятор будет отлично знать и алгоритм, и железо.

Далее нужно решить вопрос с кешем.

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

Потом retirement тоже нужно выкинуть.

После всего этого машина становится дико простой. В такой системе необходимо будет ввести спекулятивность: и data speculation, и control speculation. Далее будет очень простой scheduler, значительно проще, чем в суперскаляре. Кое-что нужно будет добавить для правильного исполнения условных переходов и входов в процедуры. Ну и исполнительные устройства понадобятся. А больше ничего не нужно.

Теперь немного про универсальность такой машины. Но это я с некоторой опаской говорю… Недавно я посмотрел на структуру кристалла смартфона Qualcomm’овского. Там приблизительно 11 квадратиков. Один из квадратиков – это универсальный процессор, всего 15% площади занимает. Дальше, стоит графика. Её на современных языках и машинах программно нельзя сделать. Это мы знаем. А на машине с поддержкой явного параллелизма она элементарно получается. Значит, квадрат с графикой в универсальный квадрат уходит. Дальше мы видим DSP. DSP – это же цикл, причем не самый сложный. На универсальной машине его легко будет исполнить. Что касается остальных квадратиков, я не очень знаю алгоритмы, для которых они предназначены. Но я попросил людей разобраться с каждым из них. Дополнительно я связался с человеком в США, который со смартфонами работает, и попросил у него информацию о задачах, которые исполняются этими квадратиками.

Так знаете, что мне этот человек сказал? Раньше каждый из квадратиков был простым небольшим специализированным устройством. Дальше эти устройства стали усложняться и превратились в программируемые контроллеры. Т.е. каждый разработчик смартфонов делает теперь больше десятка доморощенных программируемых процессоров. Это то, за счет чего разработчики смартфонов конкурируют. Они имеют примерно одинаковые процессоры общего назначения, которые занимают лишь 15% кристалла и конкурируют за счет остальных устройств.

Моя же идея такова… Что может быть в этих квадратиках, кроме дискретной арифметики и циклов? Скорее всего, ничего. Но тогда, по всей видимости, универсальный процессор сможет заменить все эти устройства. Существующие сегодня универсальные процессоры этого сделать не могут, т.к. очень неэффективны на специализированных задачах. Но процессор с поддержкой явного параллелизма сможет. Конечно, это нельзя говорить гарантированно, мы только начинаем над этим работать.

Однако если это так, то жизнь разработчиков смартфонов станет значительно легче.

Сейчас они вынуждены работать сами с большим количеством сложных устройств. Их конкуренция становится очень тяжелой.

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

Эта мысль мне пришла в голову в старое время, когда я с самсунговцами работал. Когда-то они делали специальные чипы для рефрижераторов. А потом подумали: если контроллер сделать программируемый, то мы сможем использовать одно устройство на многих продуктах. Вот и я теперь думаю: если перейти на один универсальный смартфон – это ж просто великолепно будет!

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

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

А этот предел в каком-то смысле уже неулучшаемый. Тут мне могут сказать: наверное, что-то у тебя с головой не в порядке. Что значит «неулучшаемый»? Прогресс остановился, что ли?

Ну, так бывает. Сейчас все признают, что суперскаляр почти остановился в развитии. Машина – это конечное образование. Если это конечное оборудование, значит число различных процессоров, по крайней мере, теоретически конечно. Среди этого конечного множества какой-то есть самый хороший. Значит в принципе есть неулучшаемость. Гениальность интеловских разработчиков в том, что они практически достигли эту неулучшаемость. Они не теоретически, а на практике достигли её. Сейчас нужно через это перешагнуть – ввести явный параллелизм. Тогда пределом будет сам алгоритм. Если в алгоритме есть какой-то параллелизм – его уже не перепрыгнуть. Если кто-то скажет: а я могу модифицировать алгоритм, и он станет быстрее – я скажу: ага, ну давай, модифицируй, мы возьмем этот модифицированный алгоритм и исполним его лучшим образом. Пожалуйста, всё хорошо.

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

Автор благодарит Александра Останевича и Сергея Шишлова за помощь в подготовке материала.

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

Спасибо!

Andrey Nevolin @TechThink
карма
94,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (50)

  • +1
    Спасибо, с нетерпением ждал продолжения!
  • +8
    Интересно получается, что самые прогрессивные люди — это вот такие дедки. Парадокс.
    • +3
      Почему парадокс-то? У него есть опыт глобальных разаботок, у молодежи — нет.
      Другое дело что когда что-то долго делаешь сам, то потом порой тяжело взглянуть на это под другим ракурсом. Иэто может вылиться в догматизм. Люди с острым умом и восприятием действительности этого могут избежать. Я рад за Бабяна — он не просто интересно жил, он интересно живет)
    • +6
      Они не самые прогрессивные. Они самые «оторванные от жизни». Они ещё помнят времена, когда можно было, грубо говоря, решать — расстояние между рельсами будет 1435 мм, 1520 мм или аж прямо 1676 мм. И они реально это делали.

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

      Люди же помоложе отлично понимают, что никто уже никогда колею менять не будет. То есть можно перейти от 1435 мм к 1520 мм или наоброт, но три рельса могут быть только на выставке или где-нибудь в карьере.
      • 0
        но три рельса могут быть только на выставке или где-нибудь в карьере
        … и если они там приживутся и дадут большой прирост эффективности, то мне почему-то кажется, что со временем они станут использоваться всё шире и шире, и в конце концов войдут в повседневную жизнь.
        • +1
          Вы абсолютно правы (массивно-параллельные вычисления пришли в нашу жизнь через компьютерные игры, не так ли?), но Бабаян как бы даже не пытается задуматься на тему — а в какой бы такой карьер наши новые изобретения пристроить. Он всегда изобретает «новые рельсы для всех» и потом пытается всем объяснить что все пидорасы, а он один — д’Артаньян потому что смог «спалить» ещё сколько-то миллионов долларов, но его «гениальнейшие» изобретения все проигнорировали. Смешно просто.
      • –1
        Есть большая разница. Он предлагает не архитектуру, сулящую выгоду в некоторых частных случаях(как те же явно параллельные видео ускорители, которые адаптировали для смежных задач), а радикальное изменение подхода для вычислительных ядер общего назначения. Поэтому искать нишу по сути не его забота, у Intel полно людей. Другое дело можно было бы поискать общие задачи, где такой переход дает больший выигрыш, чем в среднем, но это отдельная история.
        А коле именяются регулярно…
        • 0
          Локальная память, отсутствие кэшей — и программы общего назначения? Ну-ну. Удачи товарищу.

          Вы на его «идеальную машину» посмотрите. Она подразумевает что программы для неё пишутся сугубо на специальном языке с явной параллельностью, причем он даже не в состоянии назвать как этот язык должен выглядеть ибо «над этим еще надо поработать».
          • +1
            В том то и дело, что программы пишутся как есть. Язык просто не мешает компилятору и железу распараллеливать граф вычислений. Язык судя по всему напоминает современные функциональные, возможно тот же Хаскель. Просто нужно еще промежуточное представление(аля сжатие) и железо-ориентированный компилятор. Возможно, часть работы современного компилятора мог бы делать и сам процессор аппаратно.

            Локальная память, как я понял, это и есть замена кэшу, а может даже и регистрам. По сути это кэш L1 для данных, который не синхронизируется с остальными т.е. синхронизация только явная на основании анализа алгоритма.
            • +2
              Языка у Бабаяна нет, ни в каком виде. И предсказать сколько параллелизма он сумеет извлечь — невозможно. Функциональные языки типа Хаскеля, например, параллелят программу далеко не идеально. Программы на языке с высоким параллелизмом, «удобны» не только для бабаяновского компилятора — компилятор для «обычных суперскаляров» прекрасно себе способен использовать информацию о параллелизме для создания более быстрого и эффективного кода а следовательно при прочих равных подобная программа будет работать быстрее и на суперскаляре. Но вот только что-то Хаскель для высокопроизводительных вычислений не используется — странно, правда? А дело в том содержащейся в нем информации о параллелизме недостаточно чтобы автоматически создать код, который обогнал бы «линейную», но оптимизированную вручную программу (причем без каких-то особо фантастически сложных оптимизаций). С чего вдруг ситуация для бабаяновского процессора окажется иной? А ведь есть еще вопрос о том, насколько легко и быстро на этом языке будет создавать программы, поскольку в подавляющем большинстве случаев немного более медленная программа будет предпочтительнее втрое более дорогой в разработке быстрой — посмотрите за примерами на ту же Java. Так что вопрос языка важен. А Вы им пренебрегаете.
        • 0
          Колея меняется регулярно и потому сейчас идёт выбор между тем, что «придумал на коленке» Intel в 1978 году и тем, что придумал Acorn в 1985м?

          Вам самому-то не смешно?

          В том-то и дело, что моменты когда колея может быть сменены не редки, а исключительно редки: это можно сделать только тогда, когда «открывается» какая-то новая ниша. И количество людей у Intel'а тут ничего не изменит. Ну разве что позволит «спалить» вместо пары миллионов долларов пару сотен миллионов, но практическая-то разница какая?
          • +1
            Нет, совершенно не смешно. Я говорю даже не про колею применительно к архитектуре CPU, а в целом к любой области знания и технологии. История вычислительных устройств пока слишком молода. Еще даже одно поколение не сменилось. Чтобы поменять колею, как правило, нужна как минимум смена одного поколения людей, то есть около 50 лет. И я почти уверен, что в ближайшие лет 10 возникнет вторая колея, которая станет основной еще через лет 10-15. Дело не в нише. Революции случаются как правило по более глобальным причинам. Одна из таких причин в данном случае — больше половины площади CPU занимают костыли, эмулирующие поведение машин 60х годов разработки. А значит, теоретически, можно сделать вычислительное устройство до 2х раз быстрее только поменяв подход. И, поскольку развитие полоупроводниковых технлогий замедлется, это может стать единственным реальным выходом. И именнопо этому рельсы скоро поменяются.
    • 0
      Как раз ничего удивительного. Знания и опыт — как раз у них. И, что немаловажно, у них же есть нюх и интуиция, отполированные годами поисков. Побольше бы таких — и побольше бы им учеников!
  • +1
    RISK -> RISC
    • 0
      Спасибо! Поправил. В тексте удивительным образом чередовались оба варианта. Подсознательная тяга к риску? :)
  • +9
    Все нынешние языки высокого уровня на две группы можно поделить: в одну входят языки типа Java, в другую – типа C. Java – это строгий контроль типов, но очень неэффективный. И динамики там нет. Никто даже не думает на Java операционную систему писать.


    Как бы сказать, чтобы не обидеть… Был у нас преподаватель с кафедры ЭВМ, лет 70-десяти. Очень хороший человек. Так вот, когда он рассказывал нам про сканер (устройство такое), начинал он так: «Сканер — это такое устройство на чугунной станине, к станине прикреплена штанга, по которой ездит камера ...»

    Собственно по цитате два вопроса:

    1. Куда делся (скажем) Haskell?
    2. Как быть с Android-ом?
    • +3
      А что не так со сканером? Подумаешь заменили чугуний пластиком — точность даже понизилась из-за вибраций.
      • +2
        По чугунию, в общем-то, вопросов нет, все нормально, дедок старый.
        Но за Haskell и Java немного обидно
        • 0
          Не знаю, за что вам ставят минусы, но на всякий случай компенсировал.
          • 0
            Спасибо
    • 0
      Не совсем понимаю, как именно вы восприняли этот фрагмент, но основная мысль такова (по Бабаяну): язык высокого уровня должен иметь адекватную аппаратную поддержку. Я думал, эта мысль проглядывается через весь текст. Возможно, я где-то не доработал.

      В указанном смысле можете приписать Хаскель рядом с Джавой. Сути бабаяновской позиции это не изменит.
      • +5
        Попробую расшифровать как я понял цитату:

        Все нынешние языки высокого уровня на две группы можно поделить: в одну входят языки типа Java, в другую – типа C

        1. Не совсем понятно, по какому критерию деление?

        a) статическая/динамическая типизация?
        б) JIT/Native компилятор?
        в) присутствие/отсутствие адекватной аппаратной поддержки?
        г) высокого/низкого уровня???

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

        Никто даже не думает на Java операционную систему писать.

        Людям еще и не то в голову приходит. Не стоит вот так вот категорично

        • +2
          Деление очень простое: языки, гарантирующие (в каком-то смысле), безопасность и универсальные языки, на которых можно написать нечто «живущее» (в каком-то смысле) прямо на железе.

          Все современные «высокоуровневые» языки (Java, Haskell, etc) предполагают наличие некоей «основы», которую на них самих написать (и скомпилировать в машинные коды) не получится. Все «универсальные» языки (C, Forth, Pascal, etc) позволяют написать функцию, которая, будучи вставлена в отлаженную и безошибочную программу её уничтожит.

          Есть даже языки, которые могут работать в обоих режимах (скажем Ada), но… не одновременно. А Бабаян указывает на то, что это — не случайность, это вызвано особенностями современного состояния железа.
        • 0
          Людям еще и не то в голову приходит. Не стоит вот так вот категорично
          +1. Первым же запросом в гугл нашёл три операционных системы на Java в первых результатах выдачи.
          • 0
            Если бы вы не просто посмотрели сниппеты гугла, а разобрались, то обнаружили бы, что во всех них есть сишное или ассемблерное ядро. Сюрприз-сюрприз!
          • 0
            Еще поясню: на java везде написан юзерспейс. Где-то его больше, где-то меньше, но всегда есть ядро, которое не на java, и не потому, что даже производительность, а просто на java его написать невозможно. Именно это подразумевается в статье под неуниверсалиным языком программирования.
            • 0
              Что вы имеете в виду под «невозможно»? Java — это язык программирования (т.е. набор синтаксических конструкций), а не конкретная реализация среды исполнения или компилятора. Да, этот язык спроектирован таким образом, чтобы для него можно было написать удобную и быструю виртуальную машину. Однако, во-первых, ничто не запрещает вам создать компилируемую реализацию Java. А во-вторых, ничто не мешает вам создать виртуальную машину, работающую без ОС — и уже на ней запускать всю ОС, включая ядро. Более того, даже вполне возможно написать саму виртуальную машину на Java. Конкретно для Java я таких примеров не скажу (потому что это было никому не надо), но могу привести в пример Smalltalk, который даже сильнее завязан на виртуальную машину, и в котором даже нету примитивных типов. Так вот, есть реализации Smalltalk, в котором виртуальная машина писалась на самом Smalltalk (а потом, естественно, компилировалась в машинные коды).

              Так что никакой «невозможности» в этом нет, это исключительно ментальное ограничение. Другое дело, что на Java писать ОС очень _неудобно_, и _лучше_ для этого подходят другие языки (которые, в частности, специально проектировались для того, чтобы писать ОС — сюрприз-сюрприз).
              • 0
                Под «невозможно» я имею ввиду именно невозможно. Дело даже не в интерпритируемости. Дело в высокоуровневости и безопасности. Грубо говоря, нет указателей. Нет работы с машиной напрямую. Много чего нет.
                А ваша «виртуальная машина, работающая без ОС» будет написана на си и заменять ядро той самой ос :)
                Про смолтолк не скажу, языка не знаю.
      • +2
        На счет поддержки в железе, не стоит забывать про Jazelle, которая что-то не сильно большое распространение получила.
    • 0
      хаскель можно отнести к классу java, поскольку эффективность программы определяется оптимизацией при компиляции и сложная абстрактная программа будет медлнее аналогичной на С.
      и это печально, и безысходно.
      • +3
        Это ничего, что Haskell — компилятор?
        • +6
          А я думал, что это Haskell — это язык программирования
      • +1
        из последних статей что я тут видел код на хаскеле работает в среднем быстрее.
        Хотя это может за счет встроенных средств паралелизации исполнения
    • +2
      2. А что не так с Android? Предположу вопрос и отвечу, что он не на Java писан.
      • 0
        Ну и на Java в нем порядочно всего написано, аналогично тому как в Unix-е очень много написано на Си, а не ассемблере
        • +4
          Не нужно путать ядро и обвязку. Ядро Android — на базе ядра Linux, последнее в основном на Си. В интервью речь идет о ядре.
          • –2
            Ядро или микроядро как например здесь, разумеется всегда будет написано на чем-то низкоуровневом, но это не делает мне понятным критерия деления всех языков на два класса: «как Java» и «как Си»
    • 0
      У Android-а ядро все-таки на Си. А ОС на Java никто никогда не напишет потому, что писать ОС для Фон-Неймановской машины на управляемом языке — нонсенс.
    • 0
      Кстати Хаскель(да и в общем то все остальные функционально-ориентированные языки) как раз ближе к идеальному языку в его понимании, поскольку дает компилятору больше информации о структуре данных и графе вычислений, чем традиционные языки.
  • +9
    Статья ужасно читается. Используются нестандартные последовательности слов в предложениях, отрывные предложения и пр.
    Общее впечатление — сумбурная компиляция различных фактов и ОЧЕНЬ самоуверенных высказываний.
    • +5
      Я так понимаю, это прямая речь Бабаяна.
    • 0
      Кому как. Я лично читал взахлёб, товарищ простым языком и «на пальцах» рассказывает захватывающей сложности вещи.
  • 0
    > Первый в истории out-of-order суперскаляр – тема, вокруг которой сломано много копий.

    Теоретически iAPX 432 является таким процессором — конвейер в 3 шага есть, декодирование в полтакта тоже присутствует.
    Секвенсор тоже присутствует. OoO проводится правда над мопами уже, а не просто над инструкциями как в Эльбрусе.
    Btw, разбивка сложных условных «CISC» инструкций в несколько простых RISC-микроопераций уже была в этом процессоре. (8086 вышел только на пару лет раньше).
    То же самое сейчас происходит почти в каждом x86-ядре.

    Правда на практике всё сложнее, но надеюсь скоро попробовать in vivo.
  • +7
    ОЧЕНЬ самоуверенных высказываний

    Это же Бабаян. Это его фирменный стиль.
  • +2
    Оговорюсь, что локальную память я очень осторожно предлагаю. Я эту идею пока ещё не прорабатывал. Но думаю, это можно сделать.

    Cell'ы содержали 256Kb Local Storage на каждом SPU ядре.
    Как-то мне довелось программировать для такой системы, это было весело, но довольно трудно.
  • 0
    Спасибо большое за статью!

    По существу материала: Бабаян, ИМХО, изобретает велосипед. К примеру, «пространственный дескриптор», по сути мало чем отличается от виртуальной памяти (которую ему все равно приходится в системе делать!). «Временной дескриптор» — тот же стек. «Удобная и надежная отладка» — от юнит-тестов (которые, как прекрасно известно, ловят и близко не все баги). «Явный параллелизм и локальная память» — один-в-один подход CUDA / OpenC (тоже, как известно, работает далеко не везде). Бахвальства много, а интересных идей не видно (о них «нельзя подробнее писать», ага). Проблем много, преимуществ мало.
    • +2
      Собственно проблема ровно одна, но такая, что может все остальные идеи похоронить: совместимость. Кто будет покупать эту систему? И для чего?

      Я такой класс людей про себя называю «эсперантисты»: да, возможно, если вдруг все люди выучат эсперанто жить на Земле станет лучше и веселее. Да, возможно если все эти специализированные приборы заменит «универсальный вычислитель с пространственными и временными дескрипторами», то наступит больше такое ЩАСТЯ. Но… перехода от точки A к точке B нет и, стало быть, и говорить особо не о чем.

      Ну и бесконечное надувание щёк, конечно. Вся эта Бабаяновщина начиная с его 22 операций за такт и прочим — всё при нём. Вы думаете речь идёт о сложениях вычитаниях? Неа: и обращения в память и вычисления адресов и прочее — всё в эти 22 операции входит отдельно… современный Intel по такой методике подсчёта будет иметь параллелизм не 3-4 инструкции за такт, а штук примерно 12-16 (смотря насколько нагло считать).

      То есть нет, всё это очень интересно, только… бессмысленно, что ли. Самое главное — отсутствует.
  • 0
    Спасибо за статью. Интересный взгляд, интересный человек. По сути, он вышел на последствия введения фон-неймановской машины, ее ограничения, и назревшую необходимость пересмотра этой концепции или дополнения другими. Самый глубокий материал по it, который видел за последнее время.
  • 0
    Так как фон Нейман разрабатывал архитектуру в 40-х, когда минимизация аппаратной и логической сложности была ключевой для реализации проекта (лампы!). С тех пор технология шагнула настолько далеко, а область применения настолько изменилась и расширилась, что столь красивая модель упрощения будучи прорывной в 40-х, на данный момент уже порядком жмет в подмышках.

    И последовательность исполнения, и шина, и пространственная однородоность — оттуда.
  • 0
    А под фон Неймановскими идеями — и концепт машины Тьюринга — оказавший на них определяющее влияние. В данном случае оба концепта проясняют на мой взгляд, о чем статья, а также на каком уровне анализа человек изъясняется :)

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