在计算机系统中,CPU高速缓存(英语:CPU Cache)是用于减少处理器访问内存所需平均时间的部件。在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器。其容量远小于内存,但速度却可以接近处理器的频率。

当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。

1. CPU高速缓存(Cache Memory)

1.1 CPU为何要有高速缓存

CPU在摩尔定律的指导下以每18个月翻一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU。这就造成了高性能能的内存和硬盘价格及其昂贵。然而CPU的高度运算需要高速的数据。为了解决这个问题,CPU厂商在CPU中内置了少量的高速缓存以解决I\O速度和CPU运算速度之间的不匹配问题。

在CPU访问存储设备时,无论是存取数据抑或存取指令,都趋于聚集在一片连续的区域中,这就被称为局部性原理。

  • 时间局部性(Temporal Locality)
    如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。比如循环、递归、方法的反复调用等。

  • 空间局部性(Spatial Locality)
    如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。比如顺序执行的代码、连续创建的两个对象、数组等。

1.2 带有高速缓存的CPU执行计算的流程

  • (1) 程序以及数据被加载到主内存
  • (2) 指令和数据被加载到CPU的高速缓存
  • (3) CPU执行指令,把结果写到高速缓存
  • (4) 高速缓存中的数据写回主内存

1.3 目前流行的多级缓存结构

由于CPU的运算速度超越了1级缓存的数据I\O能力,CPU厂商又引入了多级的缓存结构。

多级缓存结构

2. 多核CPU多级缓存一致性协议MESI

多核CPU的情况下有多个一级缓存,如何保证缓存内部数据的一致,不让系统数据混乱。这里就引出了一个一致性的协议MESI。

2.1 MESI协议缓存状态

MESI 是指4中状态的首字母。每个Cache line有4个状态,可用2个bit表示,它们分别是:

缓存行(Cache line): 缓存存储数据的单元。

状态 描述 监听任务
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状态的缓存不需要使用总线事务。

2.2 MESI状态转换

理解该图的前置说明:
(1)触发事件

触发事件 描述
本地读取(Local read) 本地cache读取本地cache数据
本地写入(Local write) 本地cache写入本地cache数据
远端读取(Remote read) 其他cache读取本地cache数据
远端写入(Remote write) 其他cache写入本地cache数据

(2)cache分类:

前提:所有的cache共同缓存了主内存中的某一条数据。

本地cache:指当前cpu的cache。
触发cache:触发读写事件的cache。
其他cache:指既除了以上两种之外的cache。

注意:本地的事件触发 本地cache和触发cache为相同。

举个例子来说:
假设cache 1 中有一个变量x = 0的cache line 处于S状态(共享)。
那么其他拥有x变量的cache 2、cache 3等x的cache line调整为S状态(共享)或者调整为 I 状态(无效)。

2.3 多核缓存协同操作

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

  • 单核读取
    那么执行流程是:
    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状态(共享)。

3. MESI优化和他们引入的问题

缓存的一致性消息传递是要时间的,这就使其切换时会产生延迟。当一个缓存被切换状态时其他缓存收到消息完成各自的切换并且发出回应消息这么一长串的时间中CPU都会等待所有缓存响应完成。可能出现的阻塞都会导致各种各样的性能问题和稳定性问题。

3.1 CPU切换状态阻塞解决-存储缓存(Store Bufferes)

比如你需要修改本地缓存中的一条信息,那么你必须将I(无效)状态通知到其他拥有该缓存数据的CPU缓存中,并且等待确认。等待确认的过程会阻塞处理器,这会降低处理器的性能。应为这个等待远远比一个指令的执行时间长的多。

Store Bufferes

为了避免这种CPU运算能力的浪费,Store Bufferes被引入使用。处理器把它想要写入到主存的值写到缓存,然后继续去处理其他事情。当所有失效确认(Invalidate Acknowledge)都接收到时,数据才会最终被提交。

Store Bufferes的风险

第一、就是处理器会尝试从存储缓存(Store buffer)中读取值,但它还没有进行提交。这个的解决方案称为Store Forwarding,它使得加载的时候,如果存储缓存中存在,则进行返回。
第二、保存什么时候会完成,这个并没有任何保证。

