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

compiler reordering vs memory reordering

本文关键字:排序 新排序 内存 编译器      更新时间:2023-10-16

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

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

C++原子提供简而言之:

- acquire/release semantics
- Sequentially-consistent ordering

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

或者也许C++不做这种区别? 该语言仅提供适用于同时重新排序的语义?

第一个障碍仅在编译期间适用。编译器完成后,它没有任何影响,因为不会向代码中添加任何内容。这对于避免一些内存排序问题可能很有用(编译器不知道其他线程如何操作这些内存位置,尽管几乎没有任何具有正常设置的编译器敢于对可能出现这种情况的变量进行重新排序(。

然而,这远远不够,因为在现代无序 CPU 上,硬件本身可能会在后台重新排序操作。为了避免这种情况,你有办法告诉硬件要注意,给定你想要达到的确切限制级别和形式(顺序一致性是最严格和"安全"的排序模型,但通常也是最昂贵的性能(。

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

在您的示例中,mfence是手动执行操作的示例,但您还需要知道在哪里应用它。如果使用得当,mfence 可以足够字符串以提供 seq 一致性,但也非常昂贵,因为它包含一个存储围栏 ( mfence = sfence + lfence (,这需要从内部缓冲区中排出所有挂起的存储,这是一个缓慢的操作,因为缓冲是为了允许它们延迟提交。另一方面,如果你想要获取/发布语义,你可以选择在正确的位置使用适当的部分围栏来实现它们,或者让编译器为你做到这一点。例如,如果选择后者并在 x86 计算机上运行,您会发现大多数情况下不需要添加任何内容,因为存储具有隐式发布语义,并且加载具有获取语义,但这可能不适用于其他体系结构。

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