标准C ++ 11是否保证memory_order_seq_cst
阻止StoreLoad重新排序原子操作以进行非原子内存访问?
众所周知,C ++ 11中有6个std::memory_order
,它指定如何在原子操作周围排序常规,非原子内存访问 - 工作草案,C ++编程语言标准2016-07-12:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf
§29.3顺序和一致性
§29.3/ 1
枚举 memory_order 指定详细的常规 (非原子)内存同步顺序,如1.10和5中所定义 提供操作订购。它列举的价值和他们的 含义如下:
众所周知,这6个memory_orders会阻止其中一些重新排序:
但是,memory_order_seq_cst
是否阻止StoreLoad围绕常规,非原子内存访问的原子操作重新排序,或仅针对具有相同memory_order_seq_cst
的其他原子进行重新排序?
即。如果我们对STORE和LOAD都使用std::memory_order_seq_cst
,或者仅对其中一个使用{Store},那么要阻止这个StoreLoad重新排序吗?
std::atomic<int> a, b;
b.store(1, std::memory_order_seq_cst); // Sequential Consistency
a.load(std::memory_order_seq_cst); // Sequential Consistency
关于Acquire-Release语义是清楚的,它完全指定了原子操作中的非原子内存访问重新排序:http://en.cppreference.com/w/cpp/atomic/memory_order
为了防止StoreLoad重新排序,我们应该使用std::memory_order_seq_cst
。
两个例子:
std::memory_order_seq_cst
:有MFENCE
StoreLoad无法重新排序 - GCC 6.1.0 x86_64:https://godbolt.org/g/mVZJs0
std::atomic<int> a, b;
b.store(1, std::memory_order_seq_cst); // can't be executed after LOAD
a.load(std::memory_order_seq_cst); // can't be executed before STORE
std::memory_order_seq_cst
仅适用于LOAD:没有MFENCE
StoreLoad可以重新排序 - GCC 6.1.0 x86_64:https://godbolt.org/g/2NLy12
std::atomic<int> a, b;
b.store(1, std::memory_order_release); // can be executed after LOAD
a.load(std::memory_order_seq_cst); // can be executed before STORE
此外,如果C / C ++ - 编译器使用C / C ++ 11的替代映射到x86,它在LOAD之前刷新存储缓冲区:MFENCE,MOV (from memory)
,所以我们也必须使用std::memory_order_seq_cst
来加载LOAD :http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html正如此示例在另一个问题中讨论为方法(3):Does it make any sense instruction LFENCE in processors x86/x86_64?
即。我们应该使用std::memory_order_seq_cst
对STORE和LOAD生成MFENCE
保证,以防止StoreLoad重新排序。
是真的,memory_order_seq_cst
用于原子加载或存储:
specifici获取 - 释放语义 - 阻止:LoadLoad,LoadStore,StoreStore重新排序原子操作以进行常规,非原子内存访问,
但是阻止StoreLoad重新排序原子操作只针对具有相同memory_order_seq_cst
的其他原子操作?
答案 0 :(得分:4)
不,标准C ++ 11 不保证memory_order_seq_cst
阻止 StoreLoad 在non-atomic
周围重新排序atomic(seq_cst)
即使标准C ++ 11 也不能保证memory_order_seq_cst
阻止<{1}}周围的atomic(non-seq_cst)
StoreLoad 重新排序。
工作草案,编程语言C ++标准2016-07-12:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf
atomic(seq_cst)
操作都应该有一个总订单S - C ++ 11 Standard:§29.3
3
所有memory_order_seq_cst上应该有一个总订单S. 操作,符合“之前发生”的顺序和 所有受影响地点的修改订单,例如每个 memory_order_seq_cst操作B从原子加载值 对象M观察到以下值之一:...
memory_order_seq_cst
的任何原子操作都没有顺序一致性,并且没有单个总顺序,即非memory_order_seq_cst
操作可以使用memory_order_seq_cst
操作重新排序允许的方向 - C ++ 11标准:§29.3
8 [注意: memory_order_seq_cst可确保顺序一致性 仅适用于无数据竞赛且仅使用的程序 memory_order_seq_cst操作。任何使用较弱的订购都会 除非使用极度谨慎,否则此保证无效。特别是, memory_order_seq_cst围栏确保仅针对围栏的总订单 他们自己。通常,挡板不能用于恢复顺序 具有较弱订购规范的原子操作的一致性。 - 结束说明]
此外,C ++ - 编译器允许这样的重新排序:
通常 - 如果在编译器中seq_cst在存储后实现为屏障,那么:
memory_order_seq_cst
STORE-C(relaxed);
可以重新排序到LOAD-B(seq_cst);
LOAD-B(seq_cst);
GCC 7.0 x86_64生成的Asm的屏幕截图:https://godbolt.org/g/4yyeby
另外,理论上可行 - 如果在编译器中seq_cst在加载之前实现为屏障,那么:
STORE-C(relaxed);
STORE-A(seq_cst);
可以重新排序到LOAD-C(acq_rel);
LOAD-C(acq_rel);
STORE-A(seq_cst);
STORE-A(seq_cst);
可以重新排序到LOAD-C(relaxed);
LOAD-C(relaxed);
同样在PowerPC上也可以重新排序:
STORE-A(seq_cst);
STORE-A(seq_cst);
可以重新排序为STORE-C(relaxed);
STORE-C(relaxed);
如果允许原子变量跨原子(seq_cst)重新排序,那么非原子变量也可以在原子(seq_cst)上重新排序。
GCC 4.8 PowerPC生成的Asm截图:https://godbolt.org/g/BTQBr8
更多详情:
STORE-A(seq_cst);
STORE-C(release);
可以重新排序到LOAD-B(seq_cst);
LOAD-B(seq_cst);
Intel® 64 and IA-32 Architectures
8.2.3.4负载可能会与较早的商店重新排序到不同的位置
即。 x86_64代码:
STORE-C(release);
可以重新订购:
STORE-A(seq_cst);
STORE-C(release);
LOAD-B(seq_cst);
这可能发生,因为STORE-A(seq_cst);
LOAD-B(seq_cst);
STORE-C(release);
和c.store
之间不是b.load
:
x86_64 - GCC 7.0 :https://godbolt.org/g/dRGTaO
C ++&amp; asm - 代码:
mfence
可以重新订购:
#include <atomic>
// Atomic load-store
void test() {
std::atomic<int> a, b, c;
a.store(2, std::memory_order_seq_cst); // movl 2,[a]; mfence;
c.store(4, std::memory_order_release); // movl 4,[c];
int tmp = b.load(std::memory_order_seq_cst); // movl [b],[tmp];
}
此外,x86 / x86_64中的顺序一致性可以通过四种方式实现:http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
#include <atomic> // Atomic load-store void test() { std::atomic<int> a, b, c; a.store(2, std::memory_order_seq_cst); // movl 2,[a]; mfence; int tmp = b.load(std::memory_order_seq_cst); // movl [b],[tmp]; c.store(4, std::memory_order_release); // movl 4,[c]; }
(没有围栏)和LOAD
+STORE
MFENCE
(没有围栏)和LOAD
LOCK XCHG
+MFENCE
和LOAD
(没有围栏)- 醇>
STORE
(0)和LOCK XADD
(没有围栏)
STORE
和(LOAD
+ STORE
)/(MFENCE
) - 我们在上面进行了审核LOCK XCHG
+ MFENCE
)/ LOAD
和LOCK XADD
- 允许下一次重新排序: STORE
STORE-A(seq_cst);
可以重新排序到LOAD-C(acq_rel);
LOAD-C(acq_rel);
STORE-A(seq_cst);
STORE-A(seq_cst);
可以重新排序到LOAD-C(relaxed);
LOAD-C(relaxed);
允许存储负载重新排序(表5 - PowerPC ):http://www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.06.07c.pdf
加载后重新排序的商店
即。 PowerPC代码:
STORE-A(seq_cst);
可以重新订购:
STORE-A(seq_cst);
STORE-C(relaxed);
LOAD-C(relaxed);
LOAD-B(seq_cst);
PowerPC - GCC 4.8 :https://godbolt.org/g/xowFD3
C ++&amp; asm - 代码:
LOAD-C(relaxed);
STORE-A(seq_cst);
STORE-C(relaxed);
LOAD-B(seq_cst);
将#include <atomic>
// Atomic load-store
void test() {
std::atomic<int> a, b, c; // addr: 20, 24, 28
a.store(2, std::memory_order_seq_cst); // li r9<-2; sync; stw r9->[a];
c.store(4, std::memory_order_relaxed); // li r9<-4; stw r9->[c];
c.load(std::memory_order_relaxed); // lwz r9<-[c];
int tmp = b.load(std::memory_order_seq_cst); // sync; lwz r9<-[b]; ... isync;
}
分成两部分 - 可以重新排序为:
a.store
从内存加载#include <atomic>
// Atomic load-store
void test() {
std::atomic<int> a, b, c; // addr: 20, 24, 28
//a.store(2, std::memory_order_seq_cst); // part-1: li r9<-2; sync;
c.load(std::memory_order_relaxed); // lwz r9<-[c];
a.store(2, std::memory_order_seq_cst); // part-2: stw r9->[a];
c.store(4, std::memory_order_relaxed); // li r9<-4; stw r9->[c];
int tmp = b.load(std::memory_order_seq_cst); // sync; lwz r9<-[b]; ... isync;
}
执行的时间早于存储到内存lwz r9<-[c];
。
同样在PowerPC上也可以重新排序:
stw r9->[a];
STORE-A(seq_cst);
可以重新排序为STORE-C(relaxed);
STORE-C(relaxed);
因为PowerPC具有弱内存排序模型 - 允许Store-Store重新排序(表5 - PowerPC ):http://www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.06.07c.pdf
商店后重新订购的商店
即。在PowerPC操作上,Store可以与其他Store重新排序,然后可以重新排序前面的示例,例如:
STORE-A(seq_cst);
存储到内存#include <atomic>
// Atomic load-store
void test() {
std::atomic<int> a, b, c; // addr: 20, 24, 28
//a.store(2, std::memory_order_seq_cst); // part-1: li r9<-2; sync;
c.load(std::memory_order_relaxed); // lwz r9<-[c];
c.store(4, std::memory_order_relaxed); // li r9<-4; stw r9->[c];
a.store(2, std::memory_order_seq_cst); // part-2: stw r9->[a];
int tmp = b.load(std::memory_order_seq_cst); // sync; lwz r9<-[b]; ... isync;
}
的执行时间早于存储到内存stw r9->[c];
。
答案 1 :(得分:0)