В чем (небольшая) разница в расслабляющих атомных правилах?

После того, как Херб Саттерс отлично рассказал о "атомном оружии", я немного запутался в примерах " Расслабленной атомики".

Я взял с собой, что атом в модели памяти C++ (SC-DRF = последовательная согласованность для Data Race Free) "получает" при загрузке / чтении.

Я понимаю, что для загрузки [и магазина] по умолчанию std::memory_order_seq_cst и, следовательно, два одинаковы:

myatomic.load();                          // (1)
myatomic.load(std::memory_order_seq_cst); // (2)

Пока все хорошо, не задействовано Relaxed Atomics (и, услышав доклад, я никогда не буду использовать relaxed. Всегда. Обещание. Но когда кто-то спросит меня, мне, возможно, придется объяснить...).

Но почему это "расслабленная" семантика, когда я использую

myatomic.load(std::memory_order_acquire);   // (3)

Так как нагрузка приобретает, а не освобождает, почему это отличается от (1) а также (2)? Что на самом деле здесь расслаблено?

Единственное, о чем я могу думать, это то, что я неправильно понял, что нагрузка означает приобретение. И если это правда, и по умолчанию seq_cst означает и то, и другое, не означает ли это полный забор - ничто не может пройти ни по этой инструкции, ни вниз? Я должен был неправильно понять эту часть.

[и симметрично для хранения и выпуска ].

1 ответ

Решение

Это может быть немного запутанным, чтобы позвонить myatomic.load(std::memory_order_acquire); "расслабленная атомная" нагрузка, так как существует std::memory_order_relaxed

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

Он вступает в игру, когда вы имеете дело с более чем одной атомарной переменной: отдельные порядки модификации двух атомик могут появляться в разном относительном порядке по отношению к разным потокам, если только не установлена ​​последовательная согласованность.

Если вы "ослабите" некоторые требования к порядку следования seq_cst, вы увидите mo_acq_rel (и чистое приобретение, и чистое высвобождение).

Даже более расслабленно, чем это mo_relaxed; без заказа по. что-нибудь еще, просто атомарность1.

При компиляции для большинства ISA загрузка seq_cst может использовать тот же asm, что и загрузка загрузки; мы предпочитаем делать магазины дорогими, а не загруженными. Сопоставления C/C++11 с процессорами для ISA, включая x86, POWER, ARMv7, ARMv8 включают 2 альтернативы для некоторых ISA. Чтобы быть совместимыми друг с другом, компиляторы для одной и той же платформы должны выбирать одну и ту же стратегию, в противном случае хранилище seq_cst в одной функции может быть переупорядочено с загрузкой seq_cst в другой функции.

На типичном ЦП, где модель памяти включает буфер хранилища и согласованный кеш, если вы сохраняете и затем перезагружаете в одном потоке, seq_cst требует, чтобы вы не позволяли перезагрузке происходить до тех пор, пока хранилище не станет глобально видимым для всех потоков. Это означает либо полный барьер (включая StoreLoad) после сохранения seq_cst или до загрузки seq_cst. Поскольку дешевые грузы более ценны, чем дешевые магазины, обычное отображение выбирает x86mov + mfenceдля магазинов, например. (То же самое относится к загрузке любого другого местоположения; не может сделать это, пока магазин не зафиксирует. Но переадресация хранилища, позволяющая вам видеть свои собственные магазины до того, как они станут глобально видимыми, - это то, что происходит в книге Джеффа Прешинга переупорядочение памяти, пойманной в действии)

Это практический пример создания глобального общего порядка операций с различными переменными, с которым могут согласиться все потоки. (x86 asm предоставляет acq_rel для чистой загрузки / чистого хранилища или seq_cst дляlock-приставленные атомарные инструкции RMW. Таким образом, пример Preshing x86 asm точно соответствует C++11mo_release магазины вместо mo_seq_cst.


ARMv8 / AArch64 интересен: у него есть STLR (хранилище с последовательным выпуском) и LDAR (загрузка загрузки). Вместо остановки всех последующих загрузок до тех пор, пока буфер хранилища не истощится и не зафиксирует STLR в кеш L1d (глобальная видимость), реализация может быть более эффективной.

Ожидание сброса должно произойти только до выполнения LDAR; другие загрузки могут выполняться, и даже более поздние хранилища могут фиксироваться в L1d. (Последовательный выпуск по-прежнему является как минимум односторонним препятствием). Чтобы быть таким эффективным / слабым, LDAR должен исследовать буфер хранилища, чтобы проверить хранилища STLR. Но если ты сможешь это сделать,mo_seq_cst магазины могут быть значительно дешевле, чем на x86, если вы сразу после этого не выполните загрузку seq_cst чего-либо еще.

На большинстве других ISA единственный вариант восстановления последовательной согласованности - это полная барьерная инструкция (после сохранения). Это блокирует все последующие загрузки и сохранения до тех пор, пока все предыдущие хранилища не зафиксируются в кэше L1d. Но это не то, что ISO C++seq_cst подразумевает или требует, просто только AArch64 может быть настолько сильным, насколько требует ISO C++, но не сильнее.

(Компиляция для многих других слабо упорядоченных ISA должна продвигать acq / release к значительно более сильному, чем необходимо, например, ARMv7 нужен полный барьер для хранилищ релизов.)


Сноска 1: (Например, то, что вы получили в старом коде до C++11, используя атомики для самостоятельной прокрутки, используя volatile без преград).

И если это правда, и по умолчанию seq_cst означает оба, разве это не означает полный забор

Это абсолютно не означает и то, и другое или "полный забор", что бы это ни значило.

seq_cst подразумевает

  • приобретать только при загрузке
  • и выпускать только при работе магазина.

Таким образом, это подразумевает и то, и другое только для операций, которые объединяют оба: атомарные операции RMW.

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

Намерение seq_cstоперация над атомарным объектом не является "забором", который сделает все другие операции с памятью последовательными.

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