Это _mm_pause()
и барьер памяти компиляции, заключенный в один оператор GNU C Extended ASM.https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
asm("" ::: "memory")
предотвращает переупорядочение операций памяти во время компиляции, например, C ++ 11 std::atomic_signal_fence(std::memory_order_seq_cst)
.( не atomic_thread_fence
; хотя на x86 предотвращение переупорядочения во время компиляции достаточно для того, чтобы сделать это забором для получения + освобождения, потому что единственное переупорядочение во время выполнения, которое допускает x86, это StoreLoad.) См. Джеффа ПрешингаУпорядочение памяти во время компиляции article.
Если часть инструкции asm не пуста, это также означает, что эти инструкции asm будут выполняться каждый раз, когда C логически запускает эту строку источника (потому что это volatile
).
pause
предотвращает спекулятивные нагрузки от очистки памяти при неправильной спекуляции при упорядочении памяти (так называемые ядерные системы).Это полезно внутри циклов вращения, которые ждут, чтобы увидеть значение в памяти.
Вы можете найти это утверждение внутри цикла вращения, написанного без C ++ 11 std :: atomic, чтобы сказать компилятору, что он долженперечитайте значение глобальной переменной .(Поскольку "memory"
clobber означает, что компилятор должен предположить, что оператор asm мог изменить значение любой глобально достижимой памяти.)
Это похоже на контекст, в котором вы его нашли: some_condition_becomes_true
, вероятно, включает в себячтение не atomic
/ non- volatile
global.
Эквивалент C ++ 11 вашего цикла:
#include <atomic>
#include <immintrin.h>
std::atomic<int> flag;
void wait_for_flag(void) {
while(flag.load(std::memory_order_seq_cst == 0) {
_mm_pause();
}
}
(Не совсем эквивалентно, потому что ваша версия имеетполный барьер компилятора, в то время как у меня есть только seq-cst нагрузка, так что это не полный ограждение сигнала. Но вероятно , что не нужно, и они просто использовали что-то более сильное, чем необходимо, чтобы получить эффект volatile).
Без барьера или создания flag
atomic компилятор оптимизировал бы его до:
// Do something work
if(some_condition_becomes_true) {
// empty
} else {
while(true) {
// Do something work
__asm volatile ("pause" ::: ); // no memory clobber
}
}
, то есть это было быподнимите чек на some_condition_becomes_true
из цикла и не каждый раз перечитывайте глобальное значение.