std :: atomi c не включает ядро 1
Почти на всех обычных процессорах (типа, которые мы программируем в реальной жизни), барьер памяти инструкции являются непривилегированными и используются компилятором напрямую. Точно так же компиляторы знают, как генерировать такие команды, как x86 lock add [rdi], eax
для fetch_add
(или lock xadd
, если вы используете возвращаемое значение). Или на других ISA, буквально те же инструкции по барьеру, которые они используют до / после загрузки, хранения и RMW, чтобы дать требуемый порядок. https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/
На некоторых произвольное гипотетическое аппаратное обеспечение и / или компилятор, все, конечно, возможно, даже если это будет катастрофически плохо для производительности.
В asm, барьер просто заставляет это ядро ждать, пока некоторые предыдущие (программные) операции видны другим ядрам. Это чисто локальная операция. (По крайней мере, так устроены ЦП реального слова, так что последовательная согласованность может быть восстановлена только с локальными барьерами для управления локальным упорядочением операций загрузки и / или хранения. Все ядра имеют согласованное представление о кеше, поддерживаемое с помощью протокола, подобного MESI. Существуют несогласованные системы с разделяемой памятью, но реализации не запускают через них C ++ std :: thread и обычно не запускают ядро с одним образом системы.)
Сноска 1: (Даже атомы без блокировки обычно используют легкие блокировки).
Кроме того, ARM до ARMv7, по-видимому, не имел надлежащих инструкций барьера памяти . На ARMv6 G CC использует mcr p15, 0, r0, c7, c10, 5
в качестве барьера.
До этого (g++ -march=armv5
и ранее) G CC не знает, что делать, и вызывает __sync_synchronize
(вспомогательная функция libatomi c G CC), которая, мы надеемся, каким-то образом реализована для любой машины, на которой фактически выполняется код. Это может включать системный вызов в гипотетической многоядерной системе ARMv5, но более вероятно, что двоичный файл будет работать в системе ARMv7 или v8, где функция библиотеки может запускать dmb ish
. Или, если это одноядерная система, то, я думаю, это может быть неработоспособность. (Упорядочение памяти C ++ относится к другим потокам C ++, а не к порядку памяти, который видят возможные аппаратные устройства / DMA. Обычно реализации предполагают многоядерную систему, но эта библиотечная функция может быть в случае, когда может использоваться только одноядерная реализация. .)
На x86, например, std::atomic_thread_fence(std::memory_order_seq_cst)
компилируется в mfence
. Слабые барьеры, такие как std::atomic_thread_fence(std::memory_order_release)
, должны блокировать только переупорядочение во время компиляции; Аппаратная модель памяти времени выполнения x86 уже acq / rel (seq-cst + буфер хранения). Так что нет никаких asm-инструкций, соответствующих барьеру. (Одной из возможных реализаций библиотеки C ++ может быть GNU C asm("" ::: "memory");
, но в GCC / clang есть встроенные барьеры.)
std::atomic_signal_fence
только когда-либо блокирует переупорядочение во время компиляции , даже для слабо упорядоченных ISA, поскольку все реальные ISA гарантируют, что при выполнении в одном потоке видит свои собственные операции как происходящие в программном порядке. (Аппаратное обеспечение реализует это, загружая sn oop буфер хранилища текущего ядра). VLIW и IA-64 EPI C, или другие механизмы ISA явного параллелизма (например, Mill с его нагрузками с задержкой видимости), все еще позволяют компилятору генерировать код, который соблюдает любые гарантии упорядочения C ++, включая барьер, если асинхронный c Сигнал (или прерывание для кода ядра) поступает после любой инструкции.
Вы можете посмотреть на code-gen самостоятельно в проводнике компилятора Godbolt :
#include <atomic>
void barrier_sc(void) {
std::atomic_thread_fence(std::memory_order_seq_cst);
}
x86: mfence
.
МОЩНОСТЬ: sync
.
AArch64: dmb ish
(полный барьер на «внутреннем разделяемом» домене когерентности).
ARM с gcc -mcpu=cortex-a15
(или -march=armv7
): dmb ish
RIS C -V: fence iorw,iorw
void barrier_acq_rel(void) {
std::atomic_thread_fence(std::memory_order_acq_rel);
}
x86: ничего
POWER: lwsync
(облегченный син c).
AArch64: все еще dmb ish
ARM: все еще dmb ish
RIS C -V: все еще fence iorw,iorw
void barrier_acq(void) {
std::atomic_thread_fence(std::memory_order_acquire);
}
x86: ничего
МОЩНОСТЬ: lwsync
(легкий вес c).
AArch64: dmb ishld
(барьер загрузки, не нужно истощать буфер хранилища)
ARM: все еще dmb ish
, даже с -mcpu=cortex-a53
(ARMv8): /
RIS C -V: все еще fence iorw,iorw