CPU缓存一致性协议MESI详解

本文详细介绍了CPU高速缓存的工作原理,包括局部性原理、CPU高速缓存的执行流程以及多级缓存结构。重点讲解了多核环境下缓存一致性协议MESI的状态转换和应用场景,解释了如何处理缓存行的伪共享问题,并讨论了内存屏障和StoreBuffers在解决性能问题中的作用。此外,还提及了Java中的@sun.misc.Contended注解来缓解伪共享问题。
摘要由CSDN通过智能技术生成

前言

  • CPU在摩尔定律下以每18月翻一番的速度在发展,然而内存和磁盘的发展速度远不及CPU。
  • 这就造成了高性能的内存和磁盘价格昂贵,然而CPU的高速运算又需要高速的数据,为了解决这个问题,CPU中内置少量的高速缓存以解决IO速度和CPU运算速度之间不匹配的问题。
  • 在CPU访问存储设备时,无论是存取数据和指令,都聚集在一片连续的区域,这被称为局部性原理
- 时间局部性:当一个信息被访问,那么近期它可能还会被再次访问,例如:循环、递归、方法的反复调用;
- 空间局部性:当一个存储器位置被引用,那么它附近的位置也可能会被引用,例如:顺序执行的代码、连续创建的对象或数组

CPU高速缓存的执行流程

  1. 程序及数据被加载到主内存
  2. 指令和数据被加载到CPU高速缓存
  3. CPU执行指令,把结果写到CPU高速缓存
  4. CPU高速缓存把数据写入主内存

1628432113(1).png

CPU多级缓存结构

  • CPU的运算速度超过1级缓存的数据IO能力,CPU引入了多级缓存的结构

1628432269(1).png

多核CPU的缓存一致性协议MESI

  • 多核CPU下有多个多级缓存,如何保证缓存中内部数据的一致性,不让数据混乱。这里就引入一个一致性协议MESI
  • MESI是指4种状态的首字母。每个Cache line(缓存行:缓存存储数据的单元)有4个状态,可用2bit表示,如下:
状态描述监听
M 修改(Modified)该Cache line有效,数据被修改了,和内存中的数据不一样,数据只存在本Cache中缓存行必须时刻监听所有试图读该缓存行相对就主存的操作,这种操作必须在缓存将该缓存行写回主存并将状态变成S(共享)状态之前被延迟执行。
E 独享、互斥(Exclusive)该Cache line有效,数据和内存中的数据一样,数据只存在本Cache中缓存行也必须监听其它缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S(共享)状态。
S 共享(Shared)该Cache line有效,数据和内存中的数据一样,数据存在多个Cache中缓存行也必须监听其它缓存使该缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效(Invalid)。
I 无效(Invalid)该Cache line无效
> - 对于M和E状态来说总是精确的,它们在和该缓存行的的真正状态是一致的,而S状态是非一致的。如果一个缓存将处于S状态的缓存行进行作废,而另一个缓存实际上可能已经独享了该缓存,但该缓存不会将该缓存行升级为E状态,因为其它缓存不会通知它们作废该缓存行,同样由于缓存没有保存该缓存行的copy数量,因此也没有办法确认自己是否已经独享了该缓存行
> - 从上面的看来E状态是一种投机性优化:如果一个CPU想修改一个处于S状态下的缓存行,总线事务需要将所有该缓存行的copy变成Invalid状态,而修改E状态的缓存不需要使用总线事务。

MESI状态转换

  1. 触发事件
触发事件描述
本地读取(local read)本地cache读取本地cache数据
本地写入(local write)本地cache写入本地cache数据
远程读取(remote read)远程cache读取本地cache数据
远程读取(remote write)远程cache写入本地cache数据
  1. cache分类(前提:所有cache共同缓存了主内存中某一项数据)
    1. 本地cache:指当前CPU的cache。
    2. 触发cache:触发读写事件的cache。
    3. 其他cache:指除上两种以外的cache。
  • 注:本地的事件触发 本地cache和触发cache是相同的。
