CPU缓存一致性原理

news2024/10/6 22:21:12

CPU缓存一致性原理

在本站的文章CPU缓存那些事儿中, 介绍了cpu的多级缓存的架构和cpu缓存行cache line的结构。CPU对于缓存的操作包含读和写,读操作在cache line中有所涉及,在本文中,将重点讨论CPU对于缓存进行写时的行为。

单核CPU对高速缓存的读操作

CPU对于高速缓存的读操作的过程在之前的文章中有提到过,这里梳理一下其流程:

  • 1.首先对于一个内存地址,CPU会按照索引规则(直接映射/多路组相连/全相连)优先去cache line中进行检索。
  • 2.如果检索到了,意味着该内存地址的内容已经存在于cache中,则直接读取内容到CPU中,流程结束。如果没有检索到,进行步骤3。
  • 3.此时确认内存的数据不在cache line中,如果cache已经存满或者已经被其他内存地址映射,则进入步骤4,如果cache line中还有空位,则进入步骤5.
  • 4.执行缓存淘汰策略,腾出位置
  • 5.加载内存数据到cache line中,将cache块上的内容读取到cpu中。

对于单核CPU, 对cache的读操作的流程如下所示:

single-cpu-read

单核CPU对于高速缓存的写操作

CPU对于高速缓存的写操作比读操作要复杂一些,写操作会修改cache中的数据,而这一数据最终需要同步到内存中,在同步到内存这个点上,写操作的策略可以分为写直达策略写回策略。注意这里的讨论仍然是针对单核CPU而言的。

写直达策略(Write-Through)

写直达策略的思想是对于写入操作,同时修改cache中的数据内存中的数据,使得cache和内存中的数据保持一致。这将使得cache和内存拥有数据的强一致性

写直达策略中写数据的流程可以被细化为下列的步骤:

  • 1.判断待写入的数据是否已经存在于cache中。如写入的数据已经存在于cache中,则跳转到步骤2,否则跳转到步骤3。
  • 2.将数据写入cache中。
  • 3.将数据写入内存。

而写直达策略中读数据的流程并没有任何改变,因为cache和内存的数据是强一致的。

写直达策略的流程概括为如下所示:

single-cpu-read

对于写直达策略,读cache的流程并没有任何改变,这一点和写回策略是有区别的。

写直达的优缺点如下:

优点:对于写操作而言,可以保证内存和高速缓存内容的强一致性

缺点:由于每次写入操作都需要将数据写入内存,使得写操作的耗时增加,失去了高速缓存的高效性

写回策略(Write-Back)

写直达策略的思想是当需要修改cache时,延迟修改内存。在每个cache line上增加了Dirty的标记,当修改了cache中的内容时,会将Dirty标记置位,表明它的数据和内存数据不一致。注意此时,并不会立即写回内存。只有当cache块被替换出去时,才会回写到内存中。这其实是一种异步的思想,其相较于写直达也要复杂一些。写回策略实现的是一种最终一致性

写回策略是由读和写配合完成的。

对于写回策略中的写操作,其流程如下:

  • 1.首先判断写操作的地址是否存在于cache中,如存在,进入步骤6,否则进入步骤2。
  • 2.如果写操作的地址不在cache中,则尝试读取内存数据到cache中。这里需要判断cache此时是否已经满或者被占用。如果已经满或者被占用,则进入步骤3,否则进入步骤5。
  • 3.如果cache此时是否已经满或者被占用,则使用替换策略挪出空位。此时还需要判断被替换出的cache line是否为脏数据,如果是脏数据,则进入步骤4,否则进入步骤5
  • 4.将被替换出的cache line的数据写回内存中。
  • 5.读取内存块的数据到cache line中。
  • 6.将数据写入cache中
  • 7.将cache line标记为脏数据。

由于替换策略也可能发生在读操作中,因此对于写回策略中的读操作也发生了修改,其流程如下所示:

  • 1.首先对于一个内存地址,CPU会按照索引规则(直接映射/多路组相连/全相连)优先去cache line中进行检索。
  • 2.如果检索到了,意味着该内存地址的内容已经存在于cache中,则直接读取内容到CPU中,流程结束。如果没有检索到,进行步骤3。
  • 3.此时确认内存的数据不在cache line中,如果cache已经存满或者已经被其他内存地址映射,则进入步骤4,如果cache line中还有空位,则进入步骤6.
  • 4.执行缓存淘汰策略,腾出位置,此时需要判断被替换出的cache line是否为脏数据,如果是,则进入步骤5,否则进入步骤6.
  • 5.将脏数据写回到内存中。
  • 6.加载内存数据到cache line中,将cache块上的内容读取到cpu中。

