其他分享
首页 > 其他分享> > c – __asm volatile是什么(“pause”:::“memory”);做?

c – __asm volatile是什么(“pause”:::“memory”);做?

作者:互联网

我正在看一个开源C项目,它具有以下代码结构:

while(true) {
  // Do something work

  if(some_condition_becomes_true)
     break;

  __asm volatile ("pause" ::: "memory");
}

最后的陈述是做什么的?我知道__asm意味着它是一个汇编指令,我发现了一些关于暂停指令的帖子,这些帖子说该线程有效地提示核心释放资源并给予其他线程更多资源(在超线程的上下文中).但是:::做什么以及记忆做什么?

解决方法:

它是_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的Memory Ordering at Compile Time文章.

使asm指令部分非空也意味着每次C逻辑运行该源代码行时都会运行asm指令(因为它是易失性的).

暂停可防止推测性负载导致内存排序错误推测管道清除(又称机器核武器).它在等待在内存中查看值的自旋循环中很有用.

您可能会在没有C 11 std :: atomic的情况下在spinloop中找到此语句,以告诉编译器它必须重新读取全局变量的值. (因为“memory”clobber意味着编译器必须假设asm语句可能已经修改了任何全局可达内存的值.)

这看起来就像您找到它的上下文:some_condition_becomes_true可能包括读取非原子/非易失性全局.

你的循环的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加载,所以它不是一个完整的信号栅栏.但可能是不需要的,他们只是使用了比获得更强的东西挥发的影响).

如果没有屏障或使标志成为原子,编译器会将其优化为:

// 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的检查提升到循环之外,并且不会每次重新读取全局.

标签:c,x86,assembly,parallel-processing,inline-assembly
来源: https://codeday.me/bug/20190828/1747356.html