Comments 43
Каждый из этих этапов выполняется в один такт. В CISC процессорах пока одна инструкция проходит эти 5 этапов, вторая ждёт полного исполнения инструкции, чтобы начинать первый этап
Нет, в CISC вполне себе конвеер c i486 и какие-то команды могут исполняться в один и тот же такт на разных блоках (с пентиума про). Вот старая статья про них.
Более того, и конвейер, и суперскалярность появились ещё на машинах 1960-х годов. У нас первой конвейерной машиной была БЭСМ-6 (1967 год), у американцев всё это появилось ещё раньше.
Это да, но i486 является гибридом RISC и CISC, а в статье я говорил именно про момент появления RISC, когда у CISC ещё не было конвейеризации. Понятно, что позже, в погоне за RISC, CISC-подобные архитектуры начали задумываться о добавлении конвейеризации, и в последствии появился i486. Но спасибо за дополнение
Если говорить про архитектуры (в первом приближении -- про системы команд), то никаким гибридом i486 не является -- самый натуральный (и плохой) CISC.
Если же говорить про внутреннее устройство, то, как я уже написал, конвейеризация появилась задолго до появления RISC, и одно с другим не связано от слова "совсем".
Яркий пример — атаки Spectre и Meltdown, позволившие извлекать данные из защищённой памяти [1]. Эти атаки затронули почти все современные процессоры, включая Intel, AMD и ARM.
Вот еще одна ошибка в первых pentium - FDIV:
Так как инструкции могли быть очень длинными, они требовали больше транзисторов, что повышало энергопотребление процессора, что было большой проблемой для отрасли 3д-моделирования и рендеринга того времени;
Для декодирования и выполнения нескольких сложных операций процессору требовалось много памяти, которая на тот момент стоила очень дорого (к примеру, один мегабайт оперативной памяти стоил около 5000 долларов). Также при обращении декодера к памяти скорость обработки инструкций становилась меньше;
Большинство возможностей CISC (например, строковые операции) редко использовались, но усложняли декодер.
Строго говоря, неверны все три пункта.
Количество потребных транзисторов со сложностью системы команд связано отнюдь не линейным образом. В частности, сложность исполнительных блоков (условно говоря, АЛУ) для выполнения одних и тех же операций вообще никак не зависит от кодирования команд как такового; нет никакой разницы и в конструкциях кэшей, TLB, регистровых файлов и т.д. и т.п.
С декодированием тоже далеко не всё однозначно. Когда автор статьи пишет про длину команды от 1 до 15 байт, он, несомненно, имеет в виду IA-32 aka x86. Эта кодировка не самая длинная (у VAX-11, если память не изменяет, длина команды могла достигать вообще 36 байт), но одна из самых бредовых и сложных для декодирования -- в первую очередь, из-за наличия префиксов, ни количество, ни порядок которых никак архитектурой не ограничены. Однако CISC отнюдь не обязан быть столь безумным. Скажем, такой классический CISC, как IBM System/360 (и все её потомки, включая современную z/Architecture), имеет длину команды 2, 4 или 6 байт, причём длина однозначно устанавливается анализом двух старших битов первого байта -- т.е. даже проще, чем, скажем, для команд набора Thumb-2 архитектуры ARM (там всего две длины -- 2 или 4 байта, -- но анализ длины сложнее).
Насчёт памяти написана вообще полная ерунда -- CISC как раз требует меньше памяти, поскольку в одной команде может закодировать куда более сложную операцию. Сколько места займёт подпрограмма шифрования AES на ARM или RISC-V? Ну а на мэйнфреймах z/Architecture это делается одной командой. А если необходима десятичная арифметика? (а она необходима для финансового сектора -- недаром соответствующие вещи есть, например, в Жабе) На большинстве архитектур, включая IA-32, будут пляски с бубном, чтобы добиться того же результата, используя целочисленную двоичную арифметику, ну а мэйнфреймы ещё с Системы 360 имеют и команды десятичной арифметики. Да, и операция пересылки строк на них тоже используется весьма и весьма часто -- пересылать ведь не только строки в узком смысле приходится, а произвольные блоки данных.
(Замечу, что есть по крайней мере одно типа исследование, которое "доказывает", что плотность кода CISC ниже, чем RISC: но, во-первых, сами авторы там между строк замечают, что, фактически, их сравнение неправомерно, поскольку сравнивают современные 64-разрядные архитектуры ARMv8 и RISC-V с древними CISCами, где 64-разрядные расширения были добавлены много позже -- а соответственно, все короткие коды операций уже попросту были заняты; а во-вторых, они начисто игнорируют случаи, когда сложные операции выполняются на CISCах одной-двумя командами, а на RISCах требуют подпрограмм значительного размера -- т.е., попросту говоря, подбирают тесты под требуемые результаты)
Усложнение декодера -- тоже далеко не факт. В частности, декодер для системы команд Thumb-2 будет сложней, чем даже для современных мэйнфреймов z/Architecture, не говоря о классической Системе 360
Усложнение управляющей логики, о чём автор почему-то не говорит, -- чуть ли не единственный пункт, где можно было бы согласиться, но и здесь не всё просто. Многие CISCи (а с 1970-х годов -- пожалуй, все или почти все) используют не чисто схемное, а микропрограммное или смешанное микропрограммно-аппаратное управление, что многократно упрощает конструкцию управляющей части процессора (память микропрограмм многократно проще и дешевле, чем управляющая логика). Впервые микропрограммы были использованы на младших и средних моделях Системы 360; старшие модели делались со схемным управлением (и были конвейерными) -- разработчики опасались, что микропрограммное управление будет медленнее. Но, как оказалось, оно может быть столь же быстрым: это зависит от особенностей микроархитектуры; поэтому, набив руку, его стали использовать и в топовых моделях. Скажем, в младших моделях (IBM System360/30 и наша ЕС-1020) АЛУ было 8-разрядным, а соответственно, простейшая 32-разрядная операция требовала для своего выполнения четыре микрокоманды только для собственно вычислений -- не говоря о всяких других потребных действиях; понятно, что это было очень медленно -- но требовало очень мало аппаратуры, почему, собственно, и использовалось. Однако, если не экономить оборудование для выборки команд и для собственно выполнения операций, разницы в скорости с машиной с чисто схемным управлением не будет, а разница в объёме аппаратуры окажется весьма значительной. Скажем, наша ЕС-1130 при заполненном конвейере выполняла простые команды регистр-регистр и регистр-память (!) за один такт, но была микропрограммной, просто вся микропрограмма для этих простых команд состояла из единственной микрокоманды (а вот выборка команды, декодирование, вычисление адресов операндов и их подготовка в ней были сделаны чисто аппаратно; микропрограмма управляла лишь ходом выполнения собственно обработки данных -- была, как я уже сказал, элементарнейшей для простейших операций, но сложной для сложных). Примерно ту же идею, только в значительно более сложном исполнении (в первую очередь, из-за суперскалярности и внеочередного исполнения) используют и современные CISC-процессоры (что у интеловских процессоров есть микрокод, наверное, все знают).
В 1980-х RISC-микропроцессоры (подчёркиваю микро) показывали более высокую производительность над CISC-микропроцессорами за счёт того, что благодаря очень малому числу команд (первый из них насчитывал, насколько помню, всего 31 команду) и другим упрощениям его можно было сделать конвейерным, но всё равно впихнуть на один кристалл; для CISC-процессора на тот момент это было невозможно, поэтому приходилось выбирать: либо медленная микропрограммная реализация на одном кристалле, либо быстрая, но на куче микросхем (в IA-32 первым конвейерным стал 80486; в мэйнфреймах конвейерными давно уже были все модели, а некоторые -- и суперскалярными, но они достаточно долго оставались многокристальными). Однако, если рассматривать все процессоры, а не только микро, RISC по скорости первыми отнюдь не были -- но далеко не каждый может позволить себе поставить мэйнфрейм или, тем более, какой-нибудь Cray с охлаждением жидким азотом (и хорошо, если азотом). Но в 1990-х, когда количество транзисторов на кристалле значительно выросло, конвейерные CISC тоже стали однокристальными -- и догнали (а нередко и перегнали) по скорости RISC. Да и сами современные RISC, прямо скажем, зачастую мало чем отличаются от CISC: имеют сотни, а то и тысячи команд, включают ряд достаточно сложных операций и т.д. Изначальная идея RISC (напомню, у первого из них была 31 команда, и среди них не было даже умножения и деления -- они сложные) оказалась несовместима с высокой производительностью, поэтому от неё отошли; фактически единственное, что осталось -- это отсутствие команд обработки данных вида "регистр-память" или "память-память" -- вся обработка выполняется исключительно в регистрах.
Большое спасибо, за пояснение и разбор моих ошибок. При чтении статей про историю CISC я, видимо, не совсем корректно понял информацию по кризису CISC 80-ых, перепутал момент по поводу использования памяти, не до конца раскрыл другие пункты, и не упомянул некоторые краеугольные проблемы CISC 80-ых годов,которые и стали причиной кризиса. Буду изучать этот вопрос подробнее, чтобы в дальнейшем избежать таких ошибок и неточностей.
Сколько места займёт подпрограмма шифрования AES на ARM или RISC-V? Ну а на мэйнфреймах z/Architecture это делается одной командой.
Эти команды и на RISC процессорах есть. RISC на самом деле - не про простоту или небольшое количество инструкций (хотя название намекает), а про минимальную прослойку между ISA и железом процессора - изначально речь шла про борьбу с микрокодом, то есть о выкидывании инструкций, реализация которых требует по сути отдельных подпрограмм внутри процессора. Если в процессор добавляется специальный юнит для крипто операций, расширение ISA для работы с ним идее RISC не противречит.
Про RISC-V не знал -- по большому счёту, я с ним не знаком, хотя всё собираюсь заняться. В известных же мне типа RISCах подобные операции (шифрование, например) выполняются железными блоками, которые расположены отдельно от процессора и, по сути, выступают в роли внешних устройств, т.е. частью соответствующей архитектуры не являются. Таковы, например, многие микроконтроллеры ARM: сама система команд никаких тебе шифраций или, скажем, преобразования UTF-8 в UTF-32 или обратно не содержит.
Ну и опять повторюсь, что от изначальной идеи RISC (а она -- именно что небольшое количество простых команд) отказались, считай, полностью -- она оказалась неконкурентоспособной с CISCами, как только совершенствование технологий дало возможность конвейерный (а позже и суперскалярный) CISC впихнуть на один кристалл. Так что лично для меня сейчас RISC -- это архитектура, не имеющая команд обработки данных прямо в памяти, ибо это, по сути, единственное, что нынешние RISCи отличает от нынешних CISCов, хотя и это различие, похоже, постепенно уходит (ведь, полагаю, RISC-V шифрует данные в памяти? Регистров у него не так уж много, и большой объём в них не впихнуть. Надо как-нибудь изучить сей вопрос...)
подобные операции (шифрование, например) выполняются железными блоками, которые расположены отдельно от процессора
Да, для микроконтроллеров это логично, но в серверных чипах вполне можно выделить транзисторы на самом процессоре.
от изначальной идеи RISC (а она -- именно что небольшое количество простых команд)
Всё таки изначально Паттерсон в статье выступал в основном против микрокода. И да, в те времена - когда даже FPU на чипе встречалось нечасто - ISA, отображающая только то, что реализовано в железе, получалась весьма компактной. Но если в микроархитектуру добавляются векторные регистры, специализированные блоки в ALU и т.д. - странно не предоставлять к этому архитектурный доступ на уровне ISA.
ведь, полагаю, RISC-V шифрует данные в памяти? Регистров у него не так уж много
Инструкции работают со скалярными или векторными регистрами, если надо обработать большой массив, нужен цикл. И да, сейчас видимые отличие CISC/RISC скорее в наличии/отстутствии аргументов в памяти в арифметических инструкциях. Хотя сейчас в ARM есть, скажем, CISC-style атомики, инкрементирующие значение в памяти одной инструкцией, в спецификации (в железе пока не видел) есть и инструкции копирования памяти по типу REP MOVS в x86.
Инструкции работают со скалярными или векторными регистрами, если надо обработать большой массив, нужен цикл. И да, сейчас видимые отличие CISC/RISC скорее в наличии/отсутствии аргументов в памяти в арифметических инструкциях. Хотя сейчас в ARM есть, скажем, CISC-style атомики, инкрементирующие значение в памяти одной инструкцией, в спецификации (в железе пока не видел) есть и инструкции копирования памяти по типу REP MOVS в x86.
Кстати говоря, для реализации атомарных операций вроде И, ИЛИ шина AXI имеет соответствующие навороты, чтобы операции эти фактически выполнялись контроллером памяти, а не процессором. Откопал, когда смотрел сию шину (использую в своих мегапроектах на ПЛИС, поскольку она используется для подключения контроллера памяти).
В общем, похоже, от изначальных RISCов теперь не осталось ничего. Ну а микрокод высокой производительности ни разу не противоречит, как и я говорил, и всякие там Интелы на практике доказывали. Как по мне, бороться с ним не надо -- он даёт гибкость (медленная, но компактная реализация тоже может быть ценной -- а использование микропрограммного управления даёт возможность легко подстраиваться под потребности). В общем, исходить, смотря по задаче.
микрокод высокой производительности ни разу не противоречит
Изначальный посыл RISC был в том, что реализованные в микрокоде инструкции со сложной семантикой удобны скорее человеку - компиляторы их не генерируют, а большая часть ПО пишется на языках высокого уровня - соответственно, все эти навороты с микрокодом только зря расходуют транзисторы, которые можно использовать более продуктивно.
RISC-V, в отличие от x86 и Arm, не требует спекулятивного выполнения для высокой производительности
И что он предлагает взамен (и как тут вообще влияет ISA)? Вроде пока ничего лучше для ускорения обычного однопоточного кода не придумано.
Мне кажется, автор просто что-то там недопонял. Недостаточное понимание видно, например, из "описания" MMU -- ведь принципиально и на ARM, и на IA-32, и почти в любых других архитектурах MMU работают одинаково -- точно так же, как ещё в Системе 370 (несколько уровней таблиц переадресации в памяти, базовый адрес таблицы верхнего уровня -- в некоем специальном регистре, а для ускорения преобразования -- TLB).
Тут вы правы, я в этой теме только начинаю вариться и не сильно "прошарен")
Этот проект - по сути, моя курсовая, которую я опубликовал на хабр, больше не с целью того, чтобы не знающие люди почерпнули для себя что-то новое в теме процессорных архитектур(хотя и не без этого),а получения критики от более матёрых в этой теме людей, чтобы более основательно погрузится в изучение вопросов архитектур и их безопасности.
Тут я не совсем корректно выразился. Я имел в виду, что в основе у RISC-V есть ядра, которые не поддерживают спекулятивное выполнение, что понижает производительность этих самых ядер, но делает их менее уязвимыми к Spectre-атакам, когда как у x86 и ARM все ядра имеют поддержку спекулятивного выполнения. Я здесь больше говорю не про производительность, а про уязвимость к Spectre и Meltdown.
когда как у x86 и ARM все ядра имеют поддержку спекулятивного выполнения
По крайней мере ARM ядра c in order микроархитектурой ещё живы; насчёт x86 не уверен, но они достаточно долго были таковыми (спекулятивное выполнение появилось только в Pentium Pro) и никто не мешает сделать новое x86 in order ядро - вопрос только в рыночной востребованности, сама по себе ISA не требует какой то конкретной микроархитектуры. При этом если RISC V таки дойдёт до HPC и энтерпрайза, спекулятивное выполнение в таких чипах скорее всего будет - например, у Syntacore в ядрах начиная с SCR6 спекулятивное выполнение есть.
Ну, во-первых, ядра с внеочередным выполнением команд -- это вообще только для максимальной производительности любой ценой, что не везде нужно.
Во-вторых, и x86, и ARM -- понятия очень сильно растяжимые. У последнего, в частности, выпускаются процессоры с ядрами, где даже простой суперскалярности нет, не говоря уже о внеочередном выполнении, а среди микроконтроллеров таких явно больше половины -- а ведь они тоже ARM.
Ну а в-третьих, указанные уязвимости прямо не связаны с суперскалярностью и внеочередным исполнением: там очень большую роль играет стратегия использования кэша. Скажем, у мэйнфреймов любое прерывание вызывает т.н. сериализацию ("временную отмену совмещений", как её переводили в советской литературе), в результате чего, по сути, кэш очищается (все изменения, внесённые программой, выполнявшейся до прерывания, записываются в память, а все ранее выбранные данные аннулируются, заставляя в дальнейшем сначала выбирать их из памяти). Соответственно, любые данные, попавшие в кэш в результате спекулятивного выполнения, в случае прерывания будут благополучно "забыты" -- а, если мне память не изменяет, Spectre был основан именно на таком сценарии (обращение к запрещённой области с последующим прерыванием и возобновлением работы -- но чужие данные уже в кэше). Хотя, возможно, с чем-то путаю, смотрел очень давно...
у мэйнфреймов любое прерывание вызывает т.н. сериализацию ("временную отмену совмещений", как её переводили в советской литературе), в результате чего, по сути, кэш очищается
А как при этом с производительностью в клиент серверных приложениях, когда каждая микросекунда отклика на счету (например HFT), при этом данные для следующего запроса могут быть в кеше процессора? Или там есть технологии типа DPDK с возможностью обработать запрос из сети без прерываний и переключения в контекст ядра?
На практике, подобный подход (слабая модель упорядочивания доступов памяти) открывает возможность почти что неограниченного масштабирования машины: можно добавлять сколько угодно процессоров, упор имеет место, скорей, в массо-габаритные характеристики, энергопотребление и т.п. вещи. А у, скажем, IA-32 с его аппаратным поддержанием согласованности кэшей всех процессоров (сильная модель упорядочивания доступов к памяти) такая возможность ограничена необходимостью иметь, фактически, связь процессоров (их кэшей, причём всех уровней) "все со всеми". Очевидно, что чем больше процессоров, тем больше они порождают трафика обновления данных в кэшах -- и это становится проблемой, ведь любая запись, произведённая любым процессором, должна быть передана кэшам всех остальных процессоров машины. Кроме того, насколько понимаю, операции записи, выполняемые периферией с DMA, тоже автоматически попадают в кэши процессоров.
Что же касается производительности... Судя по всему, именно производительность у мэйнфреймов вполне себе на уровне. Однако скорость реакции на прерывания для них никогда не была определяющей (несмотря на рекламу ИБМ про "360 градусов" охвата задач, которые они могут решать -- это было, естественно, неверным даже в 1964-м, когда она их анонсировала): исторически они рассчитывались на пакетную обработку данных. В классической OS/360 оно ухудшалось ещё больше самой системой: изрядное время её код выполнялся при полностью запрещённых прерываниях, что, понятное дело, не ускоряло реакцию на их появление. Так что, думаю, обработкой срочных запросов занимаются машины других архитектур, а на мэйнфреймах лежит, так сказать, бэкенд -- гигантские базы данных и всё такое, где важней общая производительность, а не время ответа. Ещё одним вариантом может быть обработка прерываний (шире -- организация всего ввода-вывода) на одних процессорах и обработка данных -- на других, причём для уведомления других процессоров о появлении для них работы использовать не прерывания, а изменение всяких семафоров и прочих мьютексов в памяти: система команд располагает командами, осуществляющими лишь частичную сериализацию и поэтому позволяющими реализовывать быстрые примитивы синхронизации без необходимости каждый раз дёргать систему. Но это создаёт дополнительные проблемы для программиста (впрочем, они есть у любой архитектуры со слабой моделью -- на тех же ARM, где в определённых случаях нужно ручками вставлять барьеры, в то время как на IA-32 это требуется намного реже).
необходимостью иметь, фактически, связь процессоров (их кэшей, причём всех уровней) "все со всеми".
Это в любом случае нужно для когерентности кешей в любой модели памяти (записи в разные части кеш линеек должны корректно мёрджиться).
Так что, думаю, обработкой срочных запросов занимаются машины других архитектур, а на мэйнфреймах лежит, так сказать, бэкенд -- гигантские базы данных и всё такое, где важней общая производительность, а не время ответа.
Да, возможно так - основная база на мэйнфрейме, а какие-нибудь кеши в памяти и т.п. на машинках попроще.
Это в любом случае нужно для когерентности кешей в любой модели памяти (записи в разные части кеш линеек должны корректно мёрджиться).
Не нужно. Когда производится сериализация, в память записываются не целые строки кэша, а только те байты, которые реально были изменены. Соответственно, если один процессор изменяет байты 0-3, а другой -- 4-7 в одном и том же блоке памяти, занимающем одну строку кэша (скажем, 64-байтовом), то в память всё запишется корректно.
Так вопрос не в сериализации, а в постоянных обращениях разных процессоров к разным частям кеш линейки (с обновлениями) без прерываний и системных вызовов.
Так они и не будут видеть изменения, вносимые друг другом -- это нормально. Даже на машинах со строгой моделью (IA-32) "мгновенной" видимости нет, нужно использовать барьеры, ну а на машинах со слабой моделью нужно ещё обеспечить принудительную очистку соответствующих строк кэша, чтобы другие процессоры смогли увидеть изменения.
Но если Вы имеете в виду, что один процессор работает только с одними байтами, а другой -- с другими байтами, т.е. имеющими разные адреса, то абсолютно никаких проблем не возникает: им же без разницы, что творится с байтами, которые они не используют.
один процессор работает только с одними байтами, а другой -- с другими байтами, т.е. имеющими разные адреса
Именно так. Но в кешах гранулярность хранения - кеш линия (обычно 64 байта) и просто так менять отдельные части нельзя (иначе у разных процессоров будут разные версии, при этом кеш линейка не отслеживает, какие байты в ней изменены). Если, скажем, разные процессоры меняют разные целые числа в одной линейке, в каждый момент должна быть одна валидная версия, содержащая изменения от всех процессоров. Для синхронизации есть протоколы когерентности, причём их использование необходимо независимо от модели памяти.
Почему Вы решили, что строка кэша не отслеживает изменения отдельных байтов? Всё она отслеживает, поэтому никаких проблем не возникает.
Потому что работал с этими вещами не один десяток лет. Даже если ввести в теги по биту на каждый байт в кеш линии - получается, что при вытеснении грязной линии в память или следующий уровень надо записывать только отдельные байты, что сильно усложнит аппаратуру - и так сейчас не делают. Самое близкое, что видел - отдельные dirty bits на каждые 16 байт в кеш линии на одной GPUшке; на CPU только общий dirty bit на кеш линию.
Я тоже не один десяток лет работал и работаю. Аппаратура сколько-нибудь существенно не усложняется, все необходимые линии индикации, какие байты, записываются, имеются. См., например, шину AXI -- основную на ARMах (кроме не шибко мощных микроконтроллеров, где основной будет AHB -- но там та же история):