写回策略的读写操作的流程如下所示:

single_cpu_write_back

多CPU核的缓存一致性问题。

上面的写直达策略写回策略可以解决单核CPU的cache和内存的一致性问题。而现代的CPU往往都是多核CPU,每个核心都拥有其自己的cache。那么对于写操作而言,除了保证本CPU的cache和内存的一致性,还需要保证其它CPU的cache和内存的一致性问题。

MESI协议就是用来解决这个问题的。

MESI协议是一种用于保证缓存一致性的协议,其对应了CPU cache的四种状态,每个字母代表了一种状态,其含义如下:

  • M(Modified,已修改): 表明 Cache 块被修改过,但未同步回内存;
  • E(Exclusive,独占): 表明 Cache 块被当前核心独占,而其它核心的同一个 Cache 块会失效;
  • S(Shared,共享): 表明 Cache 块被多个核心持有且都是有效的;
  • I(Invalidated,已失效): 表明 Cache 块的数据是过时的。

MESI 协议其实是 CPU Cache 的有限状态机,其四种状态的转化如下图所示:

cache line

对于MESI协议,有一个可视化的网站可以演示其过程,网址:https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm。

下面我们借助该网站来理解MESI每个状态的变换过程。

独占状态E转已修改状态M E->M

如下图所示,此时cpu0上有a0的缓存,a0的缓存状态是独占状态E。

EtoM_1

此时cpu0执行write a0操作,由于其它cpu上没有a0数据,状态E转为状态M:

EtoM_2

已修改状态M转共享状态S,M->S

如下图所示,此时cpu0上有a0的缓存,a0的缓存状态是已修改状态M:

MtoS_1

随后cpu1上执行read a0的操作,则cpu 0上的a0从状态M转为状态S:

MtoS_2

共享状态S转已失效状态I, S->I

如下图所示,此时cpu0上有a0的缓存,其状态为S。除此以外,cpu1上也有a0的缓存。

StoI_1

此时cpu1上执行write a0的操作,则cpu0上的a0状态从S转为了I:

StoI_2

共享状态S转独占状态E, S->E

如下图所示,此时cpu0上有a0的缓存,其状态为S。除此以外,cpu1上也有a0的缓存。

StoE_1

此时cpu1上执行write a0的操作,则cpu1上的a0状态从S转为了E:

StoE_2

已失效状态I转独占状态E,I->E

状态I转状态E有两种路径,第一种是通过Processor Read, 此时a0数据仅存在于cpu0的cache中:

ItoE_1

cpu0执行read a0操作,a0状态从I转为了E:

ItoE_2

第二种是通过process write, 注意此时a0数据存在于cpu0和cpu1的cache中:

ItoE_3

cpu0执行write a0操作,a0状态从I转为了E:

ItoE_4

已失效状态I转共享状态S,I->S

此时cpu0上a0的状态是I, 而cpu1上a0的状态是E:

ItoS_1

此时cpu0执行read a0操作,cpu0上的a0从I转为S:

ItoS_2

独占状态E转已失效状态I,E->I

此时cpu0上的有a0的缓存,且状态为E,其它cpu上没有a0的缓存:

EtoI_1

此时cpu1执行write a0的操作,此时cpu0上a0的状态从E转为I:

EtoI_2

已修改状态E转共享状态S,E->S

如下图所示,此时cpu0的独占a0:

cache line

此时cpu1执行read a0的操作:

cache line

独占状态M转失效状态I, M->I

此时cpu0上拥有a0的缓存,且状态为M,其它cpu上没有a0的缓存:

cache line

此时cpu1执行write a0的操作,则cpu0上a0缓存的状态从独占(M)转为了失效(I):

cache line

已失效状态I转已修改状态M,I->M

此时cpu0上a0缓存的状态是I,其它cpu上没有a0的缓存:

ItoM_1

此时cpu0执行write a0, 则cpu0上的a0从I转为了E:

ItoM_2

写缓冲区和失效队列

写缓冲区和失效队列实际上是对MESI的优化策略。

写缓冲区(Store Buffer)

从上面的对于MESI的理解中,不难发现,MESI协议其实并不高效。例如当CPU1将要修改cache line时,需要广播RFO获得独占权,当收到其它cpu核的ACK之前,CPU1只能空等。 这对于CPU1而言,是一种资源的浪费。写缓冲区就是为了解决这个问题的。当CPU核需要写操作时,会将写操作放入缓冲区中,然后就可以执行其它指令了。当收到其它核心的ACK后,就可以将写入的内容写入cache中。

