Какой барьер для записи лучше для x86: lock + addl или xchgl? - PullRequest
22 голосов
/ 20 ноября 2010

Ядро Linux использует lock; addl $0,0(%%esp) в качестве барьера записи, в то время как библиотека RE2 использует xchgl (%0),%0 в качестве барьера записи.В чем разница и что лучше?

Требуется ли для x86 инструкция по защите от чтения?RE2 определяет свою функцию барьера чтения как запрет на x86, в то время как Linux определяет ее как lfence или запрет, в зависимости от того, доступен ли SSE2.Когда lfence требуется?

Ответы [ 5 ]

10 голосов
/ 21 ноября 2010

Цитирование из руководств IA32 (Том 3А, Глава 8.2: Упорядочение памяти):

В однопроцессорной системе для областей памяти, определенных как кэшируемые с обратной записью, модель упорядочения памяти учитываетследующие принципы [..]

  • Чтения не переупорядочиваются с другими чтениями
  • Писания не переупорядочиваются с более старыми чтениями
  • Записи в память не переупорядочиваются с другимиоперации записи, за исключением
    • операций записи, выполняемых с помощью инструкции CLFLUSH
    • потоковых хранилищ (операций записи), выполняемых с помощью невременных команд перемещения ([список инструкций здесь])
    • строковые операции (см. Раздел 8.2.4.1)
  • Чтения могут быть переупорядочены со старыми записями в разные места, но не со старыми записями в то же место.
  • Чтения или записи не могут быть переупорядочены с помощью инструкций ввода / вывода, заблокированных инструкций или инструкций сериализации
  • Чтения не могут передавать LFENCE и MFENCE инструкции
  • WОбряды не могут проходить SFENCE и MFENCE инструкции

Примечание: приведенное выше «В однопроцессорной системе» немного вводит в заблуждение.Те же правила действуют для каждого (логического) процессора в отдельности;далее в руководстве описываются дополнительные правила упорядочения между несколькими процессорами.Единственное, что касается этого вопроса, это то, что

  • Заблокированные инструкции имеют общий порядок.

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

Кроме того, в памяти с обратной записью чтение никогда не переупорядочивается, поэтому нет необходимости в барьерах для чтения.Последние процессоры x86 имеют более слабую модель согласованности памяти для потоковых хранилищ и памяти с комбинированной записью (обычно используется для отображения графической памяти).Вот где различные fence инструкции вступают в игру;они не нужны для любого другого типа памяти, но некоторые драйверы в ядре Linux имеют дело с объединенной записью памятью, поэтому они просто определяют свой барьер чтения таким образом.Список упорядоченных моделей для каждого типа памяти приведен в разделе 11.3.1 в вып.3А руководств IA-32.Короткая версия: Write-Through, Write-Back и Write-Protected позволяют спекулятивное чтение (следуя правилам, описанным выше), Uncachable и Strong Uncacheable память имеет строгие гарантии упорядочения (нет переупорядочения процессора, считывания / записи выполняются немедленно, используются для MMIO) и запись Комбинированная память имеет слабое упорядочение (т. е. ослабленные правила упорядочения, которые требуют ограждений).

8 голосов
/ 21 ноября 2010

« lock; addl $ 0,0 (%% esp) » быстрее в случае, если мы тестируем переменную состояния блокировки 0 по адресу (%% esp). Потому что мы добавляем 0 значение для блокировки переменной и нулевой флаг устанавливается в 1, если значение блокировки переменной по адресу (%% esp) равно 0.


Защита по данным Intel:

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

( Примечание редактора: mfence или операция lock ed - единственное полезное ограждение (после магазина) для последовательной согласованности . lfence делает не блокирует переупорядочение StoreLoad буфером хранилища.)


Например: инструкция записи в память, такая как 'mov', является атомарной (им не нужен префикс блокировки), если они правильно выровнены. Но эта инструкция обычно выполняется в кэше ЦП и не будет видна в глобальном масштабе в данный момент для всех других потоков, поскольку сначала необходимо выполнить ограничение памяти, чтобы заставить этот поток ждать, пока предыдущие хранилища не станут видимыми для других потоков.


Таким образом, основное различие между этими двумя инструкциями заключается в том, что инструкция xchgl не будет влиять на условные флаги. Конечно, мы можем проверить состояние переменной блокировки с помощью инструкции lock cmpxchg , но это все же более сложно, чем с помощью инструкции lock add $ 0 .