При записи данные идут на WDATA, а на WSTRB каждому байту данных соответствует свой сигнал, показывающий, надо ли писать этот байт. Сделано, в т.ч., для быстрой записи содержимого кэша (выполняется пакетная запись всей строки кэша, а WSTRB показывают, что пропускать, а что писать).
Со стороны памяти тоже усложнения или замедления не будет: в любом случае, когда строка памяти открывается, она целиком считывается во внутренний регистр микросхемы памяти; в случае записи в нём заменяются нужные байты, в случае чтения ничего не заменяется, но в конце операции весь регистр опять записывается в строку памяти -- выполняется регенерация этой строки. Причём строка там много шире, чем количество ног данных у микросхемы, что и позволяет достичь очень высокой скорости передачи информации.
На IA-32 -- строгая модель памяти, поэтому там нужды в записи отдельных байтов из кэша действительно нет. Но не стоит переносить этот опыт на всю вычислительную технику. (А как в графпроцессорах, я, честно говоря, не знаю, но это очень специфическая железка со своими заморочками, и там можно встретить что угодно, думаю.)
Возможно на микроконтроллерах делают по своему. На серверных ARM процессорах кеш линейки пишутся целиком.
С AXI шиной не работал. Она реально используется в многосокетных системах для обмена данными между сокетами или для работы с памятью?
Ну, у тех армовских ядер, что я видел, из ядра наружу торчит именно AXI (или AHB, но это на младших и средних микроконтроллерных, на Cortex-M7 -- уже AXI). Полагаю, внутри кристаллов от процессорных ядер до контроллера памяти идёт именно она (на ПЛИС так точно она, но ПЛИС -- всё же специфическая вещь). Вот снаружи микросхемы (кристалла), надо полагать, идёт уже обычная PCI Express, ибо она -- стандарт де-факто для всего современного мира.
Что касается серверных процов (и вообще процов) ARM, то слабая модель памяти не запрещает обеспечивать аппаратное согласование кэшей и т.д. -- она лишь разрешает этого не делать. Что её делают по крайней мере в некоторых реализациях архитектуры, я знаю, и есть предположение, что это для облегчения работы Линуха: если не возложить сию задачу на аппаратуру, придётся много что подпиливать в системе, которая изначально, как Вы не хуже меня знаете, возникла на IA-32, где всё это гарантированно не требуется на уровне архитектуры (сильная модель).
Скажем, если на одном процессоре захватили спин-блокировку и модифицировали некий управляющий блок, то перед освобождением этой блокировки надо гарантированно вытеснить данный блок (и всё, что с ним связано) из кэша в память, а на другом процессоре, ожидающем эту блокировку, после её захвата надо сначала гарантированно очистить свой кэш от старых копий этого участка памяти; очевидно, что это весьма геморройно, особенно если этого нельзя добиться одной командой. На M-профиле нельзя, там надо гулять по всему кэшу и вытеснять его строки в цикле, что само по себе весьма и весьма медленно; про A-профиль, который в телефонах и серверах, я не в курсе, подробно не смотрел доку. Вот на мэйнфреймах полную сериализацию можно сделать одной командой. Замечу попутно, что в таких ситуациях сериализация может оказаться весьма быстрой, если кэш оптимизирован под такие сценарии: скажем, если на упомянутом M-профиле приходится выполнять цикл несколько сотен раз (зависит от объёма и организации кэша), чтоб пройтись по каждой строке, железная поддержка может сразу анализировать множество строк и обеспечивать запись лишь из содержащих грязные байты, а чистые строки просто аннулировать -- что будет довольно быстро (особенно в подобном сценарии, достаточно характерном для ядер ОС, где нет больших объёмов данных, особенно изменяемых, а соответственно, почти все строки кэша между сериализациями остаются чистыми).
Но есть ещё момент, связанный с вводом-выводом. На ПК, вроде как, кэши процов отслеживают не только записи со строноны всех процов, но и доступы, выполняемые со стороны внешних устройств. Соответственно, после завершения операции чтения чистить кэши для гарантированного поступления процессорам новых данных не требуется, а перед записью не нужно записывать грязные строки в память: контроллер памяти, заметив обращение устройства по DMA к кэшированной строке, вернёт данные из кэша, а не из физической памяти (если ошибаюсь, прошу поправить). В ARMах, надо полагать, требуется ручками чистить кэш, чтоб гарантированно выполнить обмен с устройством правильно.
из ядра наружу торчит именно AXI
Вопрос для чего именно. Я то больше с "большими" процессорами работаю - там есть AMBA для связи с периферией (и вроде как AXI входит в AMBA), но работа с памятью идёт через интегрированные контроллеры, для связи между сокетами - отдельные каналы со своим протоколом.
И дело же не только в том, что торчит наружу - на телефонах, скажем, обычно процессорный чип один, но внутри него кучка разнородных ядер со своми кешами, и надо чтобы многопоточный софт корректно работал.
и есть предположение, что это для облегчения работы Линуха
Дело не только в операционке, без когерентности кешей обычный код написанный на C или Фортране (скажем, с распараллеливанием через OpenMP) не будет работать корректно, и барьеры не помогут.
там есть AMBA для связи с периферией (и вроде как AXI входит в AMBA)
Да, AXI, как и другие шины (AHB, APB, ещё что-то, вроде бы...) -- часть AMBA.
работа с памятью идёт через интегрированные контроллеры
Угу. И наверняка связь ядро-контроллер памяти -- через AXI (нет смысла специально для этого изобретать какую-то другую шину).
И дело же не только в том, что торчит наружу - на телефонах, скажем, обычно процессорный чип один, но внутри него кучка разнородных ядер со своми кешами, и надо чтобы многопоточный софт корректно работал
Вот для упрощения работы многопоточного софта могут сделать аппаратное согласование содержимого кэшей -- но, строго говоря, архитектура этого не требует (там слабая модель), что позволяет, при необходимости, строить очень крупные системы -- но перекладывая заботу о согласованности на ПО.
Дело не только в операционке, без когерентности кешей обычный код написанный на C или Фортране (скажем, с распараллеливанием через OpenMP) не будет работать корректно, и барьеры не помогут
Почему ж не помогут, если они вставляются там, где нужно? На машинах с сильной моделью барьер лишь приостанавливает работу, пока изменения, внесённые в кэш, не будут гарантированно доставлены всем другим процессорам; на машинах со слабой моделью будет выполнена принудительная запись изменённых байтов из кэша в память и ожидание, пока запись физически не закончится (естественно, на приёмной стороне тоже должен быть соответствующий барьер -- обеспечивающий очистку кэша от старых данных).
Т.е. программно это реализовать вполне можно на, скажем, уровне стандартной библиотеки (атомики и барьеры в современных версиях C++ вполне дают возможность это сделать; вот насчёт Фортрана не уверен). Ну и, в любом случае, это будет обеспечиваться при вызове сервисов ОС.
Кстати, возможен и промежуточный вариант для NUMA. Скажем, процессор (многоядерный, есно) плюс непосредственно окружающая его память обеспечивают аппаратное согласование кэшей, а между узлами NUMA такого согласования уже нет. ОС запускает связанные потоки только на одном узле NUMA, а соответственно, они могут не особо заботиться о дополнительных телодвижениях (кроме обычных барьеров -- без них в любом случае не обойтись). А вот если нужна связь потоков, работающих на разных узлах, -- там уже изволь выполнять все нужные телодвижения (или просить ОС об этом). В этом случае можно обеспечить и относительную лёгкость программирования многопоточных приложений для сильной модели, и (почти) неограниченное масштабирование машины, обеспечиваемое слабой моделью.
наверняка связь ядро-контроллер памяти -- через AXI
Не уверен, там же отношение многие ко многим (скажем, 8 контроллеров памяти на 64 ядра без явной привязки) - в любом случае такие вещи не особо документируются.
Почему ж не помогут, если они вставляются там, где нужно?
Потому что они не об этом. Барьеры указывают о том, в каком порядке видны изменения разных ячеек памяти; то, что изменения разных значений в одной кеш линейке от разных агентов корректно накладываются, обеспечивается механизмом когерентности, который к модели памяти и необходимости использования барьеров ортогонален.
Кстати, возможен и промежуточный вариант для NUMA. Скажем, процессор (многоядерный, есно) плюс непосредственно окружающая его память обеспечивают аппаратное согласование кэшей, а между узлами NUMA такого согласования уже нет.
В общепринятой программной модели NUMA влияет только на производительность - обращение к "чужой" памяти дороже - но не на корректность, за когерентностью кешей следит железо и дополнительно в коде для корректности работы ничего писать не надо (для того, чтобы не проседала производительность - надо следить, к какой памяти идём).
Но проблемы когерентности вылезают и без NUMA, когда у нас просто на одном чипе несколько ядер со своими кешами первого/второго уровня.
Для меня как новичка любителя вполне научная статья, спасибо за работу
RISC-V и безопасность: анализ методов защиты открытой архитектуры