Ale*_*lex 13 c++ concurrency standards multithreading c++11
标准C++ 11是否保证memory_order_seq_cst
阻止StoreLoad重新排序原子操作以进行非原子内存访问?
众所周知,std::memory_order
C++ 11中有6 个,它指定了如何围绕原子操作对常规的非原子内存访问进行排序 - 工作草案,编程语言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中定义的详细常规(非原子)内存同步顺序,并且可以提供操作排序.其枚举值及其含义如下:
众所周知,这6个memory_orders会阻止其中一些重新排序:
但是,是否会memory_order_seq_cst
阻止StoreLoad围绕原子操作重新排序以进行常规的非原子内存访问,或仅针对其他具有相同原子的原子进行重新排序memory_order_seq_cst
?
即,如果我们同时使用std::memory_order_seq_cst
STORE和LOAD,或仅用于其中一个,则阻止此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
Run Code Online (Sandbox Code Playgroud)
关于Acquire-Release语义是明确的,它完全指定了跨原子操作的非原子内存访问重新排序:http://en.cppreference.com/w/cpp/atomic/memory_order
为防止StoreLoad重新排序,我们应该使用std::memory_order_seq_cst
.
两个例子:
std::memory_order_seq_cst
对于STORE和LOAD:有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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
此外,如果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):在处理器x86/x86_64中它是否有意义指令LFENCE?
即我们应该使用std::memory_order_seq_cst
STORE和LOAD来生成MFENCE
保证,这会阻止StoreLoad重新排序.
memory_order_seq_cst
对于原子加载或存储是否正确:
specifici Acquire-Release语义 - 阻止:LoadLoad,LoadStore,StoreStore重新排序原子操作以进行常规的非原子内存访问,
但是阻止StoreLoad 只针对其他原子操作重新排序原子操作memory_order_seq_cst
吗?
没有,标准C ++ 11并不保证memory_order_seq_cst
防止StoreLoad重新排序的non-atomic
周围atomic(seq_cst)
。
即使是标准的C ++ 11并不保证memory_order_seq_cst
防止StoreLoad的重新排序atomic(non-seq_cst)
围绕atomic(seq_cst)
。
工作草案,C++ 编程语言标准 2016-07-12:http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf
memory_order_seq_cst
操作都应有一个总顺序 S - C++11 标准:§ 29.3
3
所有 memory_order_seq_cst 操作都应该有一个总顺序 S,与所有受影响位置的“发生在之前”顺序和修改顺序一致,这样从原子对象 M 加载值的每个 memory_order_seq_cst 操作 B 观察以下值之一: ...
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 在存储后实现为屏障,则:
STORE-C(relaxed);
LOAD-B(seq_cst);
可以重新排序 LOAD-B(seq_cst);
STORE-C(relaxed);
GCC 7.0 x86_64 生成的 Asm 截图:https : //godbolt.org/g/4yyeby
此外,理论上可能 - 如果在编译器中 seq_cst 在加载前实现为屏障,则:
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);
STORE-A(seq_cst);
在 PowerPC 上也可以这样重新排序:
STORE-A(seq_cst);
STORE-C(relaxed);
可以重新排序 STORE-C(relaxed);
STORE-A(seq_cst);
如果甚至允许原子变量跨原子(seq_cst)重新排序,那么非原子变量也可以跨原子(seq_cst)重新排序。
GCC 4.8 PowerPC 生成的 Asm 截图:https : //godbolt.org/g/BTQBr8
更多细节:
STORE-C(release);
LOAD-B(seq_cst);
可以重新排序 LOAD-B(seq_cst);
STORE-C(release);
8.2.3.4 加载可能会与较早的存储重新排序到不同的位置
即 x86_64 代码:
STORE-A(seq_cst);
STORE-C(release);
LOAD-B(seq_cst);
Run Code Online (Sandbox Code Playgroud)
可以重新排序为:
STORE-A(seq_cst);
LOAD-B(seq_cst);
STORE-C(release);
Run Code Online (Sandbox Code Playgroud)
这可能发生,因为之间c.store
和b.load
不是mfence
:
x86_64 - GCC 7.0:https : //godbolt.org/g/dRGTaO
C++ & asm - 代码:
#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];
}
Run Code Online (Sandbox Code Playgroud)
它可以重新排序为:
#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];
}
Run Code Online (Sandbox Code Playgroud)
此外,x86/x86_64 中的顺序一致性可以通过四种方式实现:http : //www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
LOAD
(无围栏)和STORE
+MFENCE
LOAD
(无围栏)和LOCK XCHG
MFENCE
+LOAD
和STORE
(无围栏)LOCK XADD
( 0 ) 和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);
STORE-A(seq_cst);
允许存储加载重新排序(表 5 - PowerPC):http : //www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.06.07c.pdf
加载后重新排序的商店
即 PowerPC 代码:
STORE-A(seq_cst);
STORE-C(relaxed);
LOAD-C(relaxed);
LOAD-B(seq_cst);
Run Code Online (Sandbox Code Playgroud)
可以重新排序为:
LOAD-C(relaxed);
STORE-A(seq_cst);
STORE-C(relaxed);
LOAD-B(seq_cst);
Run Code Online (Sandbox Code Playgroud)
PowerPC - GCC 4.8:https : //godbolt.org/g/xowFD3
C++ & asm - 代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
通过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;
}
Run Code Online (Sandbox Code Playgroud)
其中 load-from-memorylwz r9<-[c];
执行早于 store-to-memory stw r9->[a];
。
在 PowerPC 上也可以这样重新排序:
STORE-A(seq_cst);
STORE-C(relaxed);
可以重新排序 STORE-C(relaxed);
STORE-A(seq_cst);
因为 PowerPC 具有弱内存排序模型 - 允许 Store-Store 重新排序(表 5 - PowerPC):http : //www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.06.07c.pdf
商店后重新排序的商店
即在 PowerPC 上操作 Store 可以与其他 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];
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;
}
Run Code Online (Sandbox Code Playgroud)
其中 store-to-memorystw r9->[c];
早于 store-to-memory 执行stw r9->[a];
。