Имеет ли смысл использовать инструкцию LFENCE на процессорах x86/x86_64?

Часто в интернете нахожу, что LFENCE не имеет смысла в процессорах x86, т.е. ничего не делает, поэтому вместо MFENCE мы можем абсолютно безболезненно использовать SFENCE, так как MFENCE знак равно SFENCE + LFENCE знак равно SFENCE + NOP знак равно SFENCE,

Но если LFENCE не имеет смысла, тогда почему у нас есть четыре подхода для создания последовательной согласованности в x86 / x86_64:

  1. LOAD (без забора) и STORE + MFENCE
  2. LOAD (без забора) и LOCK XCHG
  3. MFENCE + LOAD а также STORE (без забора)
  4. LOCK XADD (0) и STORE (без забора)

Взято отсюда: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

А также выступления Херба Саттера на странице 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.

3 ответа

Решение

Итог (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 упоминается во всех 3 томах руководства Intel, и не нашел ни одного, который сказал бы, что LFENCE требуется для согласованности памяти. Четное MOVNTDQA - единственная пока не временная инструкция по загрузке - упоминает MFENCE но нет LFENCE,


Обновление: см. Ответы на вопрос: почему (или нет?) SFENCE + LFENCE эквивалентны MFENCE? для правильных ответов на догадки ниже

Будь то MFENCE эквивалентно "сумме" двух других заборов или нет, это сложный вопрос. На первый взгляд, только из трех инструкций по забору MFENCE обеспечивает барьер StoreLoad, то есть предотвращает изменение порядка чтения с более ранними записями. Однако правильный ответ требует знать больше, чем приведенные выше правила; а именно, важно, чтобы все инструкции по забору были упорядочены относительно друг друга. Это делает SFENCE LFENCE последовательность более мощная, чем простое объединение отдельных эффектов: эта последовательность также предотвращает переупорядочение StoreLoad (потому что загрузка не может пройти LFENCE, который не может пройти SFENCE, который не может пройти через хранилища), и, таким образом, представляет собой полный забор памяти (но также см. примечание (*) ниже). Обратите внимание, что порядок имеет значение здесь, и LFENCE SFENCE Последовательность не имеет такого же синергетического эффекта.

Однако пока можно сказать, что MFENCE ~ SFENCE LFENCE а также LFENCE ~ NOP, это не значит MFENCE ~ SFENCE, Я намеренно использую эквивалентность (~), а не равенство (=), чтобы подчеркнуть, что арифметические правила здесь не применяются. Взаимное влияние SFENCE с последующим LFENCE имеет значение; даже если грузы не переупорядочены друг с другом, LFENCE требуется для предотвращения переупорядочения грузов с SFENCE,

(*) Было бы правильно сказать, что MFENCE сильнее, чем комбинация двух других заборов. В частности, примечание к CLFLUSH Инструкция во втором томе руководства Intel гласит, что CLFLUSH заказывается только MFENCE инструкция. Не гарантируется, что он будет заказан другими инструкциями по фехтованию или сериализации или другим CLFLUSH инструкция ".

(Обновить, clflush теперь определяется как строго упорядоченный (как обычный магазин, так что вам нужно только mfence если вы хотите заблокировать более поздние загрузки), но clflushopt слабо упорядочен, но может быть огорожен sfence.)

Рассмотрим следующий сценарий - это критический случай, когда выполнение спекулятивной нагрузки теоретически может нанести вред последовательной согласованности.

изначально [x]=[y]=0

CPU0:                              CPU1: 
store [x]<--1                      store [y]<--1
load  r1<--[y]                     load r2<--[x]

Поскольку x86 позволяет переупорядочивать нагрузки с более ранними хранилищами на разные адреса, обе загрузки могут возвращать нули. Добавление одного lfence после каждого хранилища не помешает этому, поскольку они предотвращают переупорядочение только в одном и том же контексте, но, поскольку хранилища отправляются после удаления, вы можете иметь как lfences, так и обе нагрузки перед тем, как хранилища будут выполнены и просмотрены.

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

Что касается защиты - как указано в комментарии, теоретически она недостаточно сильна, чтобы предотвратить переупорядочение нагрузки над ней, поэтому она все равно может прочитать устаревшие данные. Хотя это верно в том, что касается официальных правил упорядочения памяти, я считаю, что текущая реализация x86 uarch делает его немного сильнее (я полагаю, не делая этого в будущем). Согласно этому описанию:

Из-за строгой модели упорядочения x86 буфер загрузки отслеживается трафиком когерентности. Удаленное хранилище должно сделать недействительными все остальные копии строки кэша. Если строка кэша считывается загрузкой, а затем становится недействительной удаленным хранилищем, загрузка должна быть отменена, поскольку она потенциально может считывать недействительные данные. Модель памяти x86 не требует отслеживания буфера хранилища.

Следовательно, любая нагрузка, еще не зафиксированная в машине, должна отслеживаться хранилищами из других ядер, тем самым делая эффективное время наблюдения нагрузки в точке фиксации, а не в точке выполнения (которая действительно вышла из строя и могла быть выполнена). намного раньше). Фиксация выполняется по порядку, и, следовательно, нагрузка должна соблюдаться после предыдущих инструкций, что делает бесполезными ограничения, как я уже говорил выше в комментариях, поскольку согласованность может поддерживаться таким же образом без них. Это в основном спекуляция, попытка объяснить общую концепцию, согласно которой в x86 смыслы бессмысленны - я не совсем уверен, откуда они возникли и есть ли другие соображения под рукой - был бы рад любому эксперту одобрить / оспорить эту теорию.

Все вышеперечисленное относится только к типам WB mem.

Конечно, это имеет смысл!

LFENCE из таблицы данных Intel:

Выполняет операцию сериализации для всех инструкций загрузки из памяти, которые были выполнены до инструкции LFENCE. Эта операция сериализации гарантирует, что каждая команда загрузки, которая предшествует в программном порядке команде LFENCE, будет видна глобально, прежде чем любая команда загрузки, которая следует за инструкцией LFENCE, будет видна глобально.

Инструкция по записи в память типа MOV являются атомными, если они правильно выровнены. Но эта инструкция обычно выполняется в кэше ЦП и не будет видна глобально в данный момент для всех других потоков, потому что память LFENCE/SFENCE or MFENCE должен быть предварительно сформирован.

Типичный случай:

Если средство записи потоков разблокирует область памяти с помощью инструкции записи, подобной выравниванию памяти MOVтак что нет LOCK инструкция префикса используется, чем строка кэша, где была выполнена MOV Должен быть виден в очень коротком будущем всем другим темам. LFENCE убедитесь в том, что читателю потока все остальные строки кэша от средства записи потока доступны глобально!

Другие вопросы по тегам