x86架构的内存排序限制

时间:2012-05-10 15:54:42

标签: c++ multithreading architecture c++11 memory-model

在他的伟大着作“行动中的C ++并发”中,Anthony Williams撰写了以下内容(第309页):

  

例如,在x86和x86-64架构上,原子加载操作是   始终相同,无论是标记为memory_order_relaxed还是memory_order_seq_cst   (见5.3.3节)。这意味着使用宽松的内存排序编写的代码可能   在具有x86架构的系统上工作,在具有更好的系统的情况下它将失败   粒度内存排序指令,如SPARC。

我是否认为在x86架构中所有原子加载操作都是memory_order_seq_cst?另外,在cppreference std::memory_order网站上提到x86版本 - 获取订购是自动的。

如果此限制有效,那么排序是否仍适用于编译器优化?

5 个答案:

答案 0 :(得分:5)

是的,订购仍然适用于编译器优化。

另外,在x86上“并不完全准确”“原子加载操作始终是相同的”。

在x86上,使用mov完成的所有加载都具有获取语义,并且使用mov完成的所有商店都具有释放语义。因此acq_rel,acq和轻松加载是简单的mov s,类似的acq_rel,rel和relax存储(acq存储和rel加载总是等于放松)。

然而,seq_cst不一定如此:架构保证mov的seq_cst语义。实际上,x86指令集没有任何针对顺序一致的加载和存储的特定指令。只有x86上的原子读 - 修改 - 写操作才具有seq_cst语义。因此,您可以通过执行带参数0的fetch_and_add操作(lock xadd指令)来获取负载的seq_cst语义,并通过执行seq_cst交换操作(xchg指令)来实现存储的seq_cst语义并丢弃以前的价值。

但你不需要两者兼顾!只要所有seq_cst存储都使用xchg完成,就可以使用mov简单地实现seq_cst加载。双重地,如果所有加载都是使用lock xadd完成的,则可以使用mov简单地实现seq_cst商店。

xchglock xaddmov慢得多。因为程序(通常)比商店有更多的负载,所以使用xchg进行seq_cst存储是很方便的,这样(更频繁的)seq_cst加载可以简单地使用mov。此实现细节在x86应用程序二进制接口(ABI)中编写。在x86上,兼容的编译器必须将seq_cst存储编译为xchg,以便可以使用更快的mov指令来加载seq_cst(可能出现在另一个转换单元中,使用不同的编译器编译)。 p>

因此,通常不会在x86上使用相同的指令完成seq_cst和获取加载。这是唯一的,因为ABI指定将seq_cst存储编译为xchg

答案 1 :(得分:2)

编译器当然必须遵循语言规则,无论运行什么硬件。

他说的是,在x86上你没有放松的订购,所以即使你没有要求它也会得到更严格的订购。这也意味着在x86上测试的此类代码可能无法在 放宽订购的系统上正常工作。

答案 2 :(得分:0)

值得注意的是,尽管负载松弛且seq_cst加载可能映射到x86上的相同指令,但它们并不相同。放大器可以通过内存操作自由地重新排序到不同的内存位置,而seq_cst加载不能在其他内存操作中重新排序。

答案 3 :(得分:0)

书中的句子写得有些误导。在体系结构上获得的排序不仅取决于您如何转换原子载荷,还取决于您如何转换原子库。

在x86上实现seq_cst的常用方法是在任何seq_cst存储和来自同一线程的后续seq_cst加载之间的某个点刷新存储缓冲区。编译器保证这一点的通常方法是在存储之后进行刷新,因为存储比存储更少。在此转换中,seq_cst加载不需要刷新。

如果使用普通的加载和存储对x86进行编程,则保证加载提供acquire语义,而不是seq_cst

对于编译器优化,在C11 / C ++ 11中,编译器在考虑底层硬件之前,根据特定原子的语义,根据代码移动进行优化。 (硬件可能提供更强的排序,但由于这个原因,编译器没有理由限制其优化。)

答案 4 :(得分:0)

  

我是否正确,在x86架构上所有原子负载   操作是memory_order_seq_cst吗?

仅(顺序执行)程序(程序中的某些线程间可见操作)。单个操作本身不是顺序的。

询问单个隔离操作的执行顺序是否连续是没有意义的问题。

所有需要某种保证的内存操作的转换必须遵循启用该保证的策略来完成。可能有不同的策略具有不同的编译器复杂性成本和运行时成本。

[仅存在实现虚拟功能的不同策略:唯一可行的(符合我们对速度,可预测性和简单性的所有期望)是使用vtables,因此所有编译器都使用vtable,但使用虚拟函数未定义为通过vtable。]

实践中,有没有广泛不同的策略用于在给定的CPU(我知道的 >)。编译器之间的差异很小,并且不妨碍二进制兼容性。但是可能存在差异,并且多线程程序的高级全局优化可能为原子操作更有效地生成代码打开新的机会。

根据编译器的不同,即使只在CPU顺序很严格的情况下,仅包含宽松负载和memory_order_seq_cst对象的memory_order_seq_cst修改的程序也可能仅显示顺序行为,也可能不显示顺序行为。