[英]What does __asm volatile (“pause” ::: “memory”); do?
我正在看一个开源C ++项目,它具有以下代码结构:
while(true) {
// Do something work
if(some_condition_becomes_true)
break;
__asm volatile ("pause" ::: "memory");
}
最后的陈述是做什么的? 我知道__asm
意味着它是一个汇编指令,我发现了一些关于pause
指令的帖子,这些帖子说该线程有效地提示核心释放资源并为其他线程提供更多资源(在超线程的上下文中)。 但是:::
做什么以及memory
做什么?
它是_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。)请参阅Jeff Preshing 在编译时的内存排序文章。
使asm指令部分非空也意味着每次C逻辑运行该源代码行时都会运行asm指令(因为它是volatile
)。
pause
可防止推测性负载导致内存排序错误推测管道清除(又称机器核武器)。 它在等待在内存中查看值的自旋循环中很有用。
您可能会在没有C ++ 11 std :: atomic的情况下在spinloop中找到此语句,以告诉编译器它必须重新读取全局变量的值 。 (因为"memory"
clobber意味着编译器必须假设asm语句可能已经修改了任何全局可达内存的值。)
这看起来就像您找到它的上下文: some_condition_becomes_true
可能包括读取非atomic
/非易失volatile
全局。
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加载,所以它不是一个完整的信号栅栏。但可能是不需要的,他们只是使用了比获得更强的东西挥发的影响)。
如果没有屏障或使flag
成为原子 ,编译器会将其优化为:
// 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
上的检查提升到循环之外,而不是每次都重新读取全局。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.