计算机内存中的缓存Cache Memories

这篇写一下计算机系统中的缓存Cache应用场景和实现方式介绍。

Memory hierarchy

在讲缓存之前,首先要了解计算机中的内存结构层次Memory hierarchy。也就是下图金字塔形状的结构。

从上到下,内存层次结构如下:

  1. 寄存器:这是计算机中最快速的存储区域。它们位于处理器内,用于存储即将被处理器执行的指令和数据。

  2. 高速缓存(Cache):位于处理器和主内存之间,用于存储最近或频繁访问的数据和指令。高速缓存有多级(L1、L2、L3),其中L1最接近处理器且速度最快,但也最小。

  3. 主内存(RAM):当计算机运行程序时,程序的代码和数据被加载到主内存中。

  4. 硬盘驱动器(HDD)或固态硬盘(SSD):这些是非易失性的存储设备,用于长期存储数据和程序。

  5. 网络存储和云存储:这些存储设备位于本地计算机之外,数据通常通过网络进行访问。

从上到下离CPU就越来越远,越往下的部分,容量往往越大,价格也越便宜;而越靠近CPU的部分,速度越快但是价格高且容量小。

Locality

局部性原理 (Locality of Reference) 是设计内存层次结构时需要考虑的重要因素,也是后面缓存为什么能够起作用的原因。

  • 时间局部性(Temporal Locality):如果一个数据或指令在某个时间点被访问,那么在未来的一段时间内,这个数据或指令可能会被再次访问。这是由于程序的执行往往具有重复性,例如循环和递归。高速缓存就是基于时间局部性设计的,将最近访问过的数据和指令存储在快速访问的缓存中。

  • 空间局部性(Spatial Locality):如果一个数据或指令被访问,那么在未来的一段时间内,其附近的数据或指令也可能会被访问。这是由于程序的执行通常具有连续性,例如顺序执行的指令和数组的元素。内存管理系统会将整个块(包含了访问数据或指令的附近地址)加载到高速缓存或主内存中,以利用空间局部性。

缓存Cache

如果我们把用过地址放在CPU里的存储单元,或者说一个更接近CPU、更能快速获取的地方,就有了我们广义概念上的“Cache”了。虽然这个地方很小,但基于locality的原理,程序倾向于使用之前的地址或者之前地址附近地址,每次CPU像访问数据,它都会优先从cache里查找(因为离得更近),如果是那种多次反复需要的数据,就可以直接让cache来提前处理了,提高数据获取速度。

当然,毕竟cache的位置有限,如果请求的数据没有在cache里(这叫做cache miss),就只能把cache中的数据删掉(比如最久没用的那个),然后把新数据从下面更慢的内存结构中获取后替换上去。

几种Cache miss

Cache miss主要有以下三种:

  1. 冷(强制)未命中(Cold or Compulsory Miss)
    冷未命中是因为缓存开始时是空的,而这是对该块的第一次引用。换句话说,这是无法避免的未命中,因为当你第一次访问一个数据块时,它不可能已经在缓存中。

  2. 容量未命中(Capacity Miss)
    当活动的缓存块集合(工作集)大于缓存的容量时,就会发生容量未命中。即使数据块之前已在缓存中,但由于缓存空间有限,可能已经被其他更近期访问的数据块替换出去。

  3. 冲突未命中(Conflict Miss)
    前两个都比较好理解,这个冲突未命中比较复杂,我用通俗的语言讲大概是这样:比如我们教室有三排学生,每排都有一个椅子当作cache,那么我们cache一共有3个,学生有3排。这个时候如果规定每排学生只能用对应的一把椅子,就会发生conflict miss。比如第一排第一个同学有了一个行为,他被存储在了第一个椅子上;这个时候第一排第二个同学又有一个行为,我们只会用第二个同学去换下第一个同学——即使这个时候还有两把椅子是空的。当最后再次访问第一个同学时,你会发现明明第一个同学之前访问过,明明cache里有空位,但就是在cache里找不到他,这种情况下的miss就叫做conflict miss。

Cache的参数和大小表示

高速缓存(Cache)的总大小可以由以下三个参数描述:

  • S:缓存的集数(Set)。
  • E:每个集中的线数(Line),也就是每个集中的缓存块数量(Cache blocks per set)。
  • B:每个缓存块的大小(Size of each block)。

