转载:Java CAS原理分析

Java在多线程、对象内存分配中经常用到CAS也就是Compare and Set/Swap,下面整理一下资料。

CAS伪代码表示:
compare_and_set (current, expect,new)
{
if ( current == expect ) //检查在函数执行之前,要修改的数据是否一致,不一致,就重新回到起点执行,一致就赋新值(有个例外,ABA
current = new;
else
return false;
}

CAS有两种方式保证原子性:
1、锁总线
2、缓存一致性协议

锁总线很好理解,操作码前缀是lock字节的“读-修改-写”汇编指令(cmpxchg)在多处理器是原子的。当控制单元检测到这个前缀时,就“锁定”内存总线,直到这条指令完成为止,期间其他处理器不能访问“这个”内存单元,简单粗暴。

下面主要梳理Cache一致性原理:

转自http://www.wtoutiao.com/p/69ayF1W.html

根据开发者手册 8.1.4的描述在p6以后的x86处理器中,原子操作(例如cmpxchg)不再发出任何lock信号,一切都由 Cache 一致性协议来完成。

首先说一下我们要讨论的多核处理器的缓存结构
CPU缓存结构
目前的结构每个core都会持有自己私有的L1/L2 Cache,但是 L3 cache是所有core共有的。那么就存在一个cache一致性的问题, 由公开资料可以知道, X86 采用的是改进型的MESI协议MESIF,主要是添加了一个 Forwarding的状态,简化有多share节点状态下 对read request的处理。每个core都挂在一个或数个cache ring bus只上。每个core都有一个cbox(bus agent)负责监听其它core对cache的操作行为, 从而根据协议采取对应的行动。

首先非常简单的说一下 MESI协议,展开讨论这个话题超出本文的范围。该协议规定:每个Cacheline 有4中不同的状态 :

Modified(M):表示这个cacheline已经被修改,但是还未写回主存(cache中数据与主存中数据不一致。所有其它core对这个cacheline的读操作必须在该cacheline 写回主存后,写回主存后,状态变换到share。

Exclusive(E): 表示这个cacheline 目前是独有的且与主存一致,可以转换到shared 或者M,重点是可以转换到M,也就意味着可以对其修改。

Shared(S):表示这个cacheline 在其它core的cache中也存在, 目前也与主存一致,随时会被invalidate。

Invalid(I):表示这个cacheline 目前不可用。
四种状态
这个图的含义就是当一个core持有一个cacheline的状态为Y时,其它core对应的cacheline应该处于状态X, 比如地址 0x00010000 对应的cacheline在core0上为状态M, 则其它所有的core对应于0x00010000的cacheline都必须为I , 0x00010000 对应的cacheline在core0上为状态S,则其它所有的core对应于0x00010000的cacheline 可以是S或者I 。

MESIF 就是对MESI做了一个优化。大家设想一下, 如果现在一个cacheline 是S状态,在多个core中有同一copy,那么现在有一个新的core需要读取该cacheline, 应该由那个core来应答。 如果每个持有该cacheline的core都来应答就会造成冗余的数据传输,所以对于在系统中有多个copy的S状态的cacheline中,必须选取一个转换为F,只有F状态的负责应答。通常是最后持有该copy的cacheline转换为F。
五种状态
说了这些背景知识之后,再回到我们的CAS指令。

当两个core同时执行针对同一地址的CAS指令时,其实他们是在试图修改每个core自己持有的Cache line, 假设两个core都持有相同地址对应cacheline,且各自cacheline 状态为S, 这时如果要想成功修改,就首先需要把S转为E或者M, 则需要向其它core invalidate 这个地址的cacheline,则两个core都会向ring bus 发出 invalidate这个操作, 那么在ringbus上就会根据特定的设计协议仲裁是core0,还是core1能赢得这个invalidate,者完成操作,失败者需要接受结果invalidate自己对应的cacheline,再读取胜者修改后的值,回到起点。

到这里, 我们可以发现MESIF协议大大降低了读操作的时延,没有让写操作更慢,同时保持了一致性。那么对于我们的CAS操作来说,其实锁并没有消失,只是转嫁到了ring bus的总线仲裁协议中。而且大量的多核同时针对一个地址的CAS操作会引起反复的互相invalidate 同一cacheline,造成pingpong效应,同样会降低性能。只能说,基于CAS的操作仍然是不能滥用,不到万不得已不用,通常情况下还是使用数据分离模式更好。

最后更进一步 ring bus的仲裁协议又是什么? 从公开的资料,x86这方面的公开信息非常罕见。唯一可以推断的是 这个协议会保证公平性, 在invalidate的竞争中不会总是一个core赢,从而保证不会有starving core。在power4/5 的设计中有一篇论文涉及到power处理器相关的设计细节, 大家可以参考。

http://research.cs.wisc.edu/multifacet/papers/micro06_ring.pdf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值