我正在研究一个开源C ++项目,该项目具有以下代码结构:
while(true) {
// Do something work
if(some_condition_becomes_true)
break;
__asm volatile ("pause" ::: "memory");
}
最后的陈述是做什么的?我理解__asm
意味着它是一个汇编指令,我发现了一些关于pause
指令的帖子,说明该线程有效地提示核心释放资源并为其他线程提供更多资源(在超级环境中) -threading)。但是:::
做了什么以及memory
做了什么?
答案 0 :(得分:5)
它是Project -> Compile Options
并且编译内存屏障包含在一个GNU C Extended ASM语句中。 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
_mm_pause()
阻止编译时重新排序内存操作,如C ++ 11 asm("" ::: "memory")
。 ( not std::atomic_signal_fence(std::memory_order_seq_cst)
;虽然在x86上阻止编译时重新排序足以使其成为获取+释放栏,因为x86允许的唯一运行时重新排序是StoreLoad。)请参阅Jeff Preshing的Memory Ordering at Compile Time文章。
使asm指令部分非空也意味着每次C逻辑运行该源代码行时都会运行asm指令(因为它是atomic_thread_fence
)。
volatile
可防止推测性负载导致内存排序错误推测管道清除(又称机器核武器)。它在等待在内存中查看值的自旋循环中很有用。
您可能会在没有C ++ 11 std :: atomic的情况下在spinloop中找到此语句,告诉编译器必须重新读取全局变量的值。 (因为pause
clobber意味着编译器必须假设asm语句可能修改了任何全局可访问内存的值。)
这看起来就像您找到它的上下文:"memory"
可能包括阅读非some_condition_becomes_true
/非 - atomic
全局。
C ++ 11相当于你的循环:
volatile
(不完全等效,因为你的版本有一个完整的编译器障碍,而我的版本只有一个seq-cst加载,所以它不是一个完整的信号栅栏。但可能不需要的东西,他们只是使用了一些强大的东西来获得挥发性的效果。
没有障碍或使#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();
}
}
原子,编译器会将其优化为:
flag
即。 它会将// Do something work
if(some_condition_becomes_true) {
// empty
} else {
while(true) {
// Do something work
__asm volatile ("pause" ::: ); // no memory clobber
}
}
的检查提升到循环之外,并且不每次重新读取全局。