而Cache set就等于 S × E × B。

 Cache的结构看起来这么麻烦,如何存储数据呢?对于一个数据,如cache会根据它的地址来划分和存储。

如上图所示,通常地址会被划分成3部分:块偏移量(block offset)、集索引(set index)和标签(tag)。

  • 块偏移量(Block Offset):这部分的位数取决于每个cache块(或行)的大小。例如,如果一个块的大小是16字节,那么块偏移量就需要4位(因为2^4 = 16),用于确定一个字节在其块中的位置。

  • 集索引(Set Index):这部分的位数取决于cache的集数。例如,如果有64个集,集索引就需要6位(因为2^6 = 64),用于确定一个块应该存储在哪个集中。

  • 标签(Tag):地址中剩下的位被用作标签,用于在cache查找过程中区分不同的内存块。

所以对于一个地址,大致的查找流程是这样的:首先进行地址分割,就像上面说的那样分成三部分;其次拿着集索引去cache中找到对应的集,拿到了这个集(可以理解成图里的一整行蓝色背景,包含很多line),我们查找所有line(通常会并行查找来提高速度),找到那个line,which有效位(valid bit)是1以及tag标签和地址划分出来的tag部分一样,如果找到了,则使用块偏移量从这个集中取出所需的数据。

再举个例子,在上面这个图中, block块大小是8,因此我们需要3个位作为block offset,这里offset是100也就是4,那么数据到时候会从第4位开始,也就是图中绿色块部分;集的个数不知道,集索引的位数也不确定,但这里0...01不管中间几个0,都是1,表示对应第一个集。在剩下的部分就是两个红色部分的tag比较了。

用这句话来检查一下你是否理解:这里虽然得到的块偏移量是4,但是你可以发现我们把4往后的部分也都放在cache里了(绿色部分)。因为这样的话如果下次访问这个同样的地址+1,按找那套流程算下来其实直接就对应块偏移量为5的部分,直接就在cache里了!这就是cache对Spatial Locality也友好的地方——不止是之前访问过的我有,之前访问过的邻居我也有!

Cache的写操作

当我们讨论写操作(write operations)在缓存系统中的行为时,我们需要考虑两种基本的情况:写命中(write hit)和写未命中(write miss)。在处理这两种情况时,有几种常见的策略:

  • 1 写命中(Write Hit):当我们试图写入的数据已在缓存中时,我们有两种基本策略:

    • 写直达(Write-Through):这种策略立即将更改写入到主存储器和缓存中。这种策略的优点是它保持了主存储器和缓存中的数据一致性,但缺点是每次写操作都需要访问主存储器,这可能会带来较大的性能开销。

    • 写回(Write-Back):这种策略仅将更改写入到缓存中,并将缓存行标记为"dirty"(通过设置一个"dirty bit")。只有当缓存行被替换出缓存时,更改才会被写回到主存储器。这里的dirty bit就是替换时用来判断的,如果是1,那么就需要把整个缓存行(包含2^b字节的数据块)写回(write-back)到主内存。这种策略的优点是减少了对主存储器的访问次数,从而提高了性能。但是,这也可能会导致主存储器与缓存之间的数据不一致。

  • 2 写未命中(Write Miss):当我们试图写入的数据不在缓存中时,我们有两种基本策略:

    • 不写分配(No-Write-Allocate):这种策略直接将数据写入主存储器,而不将其加载到缓存中。这种策略适用于不希望单次写操作污染缓存的情况。

    • 写分配(Write-Allocate):这种策略在写入数据之前,先将相关的缓存行加载到缓存中,再将新的写操作应用到这个缓存行。如果预计将来会有更多对同一位置的写操作,这种策略可能会很有用。

在实际的系统中,可能会组合使用这些策略。比如,一种常见的组合是使用写直达和不写分配策略,这种组合可以保持数据的一致性,而且适合处理散列的、非连续的写操作。另一种常见的组合是使用写回和写分配策略,这种组合可以减少对主存储器的访问次数,从而提高性能,尤其是在处理连续的、集中的写操作时。

小结