状态触发本地读取触发本地写入触发远端读取触发远端写入
M状态(修改)本地cache:M 触发cache:M其他cache:I本地cache:M 触发cache:M其他cache:I本地cache:M→E→S触发cache:I→S其他cache:I→S同步主内存后修改为E独享,同步触发、其他cache后本地、触发、其他cache修改为S共享本地cache:M→E→S→I触发cache:I→S→E→M其他cache:I→S→I同步和读取一样,同步完成后触发cache改为M,本地、其他cache改为I
E状态(独享)本地cache:E触发cache:E其他cache:I本地cache:E→M触发cache:E→M其他cache:I本地cache变更为M,其他cache状态应当是I(无效)本地cache:E→S触发cache:I→S其他cache:I→S当其他cache要读取该数据时,其他、触发、本地cache都被设置为S(共享)本地cache:E→S→I触发cache:I→S→E→M其他cache:I→S→I当触发cache修改本地cache独享数据时时,将本地、触发、其他cache修改为S共享.然后触发cache修改为独享,其他、本地cache修改为I(无效),触发cache再修改为M
S状态(共享)本地cache:S触发cache:S其他cache:S本地cache:S→E→M触发cache:S→E→M其他cache:S→I 当本地cache修改时,将本地cache修改为E,其他cache修改为I,然后再将本地cache为M状态本地cache:S触发cache:S其他cache:S本地cache:S→I触发cache:S→E→M其他cache:S→I当触发cache要修改本地共享数据时,触发cache修改为E(独享),本地、其他cache修改为I(无效),触发cache再次修改为M(修改)
I状态(无效)本地cache:I→S或者I→E触发cache:I→S或者I →E其他cache:E、M、I→S、I本地、触发cache将从I无效修改为S共享或者E独享,其他cache将从E、M、I 变为S或者I本地cache:I→S→E→M触发cache:I→S→E→M其他cache:M、E、S→S→I既然是本cache是I,其他cache操作与它无关既然是本cache是I,其他cache操作与它无关
  • 当一个缓存行调整状态时,其他缓存行需要调整的状态
    | | M | E | S | I |
    |- | - | - | - | - |
    | M | × | × | × | √ |
    | E | × | × | × | √ |
    | S | × | × | √ | √ |
    | I | √ | √ | √ | √ |
  • 比如:当一个cache的变量x = 0的cache line处于S状态(共享),那么其他拥有X变量的cache的cache line需要调整为S状态(共享)或I状态(无效)

多核缓存协同操作

  • 假设三个CPU A、B、C,对应三个缓存cache a、b、c。在主内存中定义了x的引用为0。

image.png

- 单核读取:CPU A发出指令,从主内存读取X,主内存通过bus读取到缓存中,这时该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中,且状态都为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(共享)。

缓存行伪共享

  • CPU缓存是以缓存行为单元进程存储的,主流的CPU cache的cache line大小都是64bytes,在多线程情况下,如果需要修改“共享同一个缓存行的变量”,就会无意的影响彼此性能,这就是伪共享。
- 例如:现有两个变量a、b,如果t1在访问a,t2在访问b,而a和b刚好在同一个cache line中,此时t1先修改a,将导致b被刷新。
  • Java 8中有一个注解:@sun.misc.Contended。加上这个注解的类会自动补齐缓存行,此注解默认失效,需要在jvm添加:-XX:-RestrictContended 才生效。

一致性协议MESI引入的问题

  • 缓存的一致性消息传递需要时间的,这使切换时会产生延迟。当一个缓存切换状态时其他缓存收到消息完成各自的切换并发出回应,这么一串的时间中CPU都会等待所有缓存响应完成。可能就会出现阻塞导致各种各样的性能问题和稳定性问题。
  • 比如:修改本地缓存一条消息,那么必须将I(无效)状态通知到其它拥有该缓存数据的CPU缓存中,并且等待确认。等待确认过程会阻塞处理器,降低处理器的性能。

Store bufferes

  • 为避免上述CPU阻塞浪费性能,Store bufferes被引入。处理器把想要写入到主内存的值写到存储缓存(Store bufferes),然后继续去处理其他事情。当所有状态确认收到,数据才会最终提交。
  • 存储缓存(store bufferes)并不是无穷大,所以处理器有时需要等待失效确认的返回,为了应付这种情况,引入失效队列:
- 对于所有收到的invalidate请求,invalidate acknowledge消息必须立刻发送
- invalidate并不真正执行,而是被放在一个特殊队列中,在方便的时间才执行
- 处理器不会发送任何消息给所处理的缓存条目,直到它处理invalidate

内存屏障

  • 即便有上述情况处理,处理器依然不知道什么时候优化是允许的,什么时候是不允许的。
  • 所以处理器就把这个任务丢给了写代码的人,这就内存屏障(memory barriers)
- 写屏障(store memory barrier):告诉处理器在执行这之后的指令前,先应用所有已经在存储缓存(store buffer)中保存的指令
- 读屏障(load memory barrier):告诉处理器在执行任何的加载前,先应用所有已经在失效队列中的失效操作的指令

最后

  • 虚心学习,共同进步 -_-
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值