Модель памяти x86 TSO в основном состоит из порядка программ плюс буфер хранилища с пересылкой хранилища.
Большинство полученных гарантий теоретически довольно легко реализовать на оборудовании, просто имея буфер хранилища и связная разделяемая память; буфер хранилища изолирует OoO exe c от требований фиксации по порядку (и от хранилищ с ошибками кеширования) и позволяет спекулятивно выполнять сохранения и перезагрузки.
Все ядра могут согласовать общий порядок , в котором все хранилища произошли . Или, точнее, ядра не могут не соглашаться ни в какой части общего порядка, который они действительно могут наблюдать. Одновременные записи в 2 разные строки являются одновременными, поэтому любые наблюдения совместимы с любым порядком в гипотетическом общем порядке.
Это происходит автоматически, если единственный способ сделать хранилище видимым для любого другого ядра делает его видимым для всех ядра одновременно. то есть путем принятия согласованного L1d. Это делает невозможным изменение порядка IRIW. (MESI гарантирует, что хранилище не сможет зафиксировать L1d, если оно не принадлежит исключительно этому ядру: ни у одного другого ядра нет действительной копии.)
На самом деле любое оборудование не редко может иметь это свойство; некоторые процессоры POWER могут хранить вперед между потоками SMT на одном и том же физическом ядре , что позволяет двум считывающим устройствам не согласовывать порядок сохранения двумя записывающими устройствами (переупорядочение IRIW). Несмотря на то, что процессоры x86 также часто имеют SMT (например, Intel HyperThreading), модель памяти требует, чтобы они не пересылали память между логическими ядрами. Это хорошо; они в любом случае статически разбивают буфер хранилища. Что будет использоваться для обмена данными между потоками, выполняемыми на одном ядре с HT? . А также Каковы затраты на задержку и пропускную способность при совместном использовании области памяти производителем и потребителем между гипер-братьями и сестрами по сравнению с не-гипер-братьями? для экспериментального тестирования.
Единственное переупорядочение, которое происходит локально, внутри каждого ядра ЦП, между его обращениями к этому глобально согласованному общему состоянию. (Вот почему локальные барьеры памяти, которые заставляют это ядро ждать, пока что-то произойдет, например, для магазина буфер для слива, может восстановить последовательную согласованность поверх x86 TSO. То же самое применимо даже к более слабым моделям памяти, BTW: просто локальное переупорядочение поверх согласованности MESI.)
Остальные гарантии применяются к каждому ( логично) ядро процессора индивидуально. ( Вопросы и ответы о том, как это может создать синхронизацию между ядрами.)
Магазины становятся видимыми в программном порядке : фиксация по порядку из магазина буфер в кэш L1d. (Записи буфера хранилища распределяются в программном порядке во время выдачи / переименования). Это означает, что хранилища промахов кэша должны блокировать буфер хранилища, не позволяя более молодым хранилищам фиксироваться. См. Почему RFO после выхода на пенсию не нарушает упорядочение памяти? для простой ментальной модели этого и некоторых подробностей о том, что Skylake действительно может делать (с фиксацией данных из промахов магазина в LFB во время ожидания строк кеша. чтобы прибыть).
Загрузки не переупорядочиваются в более поздних хранилищах : easy: требуется полное завершение загрузки (данные из кэша L1d), прежде чем они могут быть удалены . Поскольку вывод из эксплуатации в порядке, и магазин не может подтвердить L1d до , после он не будет отключен (станет неспекулятивным), мы получаем бесплатный заказ LoadStore 1 .
Загрузки берут данные из когерентного кеша (памяти) в программном порядке . Это сложный вопрос: загружает глобальное состояние доступа (кеш) при их выполнении, в отличие от хранилищ, где буфер хранилища может поглощать несоответствие между OoO exe c и фиксацией по порядку. Фактически, если сделать каждую загрузку зависимой от предыдущих загрузок, это предотвратит попадание под промах и лишит многих преимуществ выполнения вне очереди для кода, который задействует память.
На практике процессоры Intel агрессивно предполагают, что строка кэша, которая присутствует сейчас, будет все еще , когда это архитектурно разрешено для загрузки (после выполнения более ранних загрузок). Если это не так, уничтожьте конвейер (неверное предположение о порядке памяти). Для этого есть событие счетчика производительности.
На практике все может быть сложнее, чтобы добиться большей производительности, или намного больше для спекулятивных ранних загрузок.
( В терминах C ++ это, по крайней мере, такое же сильное, как acq_rel
, но также охватывает поведение вещей, которые могут быть UB в C ++. Например, загрузка частично перекрывает недавнее хранилище с местоположением, которое может также выполняет чтение или запись, позволяя этому ядру загружать значение, которое никогда не появлялось или не появится в памяти для загрузки других потоков. Глобально невидимые инструкции загрузки )
связанные вопросы и ответы:
Сноска 1: Некоторые слабоупорядоченные процессоры OoO exe c могут выполнять упорядочивание LoadStore re , предположительно, позволяя нагрузкам удаляться из ROB, пока загрузка проверяет разрешения и запрашивает строку кэша (на случай промаха), даже если данные еще не поступили. Требуется отдельное отслеживание того, что регистр не готов, а не обычный планировщик инструкций. для приемлемой производительности. Как можно переупорядочить загрузку-> сохранение с помощью фиксации в порядке?