CPU缓存一致性协议MESI
# CPU高速缓存(Cache Memory)
MESI协议动态演示视图 (opens new window)
# CPU为何要有高速缓存?
为了解决内存和硬盘I\O性能跟不上CPU高速运算需要的数据,CPU厂商在CPU中内置了少量的高速缓存以解决I\O速度和CPU运算速度之间的不匹配问题,发挥最大CPU效率
# 时间局部性(Temporal Locality)
如果一个信息项目正在被访问,那么近期它很可能还会被再次访问;例如:循环、递归、方法的反复调用
# 空间局部性(Spatial Locality):
如果一个存储器的位置被引用,那么将来它附近的位置也会被引用;例如:顺序执行的代码、连续创建的两个对象、数组等
# 带有高速缓存的CPU执行计算的流程
- 1、程序以及数据被加载到主内存(Memory)
- 2、指令和数据被加载到CPU的高速缓存(cache)
- 3、CPU执行指令,把结果写到高速缓存(cache)
- 4、高速缓存中的数据写回主内存(Memory)
# 目前流行的多级缓存结构
由于CPU的运算速度超越了1级缓存的数据I\O能力,CPU厂商又引入了多级的缓存结构
# 多核CPU多级缓存一致性协议MESI
# MESI协议缓存状态
MESI 是指4中状态的首字母。每个Cache line有4个状态,可用2个bit表示
# 缓存行(Cache line)
注意
⚠️
# 对于M和E状态而言总是精确的,他们在和该缓存行的真正状态是一致的,而S状态可能是非一致的
# 1、如果一个缓存处于S状态的缓存行作废了,而另一个缓存实际上可能已经独享了改缓存行,但是该缓存却不会将该缓存行升迁为E状态,这是因为其他缓存不会广播他们作废掉该缓存行的通知,同样由于缓存并没有保存该缓存行的copy的数量,因此(即使有这种通知)也没有办法确定自己是否已经独享了该缓存行。
# 2、从上面的意义看来E状态是一种投机性的优化:如果一个CPU想修改一个处于S状态的缓存行,总线事务需要将所有该缓存行的copy变成invalid状态,而修改E状态的缓存不需要使用总线事务
# MESI状态转换
# 多核缓存协同操作
# 单核读取
CPU A发出了一条指令,从主内存中读取x。从主内存通过bus读取到缓存中(远端读取Remote read),这时该Cache line修改为E状态(独享)
# 两核读取
- CPU A发出了一条指令,从主内存中读取x
- CPU A从主内存通过bus读取到 cache a中并将该cache line 设置为E状态。
- CPU B发出了一条指令,从主内存中读取x。
- CPU B试图从主内存中读取x时,CPU A检测到了地址冲突。这时CPU A对相关数据做出响应。此时x 存储于cache a和cache b中,x在chche a和cache b中都被设置为S状态(共享)。
# 修改数据
- CPU A 计算完成后发指令需要修改x
- CPU A 将x设置为M状态(修改)并通知缓存了x的CPU B, CPU B将本地cache b中的x设置为I状态(无效)
- CPU A 对x进行赋值。
# 同步数据
- CPU B 发出了要读取x的指令。
- CPU B 通知CPU A,CPU A将修改后的数据同步到主内存时cache a 修改为E(独享)
- CPU A同步CPU B的x,将cache a和同步后cache b中的x设置为S状态(共享)。
# 超过单个缓存行大小(64Bytes)怎么办?
此时MESI协议不适用,升级为总线锁方案,此时只有抢占到总线资源的CPU能访问主内存信息
# 缓存行伪共享
CPU缓存系统中是以缓存行(cache line)为单位存储的。目前主流的CPU Cache 的 Cache Line 大小都是64Bytes。在多线程情况下,如果需要修改“共享同一个缓存行的变量”,就会无意中影响彼此的性能,这就是伪共享(False Sharing)。
# 例子
现在有2个long 型变量 a 、b,如果有t1在访问a,t2在访问b,而a与b刚好在同一个cache line中,此时t1先修改a,将导致b被刷新!
# 如何解决?
Java8中新增了一个注解:
@sun.misc.Contended。加上这个注解的类会自动补齐缓存行,需要注意的是此注解默认是无效的,需要在jvm启动时设置
-XX:-RestrictContended 才会生效。
# MESI优化和他们引入的问题
# CPU切换状态阻塞解决—存储缓存(Store Bufferes)
# Store Bufferes
- 为了避免这种CPU运算能力的浪费,Store Bufferes被引入使用。处理器把它想要写入到主存的值写到缓 存,然后继续去处理其他事情。当所有失效确认(Invalidate Acknowledge)都接收到时(其他所有的cpu都接收到消息后),数据才会最 终被提交。
# 硬件内存模型
- 对于所有的收到的Invalidate请求,Invalidate Acknowlege消息必须立刻发送 Invalidate并不真正执行,而是被放在一个特殊的队列中,在方便的时候才会去执行。 处理器不会发送任何消息给所处理的缓存条目,直到它处理Invalidate
上次更新: 2022/08/04, 17:55:56