Который является лучшим барьерным барьером на X86: LOCK + ADDL или XCHGL?

StackOverflow https://stackoverflow.com/questions/4232660

  •  26-09-2019
  •  | 
  •  

Вопрос

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

Х86 также требуется инструкции по чтению барьера? RE2 определяет свою функцию чтения барьера как NO-OP на X86, а Linux определяет его как lfence или NO-OP в зависимости от того, доступно ли SSE2. Когда lfence обязательный?

Это было полезно?

Решение

"замок; ADDL $ 0,0 (%% ESP)«Быт быстрее в том случае, если мы проверяем 0-го состояния блокировки в адресе (%% ESP) в (%% ESP). Поскольку мы добавляем 0 значение для блокировки переменной, а нулевой флаг устанавливается на 1, если значение блокировки переменной по адресу (%% ESP ) 0.


невыполненность Из таблиц Intel:

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

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


Например: инструкция по записи памяти, как «MOV», являются атомными (им не нуждается в префиксе блокировки), если есть правильно выровнено. Но эта инструкция обычно выполняется в CPU Cache и не будет в глобальном масштабе в этот момент для всех других потоков, потому что забор памяти должен быть выполнен в первую очередь, чтобы сделать эту нить, пока предыдущие магазины не будут видны для других потоков.


Таким образом, главное различие между этими двумя инструкциями заключается в том, что xchgl. Инструкция не будет иметь никакого влияния на условные флаги. Конечно, мы можем проверить переменное состояние блокировки с Замок CMPXCHG инструкция, но это все еще сложное, чем с Блокировка Добавить $ 0 Инструкция.

Другие советы

Цитата из руководств IA32 (VOL 3A, глава 8.2: Заказ на память):

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

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

ПРИМЕЧАНИЕ. На вышеупомянутой системе «в системе« в однопроцессорной системе »слегка вводит в заблуждение. Одни и те же правила удерживаются для каждого (логического) процессора индивидуально; Затем руководство продолжается, чтобы описать дополнительные правила заказа между несколькими процессорами. Единственный бит об этом, относящийся к вопросу, в том, что

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

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

Кроме того, в обратной памяти записи чтения никогда не переупорядочены, поэтому нет необходимости читать барьеры. Процессоры X86 имеют более слабую модель согласованности памяти для потоковых магазинов и комбинированной памяти (обычно используемые для памяти графики сопоставления). Вот где разные fence инструкции приходят в игру; Они не нужны для любого другого типа памяти, но некоторые драйверы в ядре Linux имеют дело с комбинированной памятью для записи, поэтому они только что определили свой прочитанный барьер. Список модели заказа на тип памяти находится в разделе 11.3.1 в Vol. 3А руководств IA-32. КОРОТКАЯ ВЕРСИЯ: Запись, защищенная запись и запись, защищенная для записи. Разрешить спекулятивные чтения (после получения правил, как описано выше), бесцеремонтируемая и сильная бесценарная память имеет сильные гарантии заказа (без переупорядочения процессора, сразу же выполняется, используемые для MMIO ) и запись комбинированной памяти имеет слабое упорядочение (то есть расслабленные правила упорядочения, которые нуждаются в заборах).

lock addl $0, (%esp) это заменитель для mfence, нет lfence.

Корпус использования - это когда вам необходимо заблокировать переупорядочение хранилища (единственный вид, который позволяет использовать сильную модель памяти X86), но вам не нужна операция Atomic RMW в общей переменной. https://preshing.com/20120515/memory-reording-comat-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 имеет то же поведение, что и MFENGE?

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

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

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

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


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

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

Реальные случаи использования для lfence для блокировки выполнения вне заказа rdtsc для времени очень короткие блоки кода или для смягчения эффекта, блокируя спекуляции через условную или непрямую ветку.

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top