Joh*_*ine 4 c++ parallel-processing x86 assembly inline-assembly
我正在看一个开源C++项目,它具有以下代码结构:
while(true) {
// Do something work
if(some_condition_becomes_true)
break;
__asm volatile ("pause" ::: "memory");
}
Run Code Online (Sandbox Code Playgroud)
最后的陈述是做什么的?我理解这__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();
}
}
Run Code Online (Sandbox Code Playgroud)
(不完全相同,因为你的版本有一个完整的编译器屏障,而我的只有一个seq-cst加载,所以它不是一个完整的信号栅栏.但可能是不需要的,他们只是使用了比获得更强的东西挥发的影响).
没有障碍或制造flag原子,编译器会将其优化为:
// Do something work
if(some_condition_becomes_true) {
// empty
} else {
while(true) {
// Do something work
__asm volatile ("pause" ::: ); // no memory clobber
}
}
Run Code Online (Sandbox Code Playgroud)
即它会将检查提升到some_condition_becomes_true循环之外,而不是每次都重新读取全局.