value = 3;void exeToCPUA(){value = 10;isFinsh = true;
}
void exeToCPUB(){if(isFinsh){//value一定等于10?!assert value == 10;}
}

试想一下开始执行时,CPU A保存着finished在E(独享)状态,而value并没有保存在它的缓存中。(例如,Invalid)。在这种情况下,value会比finished更迟地抛弃存储缓存。完全有可能CPU B读取finished的值为true,而value的值不等于10。

即isFinsh的赋值在value赋值之前。

这种在可识别的行为中发生的变化称为重排序(reordings)。注意,这不意味着你的指令的位置被恶意(或者好意)地更改。
它只是意味着其他的CPU会读到跟程序中写入的顺序不一样的结果。

3.2 硬件内存模型

执行失效也不是一个简单的操作,它需要处理器去处理。另外,存储缓存(Store Buffers)并不是无穷大的,所以处理器有时需要等待失效确认的返回。这两个操作都会使得性能大幅降低。为了应付这种情况,引入了失效队列。它们的约定如下:

  • 对于所有的收到的Invalidate请求,Invalidate Acknowlege消息必须立刻发送
  • Invalidate并不真正执行,而是被放在一个特殊的队列中,在方便的时候才会去执行。
  • 处理器不会发送任何消息给所处理的缓存条目,直到它处理Invalidate。

即便是这样处理器已然不知道什么时候优化是允许的,而什么时候并不允许。干脆处理器将这个任务丢给了写代码的人。这就是内存屏障(Memory Barriers)。

写屏障 Store Memory Barrier(a.k.a. ST, SMB, smp_wmb)是一条告诉处理器在执行这之后的指令之前,应用所有已经在存储缓存(store buffer)中的保存的指令。

读屏障Load Memory Barrier (a.k.a. LD, RMB, smp_rmb)是一条告诉处理器在执行任何的加载前,先应用所有已经在失效队列中的失效操作的指令。

void executedOnCpu0() {value = 10;//在更新数据之前必须将所有存储缓存(store buffer)中的指令执行完毕。storeMemoryBarrier();finished = true;
}
void executedOnCpu1() {while(!finished);//在读取之前将所有失效队列中关于该数据的指令执行完毕。loadMemoryBarrier();assert value == 10;
}

这样现在确实安全了。

参考:
每个程序员都应该了解的 CPU 高速缓存
What every programmer should know about memory
CPU缓存一致性协议MESI
多线程之:MESI-CPU缓存一致性协议

多核CPU缓存一致性协议MESI相关推荐

  1. CPU缓存一致性协议MESI - 笔记

    CPU缓存一致性协议MESI CPU高速缓存(Cache Memory) CPU为何要有高速缓存 CPU在摩尔定律的指导下以每18个月翻一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU.这就造 ...

  2. CPU 缓存一致性协议 MESI

    CPU 高速缓存(Cache Memory) CPU 为何要有高速缓存 CPU 在摩尔定律的指导下以每 18 个月翻一番的速度在发展,然而内存和硬盘的发展速度远远不及 CPU.这就造成了高性能能的内存 ...

  3. CPU缓存一致性协议MESI

    吹剑出自<庄子>:"夫吹管也,犹有也:吹剑首者,而已矣." 吹剑只能发出小声,以示自谦."并发吹剑录",表达的是笔者斗胆讲一些并发编程有关的知识,由 ...

  4. CPU缓存一致性协议

                                                  一.CPU的一些基本组成 在提到CPU缓存这个问题前,先来回顾一下CPU的组成部分,及其作用.早期CPU由运 ...

  5. 12 张图看懂 CPU 缓存一致性与 MESI 协议,真的一致吗?

    本文已收录到  GitHub · AndroidFamily,有 Android 进阶知识体系,欢迎 Star.技术和职场问题,请关注公众号 [彭旭锐] 进 Android 面试交流群. 前言 大家好 ...

  6. 同时存多个变量缓存 微信小程序_CPU缓存一致性协议MESI,memory barrier和java volatile...

    MESI协议 MESI协议是一个被广泛使用的CPU缓存一致性协议.我们都知道在CPU中存在着多级缓存,缓存级别越低,容量就越小,速度也越快.有了缓存,CPU就不需要每次都向主存读写数据,这提高了CPU ...

  7. 科普:CPU缓存一致性协议

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 CPU为何要有高速缓存 CPU在摩尔定律的指导下以每18个月翻一番的速度在 ...

  8. CPU处理器一致性协议MESI详解

    CPU处理器缓存一致性协议MESI详解 缓存一致性的由来 MESI协议简介 Exclusive状态 Shared状态 Modified和Invalid状态 MESI状态切换 Modified状态跳转 ...

  9. CPU缓存一致性协议:MESI

    今天在看<架构解密>的时候,看到一段介绍CPU缓存一致性的介绍,文章详细解析了Intel多核处理器是如何解决数据一致性问题的,这本书在2020年看过一遍,现在又拿出来学习,感觉要学习的知识 ...

最新文章

  1. java 工厂模式的写法_[java设计模式] 工厂模式解析
  2. java 容器的嵌套_java界面设计里怎么实现容器嵌套
  3. freemarker的${!}
  4. CListCtrl 使用技巧
  5. 学计算机用华硕电脑,请问华硕笔记本电脑什么型号比较好用,就商务办公?
  6. Keil MDK-ARM下载 安装与和谐教程
  7. Spring5的Web 组件
  8. 服务器c的环境配置文件,配置linux服务器环境(jdk+tomcat+mysql+nginx+redis+svn+nexus的maven私服)...
  9. 戴琼海:人工智能的几点思考
  10. 一个简单的PHP购物车系统
  11. oracle 视图及函数授权,Oracle常见1000问之内部函数及管理视图
  12. 红帽 安装oracle11g,64位RedHat 5.6下安装Oracle 11g
  13. 使用ADO的通用DAL
  14. python之路day04--列表的增删改查,嵌套、元组的嵌套、range、for循环嵌套
  15. 在函数中如何获取 线程对象、线程唯一ID
  16. AI学习教程:AI(Adobe lliustrator)快速入门
  17. 双拼输入法的学习与使用
  18. 详解ArcGIS Server瓦片合并
  19. 电子电路中,PVDD、CVDD、VPP、 VDD、 AVDD、VCC分别是指什么电源?各起什么作用?
  20. C# socket通信 接收缓冲区大小设置,以及粘包问题的解决

热门文章

  1. JavaScript和Java的区别
  2. web学习笔记1--HTML
  3. curl记录响应时间
  4. 安卓学习笔记:使用PopupWindow创建简单菜单
  5. QTP的那些事--XPath的重要使用
  6. 将SSM架构中原来关于springSecurity3.x版本的写法配迁移到SpringBoot2.0框架中出现的问题解决记...
  7. linux下共享库的制作及常见的问题
  8. 广州运营开放式数据交易平台发力大数据业务
  9. JAVA 通过串口发送命令
  10. 远程登陆时,页面登陆不了,提示“user profile serveice服务未能登陆”