这篇文章写了下cache的概念以及读写过程中的读取策略。cache的缓存命中是非常有用和关键的,可以为程序或许数据节省下非常多时间。一个有趣的观点是,99%的命中率可能比97%的命中率好两倍。比如假设缓存命中的时间为1个周期,未命中的惩罚为100个周期。那么:

  • 对于97%的命中率,平均访问时间为:1个周期(命中时间) + 0.03(未命中率) * 100个周期(未命中惩罚) = 4个周期。
  • 对于99%的命中率,平均访问时间为:1个周期(命中时间) + 0.01(未命中率) * 100个周期(未命中惩罚) = 2个周期。

因此,尽管两者的命中率只相差2%,但是平均访问时间却差了一倍。

所以对于程序员来说,尽量写出“缓存友好”的代码也是能很好提升程序的效率。通过理解缓存的工作方式,我们可以编写出更有效地利用缓存的代码,一些常见方式有:

  • 重复引用变量:这是好的(利用时间局部性Temporal Locality):如果一个变量被反复引用,它可能会留在缓存中,这样每次引用都会命中缓存,从而提高性能。

  • 使用跨度为1的引用模式:这是好的(利用空间局部性Spatial Locality)。如果你的代码按顺序访问数据(例如,遍历数组),那么缓存系统可能会预先加载你即将访问的数据,从而提高缓存命中率。这就是为什么我们一行一行地遍历二维数组要比一列一列地遍历快很多,因为数组在内存中是按行存储,缓存可以帮助我们提前加载到接下来的数据。

结束!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FPGA_学习_13_方差计算小模块

测距器件APD的性能与器件本身的温度、施加在APD的偏置电压息息相关。 在不同的温度下,APD的偏压对测距性能的影响非常大。 要确定一个合适的APD的偏压Vopt,首先你要知道当前温度下,APD的击穿电压Vbr,一般来讲,Vopt Vb…

桥梁安全生命周期监测解决方案

一、方案背景 建筑安全是人们生产、经营、居住等经济生活和人身安全的基本保证,目前我国越来越多的建筑物逐 步接近或者已经达到了使用年限,使得建筑物不断出现各种安全隐患,对居民的人身安全和财产安全产 生不利影响,因此房…

gitee 配置ssh 公钥(私钥)

步骤1:添加/生成SSH公钥,码云提供了基于SSH协议的Git服务,在使用SSH协议访问项目仓库之前,需要先配置好账户/项目的SSH公钥。 绑定账户邮箱: git config --global user.name "Your Name" git config --glob…

看了2023年的一线互联网公司时薪排行榜!值得思考

前言 根据最近针对国内的一线互联网企业做的调研,汇总了他们的平均时薪水平,最终出了一个排行榜! 首先我们来看下,排行榜分哪几个Level,分别为初级、中级、高级、资深、专家/架构这五个,主要根据工程师的…

opencv对相机进行畸变矫正,及矫正前后的坐标对应

文章目录 1.背景2.需求分析3.解决方案3.1.镜头畸变矫正3.2.知道矫正后的画面坐标(x,y),求其在原画面的坐标(x,y)3.2.知道原画面坐标(x1,y1),求其在…

fastadmin框架重定向

由于&#xff0c;我们一打开fastadmin框架就进入到前端页面很麻烦&#xff0c;下面这种方法可以解决这个问题。 首先我们找到这个路径 找到重定向&#xff0c; application》index》controller》index 原本文件是这个样子&#xff1a; <?phpnamespace app\index\controll…

【ArcGIS Pro二次开发】(53):村规制表、制图【福建省】

这篇算是村规入库的一个延续。 村庄规划中有一些图纸是需要严格按照规范制图&#xff0c;或形成一定规范格式的。 这些图纸的制作基本算是机械式的工作&#xff0c;可以用工具来代替人工。 一、要实现的功能 如上图所示&#xff0c;在【村庄规划】组&#xff0c;新增了两个工…

配置代理——解决跨域问题(详解)

之前写项目的时候总会遇到配置代理的问题&#xff0c;可是配置了之后有时有用&#xff0c;有时就没有用&#xff0c;自己之前学的也是懵懵懂懂&#xff0c;于是专门花了一个小时去了解了如何配置代理跨域&#xff0c;然后在此记录一下&#xff0c;方便自己以后查阅。 一、 常用…

