как барьеры / ограждения и семантика приобретения и выпуска реализованы микроархитектурно?

Так много вопросов и статей / книг типа https://mirrors.edge.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2018.12.08a.pdf, статьи Preshing, такие как https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/ и вся серия его статей говорят об упорядочивании в памяти абстрактно, с точки зрения гарантий упорядочения и видимости, обеспечиваемых различными типами барьеров. Мой вопрос в том, как эти барьеры и семантика упорядочения памяти реализованы на микроархитектуре x86 и ARM?

Что касается барьеров между хранилищами, похоже, что на x86 буфер хранилища поддерживает программный порядок хранилищ и фиксирует их в L1D (и, следовательно, делает их глобально видимыми в том же порядке). Если буфер хранилища не упорядочен, т. Е. Не поддерживает их в программном порядке, как реализуется барьер хранилища? он просто «маркирует» буфер хранилища таким образом, что он сохраняет до фиксации барьера в когерентный домен кэша до сохранения после? или барьер памяти действительно очищает буфер хранилища и останавливает все инструкции до завершения очистки? Может ли это быть реализовано обоими способами?

Как предотвратить переупорядочение нагрузки-нагрузки для барьеров нагрузки-нагрузки? Трудно поверить, что x86 выполнит все нагрузки по порядку! Я предполагаю, что нагрузки могут выполняться не по порядку, но фиксируются / удаляются по порядку. Если да, то если ЦП выполняет 2 загрузки в 2 разных места, как одна загрузка гарантирует, что она получила значение, скажем, от T100, а следующая получила его в T100 или после него? Что делать, если первая загрузка отсутствует в кеше и ожидает данных, а вторая загрузка попадает и получает свое значение. Когда загрузка 1 получает свое значение, как она гарантирует, что полученное значение не из нового магазина, в котором загружено значение 2? если нагрузки могут выполняться не по порядку, как обнаруживаются нарушения порядка памяти?

Аналогичным образом, как реализованы барьеры между загрузкой и загрузкой (неявные во всех загрузках для x86) и как реализованы барьеры между загрузкой и загрузкой (например, mfence)? т.е. что делают инструкции dmb ld / st и just dmb на микроархитектуре на ARM, и что каждая загрузка и каждое хранилище, и инструкция mfence делают на микроархитектуре на x86, чтобы обеспечить упорядочение памяти?


person Raghu    schedule 23.09.2019    source источник
comment
Это Q об операциях с памятью или обычных объектах C в нормальной памяти, то есть об операциях с адресами, которые всегда попадают в кеш?   -  person curiousguy    schedule 24.09.2019


Ответы (1)


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


На x86 каждая загрузка asm является загрузкой-получателем. Чтобы реализовать это эффективно, современное аппаратное обеспечение x86 предположительно загружается раньше, чем разрешено, а затем проверяет это предположение. (Это может привести к сбою в конвейере неверного определения порядка памяти.) Чтобы отследить это, Intel называет комбинацию буферов загрузки и хранения «Буфером порядка памяти».

Слабо упорядоченные ISA не должны спекулировать, они могут просто загружаться в любом порядке.


Порядок хранилищ x86 поддерживается только за счет разрешения хранилищам фиксироваться из буфера хранилища в L1d в программном порядке.

По крайней мере, на процессорах Intel запись буфера хранилища выделяется для хранилища, когда оно выдает (из внешнего интерфейса в ROB + RS). Для всех мопов должна быть выделена запись ROB, но некоторым мопам также необходимо выделить другие ресурсы, такие как загрузка или сохранение записей буфера, записи RAT для регистров, которые они читают / записывают, и так далее.

Итак, я думаю, что сам буфер хранилища упорядочен. Когда выполняется uop-адрес хранилища или хранилище данных, он просто записывает адрес или данные в свою уже выделенную запись буфера хранилища. Поскольку фиксация (освобождение записей SB) и выделение выполняются в программном порядке, я предполагаю, что физически это кольцевой буфер с головой и хвостом, как ROB. (И в отличие от RS).


Отказ от LoadStore в основном бесплатен: загрузка не может быть удалена, пока она не будет выполнена (данные взяты из кеша). Магазин не может выполнить фиксацию до после его закрытия. Списание по порядку автоматически означает, что все предыдущие загрузки выполняются до того, как магазин будет «завершен» и готов к фиксации.

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

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

Как load- ›переупорядочивание магазина возможно с помощью in-order commit? рассматривает этот вопрос более глубоко, чтобы определить порядок и нарушение порядка.


