在Visual C ++ 2013上,当我编译以下代码时
#include <atomic>
int main()
{
std::atomic<int> v(2);
return v.fetch_add(1, std::memory_order_relaxed);
}
我在x86上找回了以下程序集:
51 push ecx
B8 02 00 00 00 mov eax,2
8D 0C 24 lea ecx,[esp]
87 01 xchg eax,dword ptr [ecx]
B8 01 00 00 00 mov eax,1
F0 0F C1 01 lock xadd dword ptr [ecx],eax
59 pop ecx
C3 ret
,类似于x64:
B8 02 00 00 00 mov eax,2
87 44 24 08 xchg eax,dword ptr [rsp+8]
B8 01 00 00 00 mov eax,1
F0 0F C1 44 24 08 lock xadd dword ptr [rsp+8],eax
C3 ret
我根本不明白:为什么int
变量的放宽增量需要lock
前缀?
是否有这样的理由,或者他们是否只是不包括删除它的优化?
*我使用/O2
和/NoDefaultLib
来修剪它并删除不必要的C运行时代码,但这与问题无关。
答案 0 :(得分:5)
因为它仍然需要锁定才能成为原子;即使使用memory_order_relaxed
,增量/减量的要求也太严格,无法锁定。
想象没有锁的同样的东西。
v = 0;
然后我们生成100个线程,每个线程都有这个命令:
v++;
然后你等待所有线程完成,你期望v是什么?不幸的是,它可能不是100.假设值v = 23由一个线程加载,并且在创建24之前,另一个线程也加载23然后也写出24。所以线程实际上互相否定。这是因为增量本身是非原子。当然,加载,存储,添加可能是原子的,但增量是多步,所以它不是原子的。
但是使用std :: atomic,无论std::memory_order
设置如何,所有操作都是原子操作。唯一的问题是它们将发生什么样的顺序。memory_order_relaxed
仍然保证原子性,它可能与其附近发生的任何事情有关,甚至可能在相同的值上运行。
答案 1 :(得分:1)
即使是放松的顺序,原子操作仍然必须是原子。
即使当前CPU上的某些操作是 原子,而没有lock
前缀(提示:由于多核高速缓存,它们不能被使用),将来的CPU也无法保证
让所有二进制文件在最新架构上严重失败是短视的,只是因为您想依靠二进制代码中不属于汇编规范的功能来优化字节(因此不能保证保留)在未来的x86_64架构中)
当然,在这种情况下,多核系统非常普遍,因此您实际上需要使用lock
前缀才能使其在当前CPU上工作。参见Can num++ be atomic for 'int num'?
答案 2 :(得分:-1)
首先,考虑正常分配以供参考。它在Intel / 64上生成以下内容:
// v = 10;
000000014000E0D0 mov eax,0Ah
000000014000E0D5 xchg eax,dword ptr [v (014001BCDCh)]
然后考虑一个轻松的任务:
// v.store(10, std::memory_order_relaxed);
000000014000E0D0 mov dword ptr [v (014001BCDCh)],0Ah
现在,std::atomic::fetch_add()
是一个Read-Modify-Write操作,以“脏”方式执行此操作毫无意义。默认情况下,您按http://en.cppreference.com/w/cpp/atomic/atomic/fetch_add获取std::memory_order_seq_cst
。因此,我认为,为此生成单个本机指令是有意义的。至少在英特尔/ 64便宜的地方:
// v.fetch_add(1, std::memory_order_relaxed)
000000014000E0D0 mov eax,1
000000014000E0D5 lock xadd dword ptr [v (014001BCDCh)],eax
毕竟,您可以通过显式编写编译器必须遵守的两个操作来实现您想要的目标:
// auto x = v.load(std::memory_order_relaxed);
000000014000E0D0 mov eax,dword ptr [v (014001BCDCh)]
// ++x;
000000014000E0D6 inc eax
//v.store(x, std::memory_order_relaxed);
000000014000E0D8 mov dword ptr [v (014001BCDCh)],eax