Реализация std :: atomic_thread_fence (std :: memory_order_seq_cst) на x86 без дополнительных потерь производительности

Следующий вопрос для Почему работает этот `std :: atomic_thread_fence`

Поскольку фиктивная блокируемая операция лучше, чем _mm_mfence, и есть довольно много способов ее реализовать, какую блокированную операцию и для каких данных следует использовать?

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


person Alex Guteniev    schedule 12.06.2020    source источник


Ответы (2)


Краткий ответ, не вдаваясь в подробности почему. См., В частности, обсуждение в комментариях этого связанного вопроса.

lock orb $0, -1(%rsp), вероятно, хорошая ставка, чтобы избежать удлинения цепочек зависимостей для локальных варов, которые проливаются / перезагружаются. См. https://shipilev.net/blog/2014/on-the-fence-with-dependencies для тестов. В Windows x64 (без красной зоны) это пространство не должно использоваться, кроме как для будущих инструкций вызова или push.

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

mfence - это довольно дерьмо по сравнению с горячей линией пространства стека даже на Haswell, возможно, хуже на Skylake, где он даже блокирует OoO exec. (И тоже плохо на AMD по сравнению с lock add).

person Peter Cordes    schedule 12.06.2020
comment
Полезно ли вообще сохранять переменную стека? Если не так уж и много, возможно, lock not упомянутый в комментарии по связям с общественностью MSVC является путь? - person Alex Guteniev; 12.06.2020
comment
Еще одно направление, которое следует учитывать, - это то, что успешно зафиксированная область RTM, состоящая из XBEGIN, за которым следует XEND, даже без операций с памятью в области RTM, имеет ту же семантику упорядочения, что и инструкция с префиксом LOCK. - см. ответ на stackoverflow.com/questions/61336070/, но, конечно, только тогда, когда TSX снова станет отличным - person Alex Guteniev; 12.06.2020
comment
@AlexGuteniev: резервирование пространства стека для переменной, которая больше не используется, позволит избежать удлинения цепочек зависимостей, переносимых по циклу, с участием других варов. Я предполагал, что вы этого не делаете, потому что это в основном обходной путь для недостаточно умного компилятора. Также вы спросили о встроенном asm. Встроенные asm-операторы GNU C неявно затирают "cc", поэтому избегать затирания FLAGS бесполезно. Интересный момент, если вы считаете, что компилятор генерирует его изначально. - person Peter Cordes; 12.06.2020
comment
@AlexGuteniev: Интересный момент о пустой транзакции TSX. IDK, если что будет быстрее. И да, это будет работать только для некоторых целевых процессоров. - person Peter Cordes; 12.06.2020
comment
Я не знал, что для gcc asm еще нельзя сказать, что он не затирает flags. (Я смотрю на будущее потенциальное портирование моей программы в Linux или компиляцию ее с помощью clang под Windows, но у меня нет большого опыта работы с gcc asm, поскольку у msvc его нет) - person Alex Guteniev; 12.06.2020
comment
@AlexGuteniev: Я не думаю, что вы действительно хотели бы использовать встроенный asm для этого, если только разница в производительности не была действительно значительной. В долгосрочной перспективе вы просто измените внутренний рецепт компилятора x86 / x86-64 на thread_fence, который, я думаю, представляет собой жестко запрограммированную строку asm, которую он просто генерирует. Но во внутреннем устройстве GCC, а не во встроенном asm, я думаю, вы могли бы осмысленно опустить "cc" clobber. IDK, вероятно, нет реальных недостатков в использовании вашего собственного atomic_thread_fence со встроенным asm; GCC недостаточно умен, чтобы в любом случае оптимизировать избыточный mfence, например, seq_cst магазин + thread_fence спина к спине - person Peter Cordes; 12.06.2020
comment
Я добавил свой собственный ответ, в основном, чтобы обсудить lock not [esp-1] в Windows, и намеренное дополнительное хранилище фиктивной переменной, чтобы извлечь выгоду из переадресации хранилища. - person Alex Guteniev; 13.06.2020
comment
Теперь, когда я вижу, что ваш lock orb $0, -1(%rsp) идеален как неконтекстное неизменяемое решение, вы вернетесь и предложите его gcc? - person Alex Guteniev; 13.06.2020
comment
@AlexGuteniev: Думаю, они исключили это из-за боли в инструментах анализа кода; IDK, если небольшое улучшение производительности может перевесить это; возможно нет. - person Peter Cordes; 13.06.2020
comment
Сопровождающий Boost.Atomic заметил, что gcc больше не генерирует mov+mfence` для seq cst store (ссылка на комментарий кода). Может быть, они тоже захотят пересмотреть забор потока, поскольку mfence становится только хуже. И если этот инструмент анализа связан с каким-то псевдонимом переменных, тогда может быть вариант с дополнительными переменными. - person Alex Guteniev; 13.06.2020

При переходе по маршруту взаимосвязанной работы в фиктивном местоположении необходимо учитывать несколько моментов:

  1. Находясь в L1d этого ядра,
  2. Не используется другими ядрами
  3. Не создавать длинные цепочки зависимостей
  4. Избегайте задержек из-за пропуска переадресации магазина

Без контекста все является лишь предположением, поэтому цель состоит в том, чтобы сделать наилучшее предположение.

Место в верхней части стопки - хорошее предположение для 1 и 2.

Преднамеренно назначенная переменная стека, скорее всего, исправит 3, а поскольку других хранилищ в полете нет, 4 не проблема. Лучшая операция выглядит как lock not.

Для того, чтобы не выделять переменную стека, необходимо, чтобы операция не выполнялась, поэтому lock or [mem], 0 - хороший вариант. Операнд должен быть байтовым, чтобы избежать проблем с 4. Для 3 это всегда предположение. (Хотя можно было использовать обратный адрес, сборка без контекста не знает этого. Но MSVC _AddressOfReturnAddress может быть хорошей идеей)

Я читал про красную зону. Отсутствие его в Windows дает возможность дополнительной оптимизации.

lock not byte ptr [esp-1] без дополнительной переменной подходит для Windows, так как данные считаются непостоянными и не должны использоваться. Нет проливных регистров, поэтому нет зависимости от ложных данных.

ABI с 128-байтовой красной зоной исключает использование lock not byte ptr [esp-1]. 128 байтов вне стека, вероятно, не будут L1d. Тем не менее, поскольку красная зона вряд ли будет использоваться в качестве обычного стека, ответ, данный @Peter Cordes, выглядит хорошо.

TSX в первую очередь вызывает сомнения из-за его отсутствия (не поддерживается на данном ЦП или отключен в результате исправления ошибок или снижения безопасности). В обозримом будущем будет существовать только RTM (Исчезла ли Hardware Lock Elision навсегда из-за Spectre Mitigation?). Согласно Обзор RTM, пустая транзакция RTM все еще является забором, поэтому ее можно использовать.

Успешно зафиксированная область RTM, состоящая из XBEGIN, за которым следует XEND, даже без операций с памятью в области RTM, имеет ту же семантику упорядочения, что и инструкция с префиксом LOCK.

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

if (rtm_supported && _xbegin() == 0xFFFFFFFF)
  _xend();
else
  dummy_interlocked_op();
person Alex Guteniev    schedule 13.06.2020
comment
Вы забываете о стоимости в инструкции / упс. Дополнительный магазин кажется чистым недостатком. Если для этого байта есть какое-либо оперативное сохранение, он может уже выполнить сохранение до части спекулятивной ранней загрузки lock not byte ptr [esp-1]. Современные процессоры могут пересылать из широкого хранилища на байтовую перезагрузку любого из своих байтов. blog.stuffedcow.net/2014/01/x86-memory-disambiguation. Но в большинстве случаев этого не будет. Только что ранняя загрузка в L1d так же хороша, как и перенаправление магазина; добавление еще одного магазина для слива - это явный недостаток. - person Peter Cordes; 13.06.2020
comment
TL: DR: byte размер операнда уже гарантирует, что киоски переадресации магазина не будут проблемой из-за того, что могло быть в полете заранее. Это не относится к более широкому размеру операнда, но даже в этом случае резервирование вашей собственной переменной означало бы, что любая проблема не может возникнуть в жестком цикле. - person Peter Cordes; 13.06.2020
comment
Отредактировано. Теперь выглядит лучше? - person Alex Guteniev; 13.06.2020
comment
Вы пропустили один случай предложения mov магазина; Я исправил это для вас. Теперь да, так лучше. - person Peter Cordes; 13.06.2020