Барьерные инструкции

Единственная инструкция барьера, которая что-либо делает для обычных хранилищ, - это mfence, которая на практике останавливает операции с памятью (или весь конвейер) до тех пор, пока буфер хранилища не будет опустошен. Загружаются и сохраняются единственные инструкции, которые переупорядочиваются? охватывает поведение Skylake-with-updated-microcode, которое также действует как lfence.

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

связанные с:

person Peter Cordes    schedule 23.09.2019
comment
Спасибо, Питер. 1) Не могли бы вы подробнее рассказать о проверках, которые предполагают часть загрузки? - person Raghu; 24.09.2019
comment
@Raghu: поищите вещи, которые могут вызвать неправильные предположения о порядке памяти. Я думаю, это включает в себя отслеживание того, была ли строка кэша недействительной между выполнением загрузки и отключением загрузки, возможно, путем отслеживания активности LFB и отметки этого буфера загрузки. Это довольно волнистая рука; если бы я знал что-то более конкретное, я бы вставил это в ответ. - person Peter Cordes; 24.09.2019
comment
Спасибо, Питер. Постараюсь узнать больше о MOB и т. Д. Я думаю, что магазин-магазин и магазин-загрузка понятны. Для load-store, если какой-либо CPU реализует ROB, он должен получить заказ load-store бесплатно. Есть ли какая-нибудь реализация, которая может даже теоретически переупорядочить загрузку-хранилище, учитывая, что все они должны сделать для вывода на пенсию, чтобы поддерживать иллюзию программного порядка для одного и того же ядра? - person Raghu; 24.09.2019
comment
@Raghu: Думаю, да. Как я уже сказал, исправное ядро ​​легко загружает табло после того, как проверит, что оно исправно. (т. е. обязательно произойдет, как в случае с устаревшим хранилищем, которое находится в буфере хранилища и ожидает фиксации). Загрузка может задерживаться сколь угодно долго в ожидании промаха кэша, пока ни одна инструкция не пытается прочитать целевой регистр. x86 имеет строго упорядоченную модель памяти (и требует упорядочивания нагрузки-нагрузки), поэтому x86 не будет пытаться это сделать, но для этого могут быть спроектированы слабо упорядоченные ядра. Может быть, даже с OoO. Придется поискать в гугле реальные примеры. - person Peter Cordes; 24.09.2019
comment
Спасибо, Питер. Просто для того, чтобы я четко понял ваш пример в порядке, вы говорите о ядрах в порядке (т.е. без спекулятивного выполнения, прогнозирования ветвлений и выполнения OoO), но со слабой моделью памяти? - person Raghu; 24.09.2019
comment
@Raghu: Да, как ARM Cortex-A53, который есть в большинстве смартфонов: в порядке, но в остальном довольно высокая производительность. Однако такие ядра по-прежнему будут иметь предсказание ветвлений, чтобы избежать пузырей выборки! Инструкции начинают выполняться по порядку, но могут завершиться не по порядку, если станет известно, что они не ошибаются. Но да, никакого умозрительного выполнения, только умозрительная выборка / декодирование. Нет ROB, просто суперскалярный конвейер. - person Peter Cordes; 24.09.2019
comment
Вот еще одно сообщение Питера, имеющее отношение к вышеупомянутому обсуждению: stackoverflow.com/questions/52215031/ - person Raghu; 24.09.2019
comment
@Raghu: да, спасибо, что нашел это. Добавил несколько ссылок на мой ответ. - person Peter Cordes; 25.09.2019
comment
Ожидает ли конвейер, пока буфер хранилища будет опустошен, как только он попадет в MFENCE? Или он будет продолжать отдавать инструкции, пока не столкнется с первой загрузкой? - person pveentjer; 06.05.2020
comment
@pveentjer: Зависит от микроархитектуры. В Skylake с обновлениями микрокода mfence включает lfence-подобное поведение, поэтому он останавливает интерфейс до тех пор, пока не истощится буфер хранилища. (Является ли загрузка и сохранение единственными переупорядоченными инструкциями?). Но locked инструкции, а mfence на некоторых других архивах только задерживают exec загрузок. Я не проверял, разрешает ли xchg [mem], reg ; load ; unrelated ALU выполнение независимой инструкции ALU до того, как буфер хранилища опустеет, но я надеюсь на это с помощью порядка отслеживания MOB (memory order buf). - person Peter Cordes; 06.05.2020