【发布时间】:2016-11-29 12:54:05
【问题描述】:
代码库有一个COMPILER_BARRIER 宏,定义为__asm__ volatile("" ::: "memory")。宏的目的是防止编译器重新排序跨屏障的读取和写入。请注意,这是明确的编译器屏障,不是处理器级别的内存屏障。
事实上,这是相当可移植的,因为 AssemblerTemplate 中没有实际的汇编指令,只有 volatile 和 memory clobber。因此,只要编译器支持 GCC 的扩展 Asm 语法,它就可以正常工作。不过,如果可能的话,我很好奇在 C++11 原子 API 中表达这一点的正确方法是什么。
以下似乎可能是正确的想法:atomic_signal_fence(memory_order_acq_rel);。
我的理由是:
- 在
<atomic>API 中,只有atomic_signal_fence和atomic_thread_fence不需要内存地址来操作。 -
atomic_thread_fence影响内存排序,我们不需要编译器屏障。 - Extended Asm 版本中的
memoryclobber 不区分读取和写入,因此看起来我们需要获取和释放语义,因此至少需要memory_order_acq_rel。 -
memory_order_seq_cst似乎没有必要,因为我们不需要跨线程的总顺序 - 我们只对当前线程中的指令顺序感兴趣。
是否可以使用 C++11 原子 API 完全可移植地表达 __asm__ volatile("" ::: "memory") 的等价物?如果是这样,atomic_signal_fence 是使用正确的 API 吗?如果是这样,这里适合/需要什么内存顺序参数?
或者,我是不是陷入了困境,有更好的方法来解决这个问题吗?
【问题讨论】:
-
atomic_signal_fence仅保证线程和在同一线程中运行的信号处理程序之间的顺序。同样atomic_thread_fence仅适用于线程之间的排序。如果您试图保证在其他两个上下文之间进行排序,那么两者都不是可移植的。例如在 Windows 上atomic_signal_fence不需要做任何事情,因为 Windows 不支持异步信号。 -
@RossRidge - 我对使用 atomic_signal_fence 感到有点奇怪,因为正如你所指出的,这里没有信号。但根据我上面的大纲,这是唯一“有效”的东西。我没有在标准中看到任何语言,尽管如果实现没有异步信号,那么可以省略对 atomic_signal_fence 的调用。它确实在 C++14 标准的 28.9.7 中声明“编译器优化和加载和存储的重新排序被禁止以与 atomic_thread_fence 相同的方式,但硬件围栏指令......不会发出。”
-
这是一个信息性(非规范性)说明,它不会对实施施加限制。该标准没有提供任何允许您依赖它的语言,而不仅仅是“等同于 atomic_thread_fence(order),除了生成的排序约束仅在线程和在同一线程中执行的信号处理程序之间建立” .另请注意,
atomic_thread_fence是根据标准定义的原子对象上的原子操作定义的。因此,如果您不使用std::atomic类型,那么这两个函数都不能保证工作。
标签: c++ c++11 portability atomic barrier