失效队列(Invalidation Queue)

写缓冲区是对写操作发送命令时的优化,而失效队列则是针对收写操作命令时的优化。

对于其它的CPU核心而言,在其收到RFO请求时,需要更新当前CPU的cache line状态,并回复ACK。然而在收到RFO请求时,CPU核心可能在处理其它的事情,不能及时回复

写缓冲区和失效队列将RFO请求的收发修改为了异步的,这实际上实现的是一种最终一致性。这也会引入新的问题,即CPU对于指令会有重排。如果有一些程序对于内存序有要求,那么就需要进行考虑。

考虑下面的场景,cpu0和cpu1分别执行下面的指令:

cpu0上指令

a = 1  //A1
x = b  //A2

cpu1上指令

b = 1 //B1
y = a //B2

MESI-issue

由于store-buffer的存在,尽管A1操作先于A2操作之前发生,但是A1操作完成时间可能晚于A2,如下表中反应的顺序:

顺序cpu0cpu1
0a=1写入store bufferb = 1写入store buffer
1x=b
2y = a
3b = 1操作完成
4a=1操作完成

尽管A1操作发生于B2之前,但是由于写操作的异步特性,当执行到y=a时,读取到的还是旧值。同理x=b也可能读取到旧的值。

对于这种cpu层面的指令重排问题,则需要内存屏障进行解决。

总结

  • 对于单核cpu,写操作需要考虑cache和内存的一致性,通常有写直达和写回两种策略。
  • 对于多核cpu,除了cache和内存的一致性需要保证,还需要保证每个cpu的cache的数据的一致性。
  • MESI协议就是一种解决多核cpu缓存一致性的算法
  • 为了改善MESI的效率,引入了store-buffer和Invalidation Queue,提升了效率,但是也引入了指令重排的问题。通常可以使用内存屏障解决。

参考文献

https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm
https://blog.csdn.net/weixin_46215617/article/details/115433851?share_token=0637ba7e-fc4b-4d21-8d6b-000301e7fe3e
https://juejin.cn/post/7158395475362578462
https://blog.51cto.com/qmiller/5285102

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/904419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jmap(Memory Map for Java)Java内存映像工具

jmap(Memory Map for Java)Java内存映像工具 jmap(Memory Map for Java)命令用于生成堆转储快照(一般称为heapdump或dump文件) 如果不使用jmap命令,要想获取Java堆转储快照也还有一些比较“暴…

Linux -- 进阶 利用大文件来增加分区 自动挂载大文件

情景引入 : 比如, 你的硬盘 分了三个区,但是,现在就是要求要分第四个区, 你一看硬盘没有剩余空 间了,分不出第四个区了,除非你再添加 一块儿 新硬盘。 那就可以使用我们介绍的这种方法 &…

MyBatis入门配置及CURD实现

目录 一、MyBatis简介 1. 什么是 MyBatis ? 2. MyBatis的特性 3. 什么是持久层框架? 二、MyBatis环境配置 2.1 创建maven工程 2.2 导入相关pom依赖 2.3 导入jdbc配置文件 2.4 Mybatis相关插件安装 3.5 Mybatis-cfg.xml 核心配置 2.6 引入Log4j2日志文件…

【前缀和】算法实战

文章目录 一、算法原理1. 一维前缀和2. 二维前缀和 二、算法实战1. leetcode560 和为K的子数组2. leetcode974 和可被K整除的子数组3. leetcode525 连续数组4. leetcode1314 矩阵区域和5. leetcode724 寻找数组的中心下标6. leetcode238 除自身以外数组的乘积 三、总结 一、算法…

基于Python的高校学生成绩分析系统

随着计算机技术发展,计算机系统的应用已延伸到社会的各个领域,大量基于网络的广泛应用给生活带来了十分的便利。所以把高校成绩分析与现在网络相结合,利用计算机搭建高校成绩分析系统,实现高校成绩分析的信息化。则对于进一步提高…

gdb调试core dump

gdb调试core dump 文章目录 gdb调试core dumpgdb core dump调试步骤Segmentation faultcore dump**coredump文件的存储位置**apport手动指定存储位置 开启coredump测试readelf 查看core dump文件信息gdb查看core文件总结Reference>>>>> 欢迎关注公众号【三戒纪元…

P17~P18 电路定理 电路中难得的精彩到极致的电路理论

特勒根定理、互易定理、对偶定理比较难,非常重要,因为他们可以解决其他定理无法解决的问题。 1、特勒根定理1——个人感觉像能量守恒 特勒根定理与基尔霍夫定理齐名,与拓扑结构有关。都适用于任何线性非线性,时变的非时变的元件…