pytorch实现梯度下降算法例子

如题&#xff0c;利用pytorch&#xff0c;通过代码实现机器学习中的梯度下降算法&#xff0c;求解如下方程&#xff1a; f ′ ( x , y ) x 2 20 y 2 {f}(x,y) x^2 20 y^2 f′(x,y)x220y2 的最小值。 Latex语法参考&#xff1a;https://blog.csdn.net/ViatorSun/article/d…

推荐系统(十)用户行为序列建模-Pooling 路线

对推荐系统而言&#xff0c;准确捕捉用户兴趣是其面临的核心命题。不管是样本、特征还是模型结构等方面的优化&#xff0c;本质上做的事情都是在提高推荐系统对用户兴趣的捕捉能力&#xff0c;因此如何提高这种能力&#xff0c;对推荐效果的提升有重要作用&#xff0c;也是算法…

tp6 实现excel 导入功能

在项目根目录安装 composer require phpoffice/phpspreadsheet 我们看一下郊果图&#xff0c;如下 点击导入excel表格数据 出现弹窗选择文件&#xff0c;控制台打开输出文档内容 前端layui代码 <form id"uploadForm" class"form-horizontal" encty…

7.25 Qt

制作一个登陆界面 login.pro文件 QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c11# The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on …

如何高效地查询IP归属地

高效识别IP归属地是网络安全领域中的一项重要工作。准确地识别IP的归属地不仅可以帮助网络管理员追踪和定位潜在的网络攻击者&#xff0c;还可以用于网络流量分析、地理定位服务等方面。 以下将介绍几种高效识别IP归属地的方法。 使用IP归属地数据库 IP归属地数据库是一种存储…

Clion开发STM32之W5500系列(综合实验)

说明 此为w5500模块的综合实验测试模块,包含dhcp、dns、ntp以上三个模块的驱动参考之前的文章&#xff0c;本篇不做说明.使用的开发芯片 stm32f103vet6系列,外设接口使用的spi2 实验内容: 通过dhcp动态获取ip,通过dns解析NTP服务域名的ip通过NTP服务ip获取时间 w5500配置驱…

国密SSL优势及应用场景

国密SSL的优势主要有以下几点&#xff1a; 更高的安全性&#xff1a;国密算法采用的是国家密码管理局推荐的算法&#xff0c;相对于传统的SSL协议更加安全。 更好的性能&#xff1a;国密算法是国家密码管理局推荐的算法&#xff0c;其加密效率与密钥长度相比传统算法更高。 更…

微服务架构演变

微服务架构筑基 软件架构演进 软件架构的发展经历了从单体结构、垂直架构、SOA架构到微服务架构的过程. 什么是微服务&#xff1f;&#xff1f; Spring Cloud Netfilx Spring Cloud Alibaba service Mesh 架构的发展&#xff1a;基于某一种因素 技术是服务于业务的 业务又是…

Vue mixin 混入

可以复用的组件&#xff0c;我们一般会抽离&#xff0c;写成公共的模块。 可以复用的方法&#xff0c;我们一般会抽离&#xff0c;写成公共的函数。 那么 在 Vue 中&#xff0c;如果 某几个组件实例 VueComponent 中、或者 整个 Vue 项目中 都存在相同的配置&#xff0c;那就…

数据结构(二)

目录 Trie树 并查集 堆 Trie树 作用:用来高效地存储和查找字符串集合的数据结构 基本形式: 模板代码如下: #include<iostream> using namespace std;const int N 100010;//idx代表当前用到哪个下标 //既是根节点&#xff0c;又是空节点 //cnt存储的是以当前点结尾的…

Mac 系统钥匙串证书不受信任

Mac 系统钥匙串证书不受信任 解决办法 通过尝试安装 Apple PKI 的 Worldwide Developer Relations - G4 (Expiring 12/10/2030 00:00:00 UTC) 解决该异常问题 以上便是此次分享的全部内容&#xff0c;希望能对大家有所帮助!

移动端商品详情页设计

效果图 代码如下 页面设计 <div class"container"><!--商品详情 start--><van-image class"goods-item-image" :src"goods.goodsHeadImg"></van-image><div class"goods-price">&#xffe5;<span&…