CPU 高速缓存(Cache Memory)

CPU 为何要有高速缓存

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

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

时间局部性(Temporal Locality):如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。

比如循环、递归、方法的反复调用等。

空间局部性(Spatial Locality):如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。

比如顺序执行的代码、连续创建的两个对象、数组等。

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

  1. 程序以及数据被加载到主内存

  2. 指令和数据被加载到 CPU 的高速缓存

  3. CPU 执行指令,把结果写到高速缓存

  4. 高速缓存中的数据写回主内存

目前流行的多级缓存结构

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

多级缓存结构

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

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

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

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 为相同。

上图的切换解释:

状态 触发本地读取 触发本地写入 触发远端读取 触发远端写入
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 操作与它无关

下图示意了,当一个 cache line 的调整的状态的时候,另外一个 cache line 需要调整的状态。

M E S I
M × × ×
E × × ×
S × ×
I

举个栗子来说:

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

多核缓存协同操作

假设有三个 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 状态(共享)。

MESI 优化和他们引入的问题

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

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 会读到跟程序中写入的顺序不一样的结果。

~顺便提一下 NIO 的设计和 Store Bufferes 的设计是非常相像的。~

硬件内存模型

执行失效也不是一个简单的操作,它需要处理器去处理。另外,存储缓存(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;
}
引用文章

http://www.importnew.com/10589.html
https://www.cnblogs.com/yanlong300/p/8986041.html

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

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

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

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

    在计算机系统中,CPU高速缓存(英语:CPU Cache)是用于减少处理器访问内存所需平均时间的部件.在金字塔式存储体系中它位于自顶向下的第二层,仅次于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. 利用WSS搭建学生作业平台
  2. php中文网企业网站,闻名 PHP企业网站系统 weenCompany v5.3.0 简体中文 UTF8
  3. Rosenbrock函数到底什么用
  4. 360 linux 扩展文件夹,360签具名工Linux下载0907 官方版
  5. java 8 排序_一遍记住 8 种排序算法与 Java 代码实现
  6. 怎么用计算机画正弦函数图像,几何画板如何绘制正弦函数图象
  7. Oracle中NVL2 和NULLIF的用法
  8. 招聘| 基因组所Yuwen Liu团队诚聘科研人员
  9. linux gdb模式下无反应,Linux,GDB 嵌入式Linux的GDB远程调试的问题--断点没反应
  10. 查看当前Linux系统的发行版本命令详解
  11. angular cannot get /
  12. 灵格斯与word2007或2010冲突,复制时word关闭问题的解决
  13. JDK和JRE的区别
  14. Educational Codeforces Round 95 (Rated for Div. 2)C. Mortal Kombat Tower(状态机dp)
  15. 生成自定义文字的二维码
  16. flask+python 实时视频流输出到前台
  17. vue watch监听中 immediate, deep, hander的作用
  18. 网络推广方式有哪些,网络推广哪个平台好?
  19. OpenJudge Tian Ji -- The Horse Racing
  20. 【Proteus】Proteus里的蜂鸣器的使用

热门文章

  1. 将SPS中被FrontPage修改过的页面重置为Ghost Page
  2. zip、rar文件格式
  3. linux c 获取文件数量
  4. Linux串口编程-转
  5. 蓝桥杯 ALGO-129 算法训练 特殊的数字四十
  6. Docker Image执行流程
  7. 用SignalR 2.0开发客服系统[系列5:使用SignalR的中文简体语言包和其他技术点]
  8. Linux笔记(df命令,du命令,磁盘分区)
  9. SEO学习步骤--SEO入门第一章
  10. 5.2 2.5-Dimensional Impostors for realistic trees and forests笔记