计算机内存中的缓存Cache Memories

news2024/11/25 19:55:15

这篇写一下计算机系统中的缓存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)。如果你的代码按顺序访问数据(例如,遍历数组),那么缓存系统可能会预先加载你即将访问的数据,从而提高缓存命中率。这就是为什么我们一行一行地遍历二维数组要比一列一列地遍历快很多,因为数组在内存中是按行存储,缓存可以帮助我们提前加载到接下来的数据。

结束!

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

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

相关文章

自动化测试与手工测试比较

既然现在有了自动化测试,甚至现在许多团队在使用人工智能的方法,逐渐让机器来取代人的测试。那么作为测试工程师的人未来会不会消失?这是一个摆在许多人面前的一个严肃的问题。去年刚刚过世的著名的天体物理学家斯蒂芬威廉霍金(Stephen William Hawking…

Qt - macOS 安装配置

文章目录 一、关于 QT1.2 Qt的发展史1.3支持的平台1.4 Qt版本1.5 Qt 的优点1.6 成功案例 二、软件安装1、保证已 Xcode 和 Command Line Tools2、下载 QT3、下载 [qtcreator](http://download.qt.io/official_releases/qtcreator/)查看qt版本 三、创建工程Qt 常见用法 四、基础…

智能安全配电装置应用场景有哪些?

安科瑞 华楠 一、应用背景 电力作为一种清洁能源,给人们带来了舒适、便捷的电气化生活。与此同时,由于使用不当,维护不及时等原因引发的漏电触电和电气火灾事故,也给人们的生命和财产带来了巨大的威胁和损失。 为了防止低压配电…

音频转换工具怎么使用你了解吗?让我来跟你分享背后原理

相信小伙伴们平时都会听听喜欢的音乐放松心情吧,不过你是否遇到过,想在一个播放设备上放一首歌的时候,却发现不支持该格式?音频转换格式软件在我们的生活中扮演着越来越重要的角色。这些软件可以帮助我们将音频文件转换成不同的格…

炒股最好用的5个指标?_通达信公式

摘要: 炒股市场是一个充满挑战的领域,而找到可靠的指标来辅助投资决策是成功的关键之一。在众多的指标中,神奇指标网提供了五个被广泛认为是炒股最好用的指标。本文将详细介绍这五个指标,包括其原理和如何应用它们来辅助投资决策。 导言: …

蓝桥杯专题-真题版含答案-【图形排版】【包子凑数】【位反序数】【自守数】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 👉关于作者 专注于Android/Unity和各种游…

Java进阶 —— 多进程并发

前言 在系统学完Java的面向对象编程之后,我们需要认真地来学习Java并发编程,我们在学习计算机操作系统的时候也都了解过进程、线程和协程的概念。在这篇文章中荔枝主要会梳理有关线程创建、线程生命周期、同步锁和死锁、线程通信和线程池的知识&#xff…

android app控制ros机器人一

android开发app,进而通过控制ros机器人,记录开发过程 查阅资料: rosjava使用较多,已经开发好的app也有开源的案例 rosjava GitHub https://github.com/ros-autom/RobotCA https://github.com/ROS-Mobile/ROS-Mobile-Android…

因果推断(一)合成控制法(SCM)

因果推断(一)合成控制法(SCM) 在互联网时代,产品迭代速度越来越快,营销活动也越来越多。分析师因此需要快速的量化每次迭代或每次营销的效果,探索改变与结果之间的因果关系,并将优秀…

idea如何解决导入的项目不是Maven工程(文件下面没有蓝色的方格)二

简介: Maven项目导入,idea不识别项目 解决方法: 选中pom.xml -- 右键 -- Add as Maven Project

使用Python搭建代理服务器- 爬虫代理服务器详细指南

搭建一个Python爬虫代理服务器可以让你更方便地管理和使用代理IP。下面是一个详细的教程来帮助你搭建一个简单的Python爬虫代理服务器: 1. 首先,确保你已经安装了Python。你可以在官方网站(https://www.python.org/)下载并安装最新版本的Python。 2. 安…

Spring 中简单存取 Bean 的相关注解

目录 前言存储 Bean 对象五大类注解方法注解(Bean) 获取 Bean 对象 (Autowired)属性注入多个同类型 Bean 注入怎么办? Setter 注入构造方法注入(官方推荐) 前言 之前我们存储获取 Bean 的操作很繁琐,需要将…

在职硕士|2023级中国社科院-美国杜兰大学合办双证能源管理硕士(MME)

金融硕士 在职硕士|2023级中国社科院-美国杜兰大学合办双证能源管理硕士(MME) 中国社会科学院大学与美国杜兰大学合作举办的能源管理专业硕士学位教育项目(UCASS-Tulane Master of Management in Energy,简称MME)于2…

《人工智能安全》课程总体结构

1 课程内容 人工智能安全观:人工智能安全问题、安全属性、技术体系等基本问题进行了归纳整理。人工智能安全的主要数据处理方法,即非平衡数据分类、噪声数据处理和小样本学习。人工智能技术赋能网络空间安全攻击与防御:三个典型实例及攻击图…

mybatis_配置之属性优化

概念 别名优化: 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如: 在xml文件里为SQL映射文件中定义返回值类型的属性起个别名 之后直接使用User进行使用 核心配置文件: MyBa…

常见面试题分享1

一、对JVM的了解 1.1 什么是JVM? JVM(Java Virtual Machine),俗称Java虚拟机。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java语言的一个非常重要的特点就是与平台的无关性。…

商城体系之产商品系统

本文主要讲解商城体系下产商品系统的设计。商城系统可以拆分成多个业务中台和多个应用服务。 1、产商品系统业务架构 产商品系统作为商城重要的基础信息组成部分,主要划分为产品信息和商品信息,产品信息保持最原始的产品基础属性和内容,商品…

下拉框可筛选可树状多选组件

实际效果图片 父页面 <el-form-item label"转发&#xff1a;" :label-width"formLabelWidth" class"formflex_item"><el-select ref"select" :clearable"true" clear"clearSelect" remove-tag"r…

day2 驱动开发 c语言

通过驱动开发给pcb板子点灯。 u-boot已经提前移植到了emmc中。 灯也是一种字符型设备。 编程流程需要先注册设备&#xff0c;然后创建结点&#xff0c;然后操作电灯相关寄存器 应用层直接调用read write来打开字符设备进行操作。 这样写会造成无法处理内核页面请求的虚拟地址…

SpringBoot中java操作excel【EasyExcel】

EasyExcel 处理Excel&#xff1b;简单记录&#xff0c;方便日后查询&#xff01; 官方文档&#xff1a; Easy Excel (alibaba.com) 一、EasyExcel概述 Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存&#xff0c;poi有一套…