jle*_*ahy 13 c++ gcc atomic c++11
我的印象是内存加载无法在C++ 11内存模型中的获取负载之上提升.然而,看看gcc 4.8产生的代码,对于其他原子载荷而言似乎只是真实的,而不是所有的内存.如果这是真的并且获取负载不同步所有内存(只是std::atomics)那么我不确定如何用std :: atomic实现通用互斥锁.
以下代码:
extern std::atomic<unsigned> seq;
extern std::atomic<int> data;
int reader() {
int data_copy;
unsigned seq0;
unsigned seq1;
do {
seq0 = seq.load(std::memory_order_acquire);
data_copy = data.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
seq1 = seq.load(std::memory_order_relaxed);
} while (seq0 != seq1);
return data_copy;
}
Run Code Online (Sandbox Code Playgroud)
生产:
_Z6readerv:
.L3:
mov ecx, DWORD PTR seq[rip]
mov eax, DWORD PTR data[rip]
mov edx, DWORD PTR seq[rip]
cmp ecx, edx
jne .L3
rep ret
Run Code Online (Sandbox Code Playgroud)
这对我来说是正确的.
但是,将数据更改为int而不是std::atomic:
extern std::atomic<unsigned> seq;
extern int data;
int reader() {
int data_copy;
unsigned seq0;
unsigned seq1;
do {
seq0 = seq.load(std::memory_order_acquire);
data_copy = data;
std::atomic_thread_fence(std::memory_order_acquire);
seq1 = seq.load(std::memory_order_relaxed);
} while (seq0 != seq1);
return data_copy;
}
Run Code Online (Sandbox Code Playgroud)
产生这个:
_Z6readerv:
mov eax, DWORD PTR data[rip]
.L3:
mov ecx, DWORD PTR seq[rip]
mov edx, DWORD PTR seq[rip]
cmp ecx, edx
jne .L3
rep ret
Run Code Online (Sandbox Code Playgroud)
发生什么了?
为什么负载被提升到获取上方
我已将其发布在gcc bugzilla上,他们已确认它是一个错误。
MEM 别名集 -1 (ALIAS_SET_MEMORY_BARRIER) 应该可以防止这种情况,但 PRE 不知道这个特殊属性(它应该“杀死”所有穿过它的引用)。
看起来gcc wiki有一个关于此的很好的页面。
一般来说,释放是下沉代码的障碍,获取是提升代码的障碍。
为什么这段代码仍然被破坏
根据本文,我的代码仍然不正确,因为它引入了数据竞争。即使修补后的 gcc 生成了正确的代码,但data如果不将其包装起来,仍然无法正确访问std::atomic. 原因是数据竞争是未定义的行为,即使由此产生的计算被丢弃。
AdamH.Peterson 提供的示例:
int foo(unsigned x) {
if (x < 10) {
/* some calculations that spill all the
registers so x has to be reloaded below */
switch (x) {
case 0:
return 5;
case 1:
return 10;
// ...
case 9:
return 43;
}
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里,编译器可能会将开关优化为跳转表,并且由于上面的 if 语句将能够避免范围检查。但是,如果数据争用不是未定义的行为,则需要进行第二次范围检查。
| 归档时间: |
|
| 查看次数: |
782 次 |
| 最近记录: |