Является ли модель памяти Intel избыточными и LFENCE?

Модель памяти Intel гарантирует:

  • Магазины не будут переупорядочены в других магазинах
  • Нагрузки не будут перенаправлены с помощью других нагрузок

http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/

Я видел утверждения, что SFENCE избыточно на x86-64 из-за модели памяти Intel, но никогда LFENCE. Выполняют ли указанные выше правила модели памяти либо избыточные инструкции?

Ответы

Ответ 1

Да, SFENCE и LFENCE не являются операциями, за исключением случаев использования хранилищ потоковой передачи movnt (Non-Temporal) или работы с областями памяти с типом, установленным на нечто иное, чем нормальное Write-Back. Хранилища NT обходят кеш, а также слабо упорядочиваются (но x86 нормальная модель памяти сильно упорядочена (за исключением приведенных ниже для опций Fast-String)). (NT загружает (movntdqa) из памяти WB по-прежнему сильно упорядочен, поэтому LFENCE полезен только при чтении из слабо упорядоченной памяти). Это происходит не случайно в "нормальных" программах, поэтому вам нужно только беспокоиться об этом, если вы mmap видеопамять или что-то в этом роде.

Это сообщение: Переупорядочение памяти, занесённое в акте, - это более легкое для чтения описание того же случая, о котором сообщает Bartosz post, где вам нужен барьер StoreLoad, такой как MFENCE.

Если у вас возникли вопросы после прочтения этой ссылки, вы опубликовали сообщения в блоге Jeff Preshing. Они дали мне хорошее представление о предмете.:) Хотя я думаю, что я нашел лакомый кусочек о SFENCE/LFENCE, как правило, не являющемся op на странице Doug Lea. Сообщения Jeff не учитывали загрузки/хранения NT.


Мне это любопытно пару недель назад, и выложил довольно подробный ответ на недавний вопрос: Атомные операции, std:: atomic < > и упорядочение записей. Я включил множество ссылок на материал о модели памяти моделей С++ и аппаратной памяти.

Если вы пишете на С++, использование std::atomic<> - отличный способ рассказать компилятору, какие у вас требования к заказу, поэтому он не изменяет порядок операций с памятью во время компиляции. Вы можете и должны использовать более слабый выпуск или приобретать семантику, где это необходимо, вместо последовательной согласованности по умолчанию, поэтому компилятору вообще не нужно выводить какие-либо барьерные инструкции на x86. Он просто должен сохранить ops в исходном порядке.


В слабо упорядоченной архитектуре, такой как ARM или PPC, или x86 с movnt, вам нужна команда StoreStore для написания буфера и установка флага для указания готовности данных. Кроме того, читателю нужна инструкция по загрузке LoadLoad между проверкой флага и чтением буфера.

Не считая movnt, x86 уже имеет барьеры LoadLoad между каждой нагрузкой и барьеры StoreStore между каждым магазином. (Заказ LoadStore также гарантирован). MFENCE - все четыре вида барьеров, включая StoreLoad, который является единственным барьером x86 по умолчанию. MFENCE гарантирует, что нагрузки не будут использовать старые предварительно выбранные значения до того, как другие потоки увидели ваши магазины и, возможно, сделают собственные магазины. (А также быть препятствием для упорядочивания заказа и заказа на загрузку в NT).

Забавный факт: x86 lock -предоставляемые инструкции также являются полными барьерами памяти. Они могут использоваться в качестве замены для MFENCE в старом 32-битном коде, который может работать на процессорах, не поддерживающих его. lock add [esp], 0 в противном случае является no-op, и цикл чтения/изменения/записи в памяти очень важен в кеше L1 и уже в состоянии M протокола согласования MESI.

SFENCE - это барьер StoreStore.

LFENCE - это LoadLoad и также барьер LoadStore. (loadNT / LFENCE / storeNT предотвращает глобальное видимость хранилища перед загрузкой. Я думаю, что это может произойти на практике, если адрес загрузки был результатом длинной цепи зависимостей или результатом другой загрузки, пропущенной в кеше.)


Забавный факт # 2 (спасибо @EOF): магазины из Fast-String Ops (rep stosb/rep movsb на IvyBridge и позже) слабо упорядочены (но не кэшируются).

Intel документирует тот факт, что Fast-String Ops "может показаться неработоспособным" в разделе 7.3.9.3 Руководства для разработчиков ПО, vol1. Они также говорят

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

Они не упоминают никаких барьерных инструкций. То, как я его читал, есть неявное SFENCE после rep stosb / rep movsb (по крайней мере, забор для строковых данных, возможно, не для другого слабого заказанного NT). Во всяком случае, формулировка подразумевает, что запись в флаг/семафор становится глобально видимой после того, как все записи строкового перемещения будут записаны, поэтому в коде, который заполняет буфер операцией быстрой строки, а затем записывает флаг, или нет, SFFF/LFENCE в коде, который его читает. (Упорядочивание LoadLoad всегда происходит, поэтому вы всегда видите данные в том порядке, в котором другие ЦП сделали его глобально видимым, т.е. Использование слабо упорядоченных хранилищ для записи буфера не меняет того факта, что нагрузки в других потоках по-прежнему сильно упорядочены.)

summary: используйте обычное хранилище для записи флага, указывающего, что буфер готов. У читателей нет проверки только последнего байта блока, написанного с помощью memset/memcpy. Тем не менее, я думаю, что хранилища быстрых строк не позволяют каким-либо более поздним хранилищам передавать их, поэтому вам все равно нужен SFENCE/LFENCE, если вы используете movnt.

Имеется бит MSR CPU, который можно очистить, чтобы отключить операторы быстрой строки, для новых серверов, которым необходимо запустить старые двоичные файлы, которые записывают флаг "data ready" как часть rep stosb или rep movsb.