使用zoom预览出图和系统相机预览出图,画质不一样的问题分析

1、问题背景 最近在基于 Android 的平台调试一款摄像头,客户有反馈一个问题,系统自带的 Camera2 app 预览出图是正常的,但用 Zoom app 打开摄像头,出图画面存在畸变、锯齿、过曝的问题,现象如下图所示。 2、问题分析 …

前端基础(Web API)

目录 前言 Web API DOM 基本概念 查找元素 document.getElementById document.getElementsByClassName document.evaluate() 修改元素 添加元素 修改元素 复制元素 删除元素 Event事件 事件创建 常用的事件 监听事件 click事件 mouseover事件 事件绑定…

LTPP在线开发平台【使用教程】

LTPP在线开发平台 点击访问 LTPP在线开发平台 LTPP(Learning teaching practice platform)在线开发平台是一个编程学习网站,该网站集文章学习、短视频、在线直播、代码训练、在线问答、在线聊天和在线商店于一体,专注于提升用户编…

使用SpringBoot+SpringMVC+Mybatis+WebSocket实现云聊天项目

云聊天 1. 项目介绍 本项目是仿照微信实现网页版聊天程序,用户注册登录后可与在线好友实时聊天,下线好友上线后可以查看到好友发送的消息;用户可以在搜索框搜索用户添加好友;用户还可以查看好友申请列表,选择是否同意…

Golang使用消息队列(RabbitMQ)

最近在使用Golang做了一个网盘项目(类似百度网盘),这个网盘项目有一个功能描述如下:用户会删除一个文件到垃圾回收站,回收站的文件有一个时间期限,比如24h,24h后数据库中记录和oss中文件会被删除…

使用Vscode 编辑器 导出、导入和运行Excel中的VBA代码

使用Vscode 编辑器 导出、导入和运行Excel中的VBA代码 前言 Excel自带的 Microsoft Visual Basic for Applications 编辑器常被人称为上古编辑器,的确不适合代码编辑,这是其一,其二是当系统语言与Excel的安装语言不一致时,往往出现…

QChart类用来 管理 图表的:数据序列(series)、图例(legend)和坐标轴(axis)

QChart类用来 管理 图表的:数据序列(series)、图例(legend)和坐标轴(axis) 1、数据序列类 继承关系 2、坐标轴类 的继承关系 3、图例类 什么是图例? 图例:是集中于地图…

Docker搭建LNMP运行Wordpress平台

一、项目1.1 项目环境1.2 服务器环境1.3 任务需求 二、Linux 系统基础镜像三、Nginx1、建立工作目录2、编写 Dockerfile 脚本3、准备 nginx.conf 配置文件4、生成镜像5、创建自定义网络6、启动镜像容器7、验证 nginx 四、Mysql1、建立工作目录2、编写 Dockerfile3、准备 my.cnf…

如何做H5性能测试?

提起H5性能测试,可能许多同学有所耳闻,但是不知道该如何对H5做性能测试,或者不知道H5应该关注哪些性能指标。今天我们就来看下,希望阅读本文后,能够有所了解。 常用指标 1、H5性能相关参数介绍 白屏时间:…

FRP内网穿透,配置本地电脑作为服务器

FRP内网穿透,配置本地电脑作为服务器 下载FRP服务端客户端 参考链接: https://www.it235.com/实用工具/内网穿透/pierce.html https://www.cnblogs.com/007sx/p/17469301.html 由于没有公网ip,所以尝试内网穿透将本地电脑作为服务器&#xff…

第 6 章 递归(1)(应用场景,概念,调用机制,解决问题类型,重要规则)

6.1递归应用场景 看个实际应用场景,迷宫问题(回溯), 递归(Recursion) 6.2递归的概念 简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。 6.3递归调用机制 我列举两个小案例,…

代码随想录算法训练营之JAVA|第三十四天|509. 斐波那契数

今天是第 天刷leetcode,立个flag,打卡60天,如果做不到,完成一件评论区点赞最高的挑战。 算法挑战链接 509. 斐波那契数https://leetcode.cn/problems/fibonacci-number/ 第一想法 这个就是求斐波那契数,感觉应该不用…

Error creating bean with name ‘esUtils‘ defined in file

报错异常: 背景: esUtils在common服务中、启动media服务时候、报这个异常、后排查esUtils在启动时候发生异常引起的、在相关bean中加入try{}catch{}即可解决问题 String[] split url.split(","); HttpHost[] httpHosts new HttpHost[split.…