__asm volatile ("pause" ::: "memory"); 是什么意思?做?
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
指令的帖子说线程有效地暗示核心释放资源并为其他线程提供更多资源(在 hyper 的上下文中-线程)。但是 :::
和 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)
。 (not atomic_thread_fence
; 尽管在 x86 上阻止编译时重新排序足以使其成为获取 + 释放栅栏,因为 x86 允许的唯一 运行-time 重新排序是 StoreLoad。)请参阅 Jeff Preshing 的 Memory Ordering at Compile Time 文章。
使 asm 指令部分非空也意味着那些 asm 指令将 运行 每次 C 逻辑上 运行 那个源代码行(因为它是 volatile
)。
pause
防止推测负载导致内存排序错误推测管道清除(又名机器核弹)。它在等待查看内存中值的自旋循环中很有用。
您可能会在非 C++11 std::atomic 编写的自旋循环中发现此语句,告诉编译器它必须重新读取全局变量的值. (因为 "memory"
破坏意味着编译器必须假定 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
的检查提升到循环之外并且 不会 每次都重新读取全局。
我正在查看具有以下代码结构的开源 C++ 项目:
while(true) {
// Do something work
if(some_condition_becomes_true)
break;
__asm volatile ("pause" ::: "memory");
}
最后一条语句是做什么的?我知道 __asm
意味着它是一个汇编指令,我发现一些关于 pause
指令的帖子说线程有效地暗示核心释放资源并为其他线程提供更多资源(在 hyper 的上下文中-线程)。但是 :::
和 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)
。 (not atomic_thread_fence
; 尽管在 x86 上阻止编译时重新排序足以使其成为获取 + 释放栅栏,因为 x86 允许的唯一 运行-time 重新排序是 StoreLoad。)请参阅 Jeff Preshing 的 Memory Ordering at Compile Time 文章。
使 asm 指令部分非空也意味着那些 asm 指令将 运行 每次 C 逻辑上 运行 那个源代码行(因为它是 volatile
)。
pause
防止推测负载导致内存排序错误推测管道清除(又名机器核弹)。它在等待查看内存中值的自旋循环中很有用。
您可能会在非 C++11 std::atomic 编写的自旋循环中发现此语句,告诉编译器它必须重新读取全局变量的值. (因为 "memory"
破坏意味着编译器必须假定 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
的检查提升到循环之外并且 不会 每次都重新读取全局。