在“行动C ++并发”安东尼·威廉斯他的伟大的书中写道以下(页309):
例如,在x86和x86-64架构,原子加载操作总是相同的,无论是标记为memory_order_relaxed或memory_order_seq_cst(见第5.3.3节)。 这意味着使用松散内存排序可在与x86架构,它会在系统上会失败,并finer-晶组内存排序指令,如SPARC系统的工作编写的代码。
我得到这个权利是在x86架构上的所有原子负载操作memory_order_seq_cst
? 此外,在cppreference std::memory_order
网站提及的是,在x86版本,AQUIRE排序是自动的。
如果这种限制是有效的,执行排序仍适用于编译器优化?
是的,排序仍然适用于编译器优化。
此外,它是不完全准确的说在x86“原子加载操作都是一样的”。
在x86上,以完成所有负载mov
有收购语义,并做了所有商店mov
有释放的语义。 所以acq_rel,ACQ和轻松的负荷是简单的mov
s,而同样acq_rel,rel和轻松商店(ACQ店和相对负载总是等于放宽)。
然而,这不是seq_cst不一定是真的:建筑不保证seq_cst语义mov
。 事实上,x86指令集不具有一致的顺序加载和存储任何特定的指令。 在x86上只有原子读 - 修改 - 写操作将有seq_cst语义。 因此,你可以做一个fetch_and_add操作(获取负载seq_cst语义lock xadd
做一个seq_cst交换操作(指令)以0商店的说法,并seq_cst语义xchg
指令),并放弃以前的值。
但你并不需要做两件事! 只要所有seq_cst商店都用做xchg
,seq_cst负载可以简单地用一个执行mov
。 双重,如果所有负载用做lock xadd
,seq_cst商店可以简单地用一个执行mov
。
xchg
和lock xadd
比慢得多mov
。 因为程序具有比专卖店(通常)多个负载,可以很方便地做seq_cst商店的xchg
从而使(更频繁)seq_cst负载可以简单地用一个mov
。 此实现细节在x86应用程序二进制接口(ABI)编纂。 在x86上,一个标准的编译器必须编译seq_cst店xchg
使seq_cst负载(可能出现在另一个翻译单位,具有不同的编译器编译)可以用更快的完成mov
指令。
因此,它不是一般的说seq_cst真实,获得载荷与86相同的指令完成。 因为ABI指定seq_cst商店被编译到它是唯一真正的xchg
。
当然,编译器必须遵循的语言,无论硬件在其上运行的规则。
他说的是,在x86你没有松散排序,所以你得到,即使你不要求它更严格的顺序。 这也意味着,在x86处理器上测试这样的代码可能不是确实有松散排序的系统上正常工作。
这是值得记住的是,尽管负载轻松seq_cst负载可以映射在x86相同的指令,它们是不一样的。 放松的负载可以通过在存储器操作编译器不同的存储器位置被自由地重新排序而seq_cst负载不能在其他存储器操作来重新排序。
从书中的一句是写在一个有点误导的方式。 上结构获得的顺序取决于不只是你如何翻译原子负荷,而是你如何翻译原子商店。
实施通常的方式seq_cst
在x86是在任何之间的某一点来刷新存储缓冲器seq_cst
存储和随后的seq_cst
从同一线程的负载。 编译器的常用方法,以保证这是店后刷新,因为有比卖场少的负载。 在这个翻译, seq_cst
负载不需要刷新。
如果您只是普通的负载和存储程序的x86,负载保证提供acquire
语义,不seq_cst
。
至于编译器优化,在C11 / C ++ 11,编译器的优化取决于基于特定原子公司的语义代码运动,考虑到底层硬件之前。 (硬件可以提供更强的排序,但没有理由让编译器限制其因为这个优化。)