繁体   English   中英

x86 上是否需要 std::memory_order_acquire 栅栏?

[英]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))); 但是您仍然需要volatileatomic<T>才能在循环中读取才能工作。


一般来说

使用std::atomic_thread_fence(std::memory_order_acquire); 根据 C++ 内存模型的要求; 这就是在编译时管理重新排序的原因。

编译x86时,不会变成任何asm指令; 在 asm 中,这是一个空操作。 但是如果你不告诉编译器它不能重新排序某些东西,你的代码可能会根据编译器优化级别而中断。

您可能很幸运,并让编译器在原子mo_relaxed加载之后执行非原子加载,或者如果您不告诉它不这样做,它可能会更早执行非原子加载。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM