Делает ли смысл какой-либо смысл 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!