为什么CompareAndSwap指令被认为是昂贵的?
我读了一本书:
“内存障碍很昂贵,差不多 昂贵的原子compareAndSet() 指令“。
谢谢!
答案 0 :(得分:14)
“CAS与正常商店没有明显不同。有关CAS的一些错误信息可能源于英特尔处理器上最初的lock:cmpxchg(CAS)实现.lock:前缀导致了LOCK#信号获得对总线的独占访问权限。这当然没有扩展。锁的后续实现:cmpxchg利用缓存一致性协议 - 通常基于snoop的MESI - 并且不断言LOCK#。“ - David Dice,Biased locking in HotSpot
“内存障碍很昂贵,与原子compareAndSet()指令一样昂贵。”
这是真的 例如。在x86上,多处理器系统上的正确CAS具有锁定前缀 锁定前缀会导致完整的内存屏障:
“...锁定操作序列化所有未完成的加载和存储操作(即等待它们完成)。” ......“锁定操作相对于所有其他内存操作和所有外部可见事件都是原子操作。只有指令获取和页表访问才能传递锁定指令。锁定指令可用于同步由一个处理器写入的数据并由另一个处理器读取“。 - Intel® 64 and IA-32 Architectures Software Developer’s Manual,第8.1.2章。
事实上,在x86 / x64上,内存屏障在the .NET和the JAVA JIT中实现为虚拟LOCK OR
或LOCK AND
。
在x86上,CAS会产生完整的内存屏障。
在PPC上,它是不同的。 LL/SC对 - lwarx
& stwcx
- 可用于将内存操作数加载到寄存器中,然后在目标位置没有其他存储时将其写回,或者如果有,则重试整个循环。 LL / SC可以被中断
它也不代表自动完整的围栏
不同架构的性能特征和行为可能会有很大差异
但话说回来 - weak LL/SC不是CAS。
答案 1 :(得分:3)
这是因为它们为操作原子引入了额外的开销。底层平台必须抑制优化(如缓存)并暂停线程执行以促进障碍,这需要大量额外的工作。当额外的活动正在进行时,线程无法执行,因此整个程序会产生时间延迟。
答案 2 :(得分:3)
int
比从两个寄存器中添加{{1}}要容易50倍。
因此,由于内存屏障基本上强制直接访问RAM(可能是多个CPU),因此它们相对昂贵。
答案 3 :(得分:2)
我想我在书中找到了答案:
每个getAndSet()都会广播到总线。因为 所有线程都必须使用总线进行通信 内存,这些getAndSet()调用延迟所有线程(核心), 即便是那些没有等待锁定的人。
更糟糕的是,getAndSet()调用强制其他 处理器丢弃自己的缓存副本 锁,所以每个纺纱线遇到一个 缓存几乎每次都错过了,必须使用 总线获取新的但未改变的值。
答案 4 :(得分:1)
通常,原子操作很昂贵,因为它们需要跨CPU同步。允许“正常”操作对缓存数据进行操作,从而允许额外的速度。例如,在2 cpu系统上:
线程1
while (1) x++;
线程2
while (1) x++;
因为增量不是原子操作或受内存屏障保护,所以结果几乎没有定义。你不知道x会如何增加,甚至可能会被破坏。
线程1
while (1) atomicIncrement(&x);
线程2
while (1) atomicIncrement(&x);
现在,您正在尝试获得明确定义的行为 - 无论顺序如何,x必须逐个增加。如果两个线程在不同的CPU上运行,它们必须减少允许的缓存量或以其他方式“比较注释”以确保发生合理的事情。
这个额外的开销可能非常昂贵,而且这是声称原子操作很慢的一般原因。