编译器重新排序与内存重新排序

时间:2014-12-14 17:30:09

标签: c++ gcc atomic

在gcc下,有以下可用于设置内存屏障的说明。他们都提供不同的保护"

asm volatile("" ::: "memory"); // compiler reorder
asm volatile("mfence" ::: "memory"); // memory reordering

C ++原子提供简短:

- acquire/release semantics
- Sequentially-consistent ordering

我想知道gcc原语和C ++原子语义之间是否存在直接映射? (例如(必须是错误的,它只是出于解释目的),获取/释放语义是为了防止编译器重新排序和顺序一致的排序是为了防止内存重新排序)

或许C ++没有做到这一点?语言只提供适用于同时重新排序的语义吗?

1 个答案:

答案 0 :(得分:2)

第一道屏障仅适用于编译期间。编译完成后,它没有任何影响,因为没有任何内容添加到代码中。这可能有助于避免一些内存排序问题(编译器不知道其他线程如何操作这些内存位置,尽管几乎没有任何具有正常设置的编译器都敢于重新排序具有潜在可能性的变量)。

然而,这远远不够,因为在现代无序CPU上,硬件本身可能会重新排序引擎盖下的操作。为了避免这种情况,考虑到您想要达到的限制的确切级别和形式,您有办法告诉硬件要注意(顺序一致性是最严格和“安全”的订购模式,但通常也是最昂贵的表现)。

要实现这些限制,您可以尝试手动维护ISA提供的障碍和类似构造(通过内在函数,内联汇编,序列化操作或任何其他技巧)。即使您知道自己在做什么,这通常也很复杂,甚至可能是微架构特定的(某些CPU可能会“免费”授予一些限制,使得明确的防护无用),因此c ++ 11添加了原子语义使这个任务更容易,现在编译器会根据您想要的指定排序模型为您添加必要的代码。

在您的示例中,mfence是手动执行操作的示例,但您还需要知道应用它的位置。正确使用,mfence可以足够串以提供seq一致性,但也非常昂贵,因为它包含商店围栏(mfence = sfence + lfence),这需要耗尽所有来自内部缓冲区的挂起存储,缓慢操作,因为缓冲完成后允许它们进行延迟提交。 另一方面,如果您想要获取/释放语义,您可以选择在考虑您的体系结构的正确位置使用适当的部分栅栏来实现它们,或者让编译器为您执行此操作。例如,如果选择后者并在x86机器上运行,您会发现大多数时候都不需要添加,因为存储具有隐式发布语义并且加载具有获取语义,但同样可能不适用于其他体系结构。

以下是每个架构的各种排序语义实现的一个很好的总结 - http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html