7 голосов
/ 21 октября 2018

lock addl $0, (%esp) является заменой mfence, а не lfence.

. Вариант использования - когда вам нужно заблокировать переупорядочение StoreLoad (единственный вид, который позволяет модель сильной памяти x86), новам не нужна атомарная операция RMW с общей переменной.https://preshing.com/20120515/memory-reordering-caught-in-the-act/

например, предполагая, что выровнено std::atomic<int> a,b:

movl   $1, a             a = 1;    Atomic for aligned a
# barrier needed here
movl   b, %eax           tmp = b;  Atomic for aligned b

Возможны следующие варианты:

  • Создать хранилище последовательной согласованности с xchg, например mov $1, %eax / xchg %eax, a, поэтому вам не нужен отдельный барьер;это часть магазина.Я думаю, что это самый эффективный вариант на самом современном оборудовании;Компиляторы C ++ 11, кроме gcc, используют xchg для хранилищ seq_cst.
  • Используйте mfence в качестве барьера.(gcc использует mov + mfence для магазинов seq_cst).
  • Используйте lock addl $0, (%esp) в качестве барьера.Любая инструкция lock является полным барьером. Имеет ли блокировка xchg такое же поведение, как и у mfence?

    (Или в каком-то другом месте, но стек почти всегда приватный и горячий в L1d, так что это несколько хороший кандидат. Однако этоможет создать цепочку зависимостей для чего-либо, используя данные в нижней части стека.)

Вы можете использовать xchg в качестве барьера, только сложив его в хранилище, потому что оно безоговорочно записываетячейка памяти со значением, которое не зависит от старого значения.

Когда это возможно, лучше использовать xchg для хранилища seq-cst, даже если оно также считывает данные из общей папки.mfence медленнее, чем ожидалось, на последних процессорах Intel ( Является ли загрузка и сохранение единственными командами, которые переупорядочиваются? ), также блокируя неупорядоченное выполнение независимых инструкций без памяти lfence делает.

Возможно, даже стоит использовать lock addl $0, (%esp)/(%rsp) вместо mfence, даже когда mfence доступно, но я не экспериментировал с недостатками.Использование -64(%rsp) или чего-либо еще может снизить вероятность зависимости данных от чего-то горячего (локальный или обратный адрес), но это может сделать такие инструменты, как valgrind, несчастными.


lfence никогда не используется для упорядочения памяти, если вы не читаете из видеопамяти RAM (или некоторой другой слабо упорядоченной области WC) с загрузками MOVNTDQA.

Сериализация неупорядоченного выполнения (но неБуфер хранилища) бесполезен для остановки переупорядочения StoreLoad (единственный тип, который модель сильной памяти x86 допускает для нормальных областей памяти WB (с обратной записью)).

Реальные сценарии использования для lfenceпредназначены для блокирования выполнения не по порядку rdtsc для синхронизации очень коротких блоков кода или для ослабления Спектра путем блокирования спекуляций через условную или косвенную ветвь.

См. также Когда следует использовать_mm_sfence _mm_lfence и _mm_mfence (мой ответ и ответ @ BeeOnRope), чтобы узнать больше о том, почему lfence бесполезен, и когда использовать каждый из барьеров,ионов.(Или, по моему, встроенные функции C ++ при программировании на C ++ вместо asm).

6 голосов
/ 17 июня 2015

В дополнение к другим ответам разработчики HotSpot обнаружили, что lock; addl $0,0(%%esp) с нулевым смещением может быть неоптимальным, на некоторых процессорах он может вводить ложные зависимости данных ; связанные ошибка JDK .

Касание местоположения стека с другим смещением может улучшить производительность при некоторых обстоятельствах.

2 голосов
/ 20 ноября 2010

Важной частью lock; addl и xchgl является префикс lock. Это неявно для xchgl. Там действительно нет никакой разницы между ними. Я бы посмотрел, как они собираются, и выбрал тот, который короче (в байтах), так как обычно он быстрее для эквивалентных операций на x86 (отсюда и уловки типа xorl eax,eax)

Наличие SSE2, вероятно, является просто прокси для реального состояния, которое в конечном итоге является функцией cpuid. Вероятно, оказывается, что SSE2 подразумевает существование lfence, а доступность SSE2 проверялась / кэшировалась при загрузке. lfence требуется, когда доступно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...