![](/img/trans.png)
[英]Does the semantics of `std::memory_order_acquire` requires processor instructions on x86/x86_64?
[英]std::memory_order_acquire fence necessary on x86?
鉴于 x86 具有强大的内存模型,是否需要std::memory_order_acquire
栅栏( 不是操作)?
例如,如果我有这个代码:
uint32_t read_shm(const uint64_t offset) {
// m_head_memory_location is char* pointing to the beginning of a mmap-ed named shared memory segment
// a different process on different core will write to it.
return *(uint32_t*)(m_head_memory_location + offset);
}
....
int main() {
uint32_t val = 0;
while (0 != (val = shm.read(some location)));
.... // use val
}
在 return 语句之前我真的需要std::atomic_thread_fence(std::memory_order_acquire)
吗?
我觉得没有必要,因为上面代码的目标是尝试从m_head_memory_location + offset
读取前 4 个字节,因此栅栏重新排序后的任何内存操作都不会影响结果。
或者有一些副作用使获取栅栏变得必要?
在 x86 上是否需要获取栅栏(不是操作)?
欢迎任何意见。
return *(uint32_t*)(m_head_memory_location + offset);
您转换为非atomic
非易失volatile
uint32_t*
并取消引用!!!
允许编译器假设这个uint32_t
对象不是由其他任何东西写入的(即假设没有数据竞争 UB),因此它可以并将负载提升到循环之外,有效地将其转换为类似if((val=load) == 0) infinite_loop();
.
GCC 内存屏障将强制重新加载,但这是std::atomic_thread_fence(std::memory_order_acquire)
的实现细节。 对于 x86,该屏障只需要阻止编译时重新排序,因此 GCC 的典型实现可能是asm("" ::: "memory")
。
执行任何操作的不是获取排序,而是阻止 GCC 假设另一个读取将读取相同内容的内存破坏。 这不是 ISO C++ std::atomic_thread_fence(std::memory_order_acquire)
对非原子变量暗示的东西。 (而且它总是暗示原子和易失性)。 所以就像我说的,这可以在 GCC 中工作,但只能作为一个实现细节。
如果此内存曾被除此char*
以外的其他类型访问,或者如果底层内存被声明为char[]
数组,则它也是严格别名 UB。 如果你从mmap
或其他东西得到一个char*
,那你就没事了。
除非已知offset
是 4 的倍数,否则 UB 也可能未对齐。(尽管除非 GCC 选择 auto-vectorize ,但这在 x86 上实际上不会咬你。)
您可以使用typedef uint32_t unaligned_u32 __attribute((may_alias, aligned(1)));
为 GNU C 解决这两个问题typedef uint32_t unaligned_u32 __attribute((may_alias, aligned(1)));
但是您仍然需要volatile
或atomic<T>
才能在循环中读取才能工作。
使用std::atomic_thread_fence(std::memory_order_acquire);
根据 C++ 内存模型的要求; 这就是在编译时管理重新排序的原因。
编译x86时,不会变成任何asm指令; 在 asm 中,这是一个空操作。 但是如果你不告诉编译器它不能重新排序某些东西,你的代码可能会根据编译器优化级别而中断。
您可能很幸运,并让编译器在原子mo_relaxed
加载之后执行非原子加载,或者如果您不告诉它不这样做,它可能会更早执行非原子加载。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.