Делает ли смысл какой-либо смысл LFENCE в процессорах x86/x86_64?
Часто в Интернете я обнаружил, что LFENCE
не имеет смысла в процессорах x86, т.е. ничего не делает, поэтому вместо MFENCE
мы можем безболезненно использовать SFENCE
, потому что MFENCE
= SFENCE
+ LFENCE
= SFENCE
+ NOP
= SFENCE
.
Но если LFENCE
не имеет смысла, то почему у нас есть четыре подхода, чтобы сделать Sequential Consistency в x86/x86_64:
-
LOAD
(без забора) и STORE
+ MFENCE
-
LOAD
(без забора) и LOCK XCHG
-
MFENCE
+ LOAD
и STORE
(без забора)
-
LOCK XADD
(0) и STORE
(без забора)
Взято отсюда: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
Помимо выступлений Herb Sutter на стр. 34 внизу: https://skydrive.live.com/view.aspx?resid=4E86B0CF20EF15AD!24884&app=WordPdf&wdo=2&authkey=!AMtj_EflYn2507c
Если LFENCE
ничего не сделал, то подход (3) имел бы следующие значения: SFENCE + LOAD and STORE (without fence)
, но нет смысла делать SFENCE
до LOAD
. Т.е. если LFENCE
ничего не делает, подход (3) не имеет смысла.
Делает ли какая-либо команда чувств LFENCE
в процессорах x86/x86_64?
ОТВЕТ:
1. LFENCE
требуется в случаях, описанных в принятом ответе ниже.
2. Подход (3) следует рассматривать не независимо, а в сочетании с предыдущими командами. Например, подход (3):
MFENCE
MOV reg, [addr1] // LOAD-1
MOV [addr2], reg //STORE-1
MFENCE
MOV reg, [addr1] // LOAD-2
MOV [addr2], reg //STORE-2
Мы можем переписать код подхода (3) следующим образом:
SFENCE
MOV reg, [addr1] // LOAD-1
MOV [addr2], reg //STORE-1
SFENCE
MOV reg, [addr1] // LOAD-2
MOV [addr2], reg //STORE-2
И здесь SFENCE
имеет смысл предотвратить переупорядочение STORE-1 и LOAD-2. Для этого после команды STORE-1 SFENCE
выполняется сброс Store-Buffer.
Ответы
Ответ 1
Нижняя строка (TL; DR): LFENCE
сама по себе кажется бесполезной для упорядочения памяти, однако она не делает замену SFENCE
для MFENCE
. "Арифметическая" логика в вопросе не применима.
Вот выдержка из Руководство для разработчиков программного обеспечения Intel, том 3, раздел 8.2.2 (издание 325384-052US от сентября 2014 года), то же, что я использовал в другом ответе
- Чтения не переупорядочиваются с другими чтениями.
- Писания не переупорядочиваются с помощью более старых чтений.
- Запись в память не переупорядочивается с помощью других записей со следующими исключениями:
- записи выполняются с помощью команды CLFLUSH;
- потоковые хранилища (записи), выполняемые с инструкциями невременного перемещения (MOVNTI, MOVNTQ, MOVNTDQ, MOVNTPS и MOVNTPD); и
- строковые операции (см. раздел 8.2.4.1).
- Считывание может быть переупорядочено с помощью более старых записей в разных местах, но не с более старых записей в том же месте.
- Считывание или запись не могут быть переупорядочены с инструкциями ввода/вывода, заблокированными инструкциями или инструкциями по сериализации.
- Считывание не может пройти более ранние инструкции LFENCE и MFENCE.
- Писания не могут проходить более ранние инструкции LFENCE, SFENCE и MFENCE.
- Инструкции LFENCE не могут проходить более ранние чтения.
- Команды SFENCE не могут передавать более ранние записи.
- Инструкции MFENCE не могут проходить более ранние чтения или записи.
Отсюда следует, что:
-
MFENCE
- это полный забор памяти для всех операций во всех типах памяти, независимо от того, является ли он временным или нет.
-
SFENCE
только предотвращает переупорядочение записей (в другой терминологии, это бар StoreStore) и полезен только вместе с невременными хранилищами и другими инструкциями, перечисленными в качестве исключений.
-
LFENCE
предотвращает переупорядочение чтений с последующими чтениями и записью (т.е. сочетает блокировки LoadLoad и LoadStore). Тем не менее, первые две пули говорят, что барьеры LoadLoad и LoadStore всегда на месте, никаких исключений. Поэтому LFENCE
один бесполезен для упорядочения памяти.
Чтобы поддержать последнее утверждение, я просмотрел все места, где LFENCE
упоминается во всех трех томах руководства Intel, и не нашел ни одного, который мог бы сказать, что LFENCE
требуется для согласованности памяти. Даже MOVNTDQA
- единственная инструкция, не относящаяся к временной нагрузке, - упоминает MFENCE
, но не LFENCE
.
Является ли MFENCE
эквивалентом "суммы" других двух заборов или нет, это сложный вопрос. На первый взгляд, среди трех команд забора только MFENCE
предусмотрен барьер StoreLoad, т.е. Предотвращает переупорядочение чтений с более ранними записями. Однако для правильного ответа требуется знать больше, чем приведенные выше правила; а именно, важно, чтобы все инструкции забора были упорядочены относительно друг друга. Это делает последовательность SFENCE LFENCE
более мощной, чем просто объединение отдельных эффектов: эта последовательность также препятствует переупорядочению StoreLoad (поскольку нагрузки не могут пройти LFENCE
, которые не могут пройти SFENCE
, которые не могут пропустить магазины) и, таким образом, составляют полный (но также см. примечание (*) ниже). Обратите внимание, что этот порядок имеет значение здесь, а последовательность LFENCE SFENCE
не имеет такого синергетического эффекта.
Однако, хотя можно сказать, что MFENCE ~ SFENCE LFENCE
и LFENCE ~ NOP
, это не означает MFENCE ~ SFENCE
. Я намеренно использую эквивалентность (~), а не равенство (=), чтобы подчеркнуть, что арифметические правила здесь не применяются. Взаимный эффект SFENCE
, за которым следует LFENCE
, делает разницу; даже если нагрузки не переупорядочиваются друг с другом, LFENCE
требуется для предотвращения переупорядочения нагрузок с помощью SFENCE
.
(*) Можно еще сказать, что MFENCE
сильнее, чем комбинация двух других ограждений. В частности, в примечании к инструкции CLFLUSH
в томе 2 руководства Intel говорится, что "CLFLUSH
упорядочивается только командой MFENCE
. Он не гарантируется заказом каких-либо других инструкций по фехтованию или сериализации или другим CLFLUSH
инструкция.
Ответ 2
Рассмотрим следующий сценарий - это критический случай, когда выполнение спекулятивной нагрузки может теоретически повредить последовательную согласованность
изначально [x] = [y] = 0
CPU0: CPU1:
store [x]<--1 store [y]<--1
load r1<--[y] load r2<--[x]
Так как x86 позволяет переупорядочивать нагрузки с более ранними хранилищами на разные адреса, обе нагрузки могут возвращать 0. Добавление lfence только после того, как каждый магазин не помешает этому, поскольку они только предотвращают переупорядочение в том же контексте, но поскольку магазины отправляются после выхода на пенсию, вы можете иметь как lfences, так и обе нагрузки совершать, прежде чем магазины будут выполняться и наблюдаться.
С другой стороны, mfence заставит магазины выполнять, и только затем разрешить выполнение нагрузок, поэтому вы увидите обновленные данные по крайней мере в одном контексте.
Что касается sfences - как указано в комментарии, теоретически он недостаточно силен, чтобы предотвратить переупорядочение нагрузки над ним, поэтому он мог бы читать устаревшие данные. Хотя это верно в том, что касается официальных правил упорядочения памяти, я считаю, что текущая реализация x86 uarch делает ее немного сильнее (хотя я и не собираюсь делать это в будущем). Согласно это описание:
Из-за сильной модели упорядочения x86 буфер загрузки загружается путем когерентного трафика. Удаленный магазин должен аннулировать все остальные копии строки кэша. Если строка кэша считывается нагрузкой, а затем аннулируется удаленным хранилищем, загрузка должна быть отменена, так как она потенциально читать недопустимые данные. Модель памяти x86 не требует snooping буфера хранения.
Следовательно, любая нагрузка, еще не зафиксированная в машине, должна быть отслежена магазинами из других ядер, тем самым делая время эффективного отслеживания нагрузки на точке фиксации а не точкой выполнения (что действительно не соответствует порядку и может быть выполнено гораздо раньше). Commit выполняется по порядку, и, следовательно, нагрузка должна наблюдаться после предыдущих инструкций, что делает невозможным, поскольку я сказал выше в комментариях, так как согласованность может поддерживаться без них.
Это в основном предположение, пытаясь объяснить общую концепцию, что lfences не имеет смысла в x86 - я не совсем уверен, откуда она возникла, и если есть другие соображения - будет рад, если бы какой-либо эксперт одобрил эту теорию/бросил вызов.
Все вышеизложенное относится только к типам памяти WB, конечно
Ответ 3
Конечно, это имеет смысл!
LFENCE
из спецификации Intel:
Выполняет операцию сериализации во всех инструкциях с нагрузкой которые были выпущены до инструкции LFENCE. Эта сериализация гарантирует, что каждая инструкция по загрузке, которая предшествует программы, команда LFENCE отображается глобально инструкция нагрузки, которая следует за инструкцией LFENCE, глобально видны.
Инструкция записи памяти типа MOV
является атомарной, если она правильно выровнена. Но эта инструкция обычно выполняется в кэше ЦП и не будет отображаться в глобальном масштабе в настоящий момент для всех остальных потоков, поскольку сначала необходимо предварительно сформировать память LFENCE/SFENCE or MFENCE
.
Типичный случай:
Если писатель потока разблокирует область памяти с инструкцией записи, подобной памяти, выровненной MOV
, поэтому не используется инструкция префикса LOCK
, чем строка кэша, в которой была сформирована MOV
shuld, была видна в очень короткое будущее для всех другие потоки. LFENCE
обеспечить для считывателя потоков, что также все остальные строки кэша от писателя потока являются глобальными visibe!