理解计算机Cache

理解计算机Cache

如果我们仔细的查看芯片架构,就会发现里面都有缓存( MCU 可能没有缓存),可见缓存的重要性。本文将详细的介绍缓存的基础知识。

1. 什么是缓存

首先考虑一种标量访问的情况。就像下面这段代码,它重复地访问同一个标量。在这种情况下,存储访问会出现需要访问的地址不随时间变化的特点。

a = 10
for i in range(5):print(a)

如果我们考虑数组访问,比如顺序访问数组中的每个元素。那么存储访问会出现需要访问的地址随时间线性变化的特点。

观察这两种不同的访问情况,我们可以发现,内存访问并非是完全随机的,这背后体现的是局部性原理。局部性原理可以分为两类。一个是时间局部性,一个元素一旦被访问到,很可能在短时间内再次被访问到。另一个是空间局部性,一个元素周围的元素很有可能会接下来被访问到。局部性原理至关重要,确保了缓存能够有效运作。

接下来我们关注一个最基础的处理器内存模型。当处理器需要读取数据时,它通过一个特定的地址访问内存,内存响应这一请求并将数据返回。然而,存储器的一个有趣特性是它离处理器越远,容量通常越大,但速度却越慢。这种速度的差异在处理器和内存之间依然很明显。

为了解决速度差异带来的挑战,我们在处理器和内存之间引入了一个强大的中间者——缓存。当处理器寻找的数据恰好在缓存中时,就称为缓存命中。这时处理器可以迅速通过地址访问缓存中的数据。而如果所需数据不在缓存中,此时,缓存必须先从内存中获取相应的数据,然后再将它返回。我们称这种情况为缓存丢失。

2. 块

00123
14567
2891011
312131415
416171819
520212223
624252627
728293031
832333435
936373839
1040414243
1144454647
1248495051
1352535455
1456575859
1560616263

首先,我们来观察这个64字节的内存模型。在按字节编址的情况下,每个小格子代表一个字节,因此我们需要6个位来访问这64个格子。如果我们按字来编址,假设一个字等同于四个字节,那么每行就代表一个字,此时只需四位便可访问这16行。实际上块的概念与字的概念在本质上是相似的,比如我们可以设定一个块等同于1个字。这样内存的一行就相当于一个块。

0
1
2
3

让我们再来看一个四行的缓存模型。在这个模型中,缓存的每一行都对应一个块,因此一个内存块就可以直接放入缓存的任意一行。

内存
字00 块01 块02 块03 块0
字14 块05 块06 块07 块0
字28 块09 块010 块011 块0
字312 块013 块014 块015 块0
字416 块117 块118 块119 块1
字520 块121 块122 块123 块1
字624 块125 块126 块127 块1
字728 块129 块130 块131 块1
字832 块233 块234 块235 块2
字936 块237 块238 块239 块2
字1040 块241 块242 块243 块2
字1144 块245 块246 块247 块2
字1248 块349 块350 块351 块3
字1352 块353 块354 块355 块3
字1456 块357 块358 块359 块3
字1560 块361 块362 块363 块3
缓存
0
1

那么如果我们让一个块等于四个字呢?在这种情况下,四个内存行才能组成一个完整的块。我们给它们标上序号方便查看。此时展示一个两行的缓存模型,缓存行始终等于一个块的大小。在这种情况下,由四行内存组成的块能够被整体放进缓存的一行中。此时我们对内存地址可以进行重新的理解。目前有四个快,可以使用两位来进行索引。对于按字编址,一个块内有四个字,那么就是用两位作为偏移。对于按字节编址,对块的索引是一致的。而一个块内有16个字节,那么就使用四位作为偏移。

3. 缓存映射

内存
字00 块01 块02 块03 块0
字14 块05 块06 块07 块0
字28 块19 块110 块111 块1
字312 块113 块114 块115 块1
字416 块217 块218 块219 块2
字520 块221 块222 块223 块2
字624 块325 块326 块327 块3
字728 块329 块330 块331 块3
字832 块433 块434 块435 块4
字936 块437 块438 块439 块4
字1040 块541 块542 块543 块5
字1144 块545 块546 块547 块5
字1248 块649 块650 块651 块6
字1352 块653 块654 块655 块6
字1456 块757 块758 块759 块7
字1560 块761 块762 块763 块7

接着,就让我们尝试自己设计一个缓存。我们令一个块等于两个字,因此两个内存行就是一个块。后面出现的内存模型将会以块为基础单位。

缓存
0
1
2
3

让我们使用一个四行的缓存模型,我们都知道缓存行等于一个块,那么其中就包括了两个字,也就是八个字节。缓存的目的是通过内存地址返回给处理器数据,而处理器是不知道块的存在的。因此我们需要从块中找到对应的数据。如果此时是按字节编址,那么访问一个块中的八个字节就需要三个位,我们称之为偏移量。而如果是按字编址,那么访问一个块中的两个字就只需要一个位。

按字编址4位<=>16个字
标记Tag索引Index索引Index偏移量Offset

接下来观察这个四行的缓存。为了定位到其中一行的数据,我们需要通过两位来访问这四行,可以将其称之为索引。

内存
块0
块1
块2
块3
块4
块5
块6
块7

再拿出以块为一行的内存模型。当八个内存块对应到四个缓存行时,必然会有两个内存块映射到同一缓存行。为了解决这个问题,我们在编址时加入了一个标记位。但如果又有新的内存块要映射到这个已满的缓存行呢?单靠一位无法区分三个不同的内存块。为了解决这一问题,我们需要引入不同的映射策略。既然一个标记为只能区分两个内存块,那么我们就指定内存块存储的位置,直接映射(Direct-mapped)的思想就是这样。每两个内存块指向同一个缓存行,比如内存块0和内存块4指向缓存行0、内存块1和内存块5指向缓存行1,以此类推。这样一来,一个缓存行就只会有两个内存块存入,符合我们之前的设计。通过索引就能找到对应的缓存行,再根据标记就能找到对应的内存块。直接映射的优点是硬件设计简单,索引的存在使得查询速度快。缺点是每个缓存行的冲突概率较高,有很大可能会影响效率。

按字编址4位<=>16个字
标记Tag标记Tag标记Tag偏移量Offset

那我们就让每个缓存行区分更多的内存块,从而减少冲突的发生,直接区分全部内存块会怎么样?这正是全相联映射(Fully associative)的思想。一个内存块可以放入任意的缓存行,每个内存块都是如此。此时我们需要重新理解内存地址,只需要通过标记就能访问到对应的缓存行。因此不再需要索引,将索引位都改为标记,而三位的标记也对应着一个缓存行能够区分八个内存块。其优点很明显,冲突会特别少。但由于没有了索引,每次访问都需要和所有的标记进行比较,查询速度就会特别慢并且硬件设计也会很复杂。

二路组相联缓存
0组0
1组0
2组1
3组1
按字编址4位<=>16个字
标记Tag标记Tag索引Index偏移量Offset

因此,我们需要一个折中的策略,这就是组相联映射(Set-associative)。组的概念本质上与字相似。如果一个组中有一个块,即一个缓存行,我们就称之为一路组相联,就是现在没有区分的状态。而一个组里面有两个块,即两个缓存行,就称为二路组相联。这样我们可以通过一位的组索引来访问这两个组,此时的标记是两位,那么能区分四个内存块。一个内存块可以放到固定的一个组中,而在一个组中可以放置在任意一行。在组中就与全相联一样,因此我们只需要通过标记就可以在组里面访问到对应的内存块。而每个内存块都指定放入对应的组里面,这是直接相联的思路。组相联映射巧妙的平衡了冲突频率、查询速度和硬件设计的复杂度,是一种高效的映射策略。

4. 缓存结构

接下来让我们看一下内存与缓存在结构上的差异。

首先,内存的结构相对比较直观,它包含存储空间及其对应的地址,通过这些地址我们可以直接访问存储空间中的特定数据。而缓存同样具有存储空间,但与内存不同,缓存还需要控制信息,如有效位用于标示该行数据是否有效。此外还可以有其他的标志位,比如一个标示脏数据的位。一行的控制信息与存储数据一起被称为缓存槽。

我们来看一下内存的地址结构。首先能够以块为单位理解地址,分为块索引和块内偏移。而缓存能够对内存地址重新进行理解。以组相联映射为例,分为标记、组索引和偏移。其中的标记正是缓存中的控制信息,此时我们能够计算出缓存的总容量,就是缓存槽的大小乘以缓存总行数。一个槽中包含了有效位、标志位、标记和块容量。缓存的存储容量是另一个关键的概念,它指的是单个缓存槽的块容量与缓存总行数的乘积。在这个情况下,引入缓存地址的概念,由行索引和行内偏移组成。其本质和以块为单位对内存地址的理解是一致的。因此我们可以通过存储容量计算出缓存的地址位数。

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

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

相关文章

json中的dump用法和区别

使用python 3.7和python3.11测试 不使用dumps&#xff0c;json中的数据 import jsonjson_datatest.json data[{ip:2222,model:c://ddd/oo.onnx}] # updatedjson.dumps(data) # print(updated) with open(json_data,w) as file:json.dump(data,file,indent4) 使用dumps之后&am…

18V-120V降12V300m恒压WT5117

18V-120V降12V300m恒压WT5117 WT5117 是一款专为开关电源设计的集成了 150V 高电压 MOSFET 的 DC-DC 控制器。这个设备具备内置高压启动和自供电功能&#xff0c;能够满足快速启动及低能耗待机状态的需求。 WT5117 配备了自适应降频技术&#xff0c;以提升在低负载条件下的转换…

怎么做好现货白银交易?原来三点很重要......

要做好现货白银交易&#xff0c;投资者除了要掌握基本的分析和交易技巧&#xff0c;还有很重要的一点就是耐得住寂寞&#xff0c;学会空仓。一些投资者喜欢短线高频地进行交易&#xff0c;这样做赚的时候可能很快&#xff0c;但如果做不好&#xff0c;回撤也同样可能来得更迅猛…

vmware workstation下centos7屏幕切换及大小调整

虚拟机版本&#xff1a;vmware workstation15.5.2 操作系统版本&#xff1a;centos 7.9.2009 一 图形界面和命令行界面切换方法 在CentOS 7中&#xff0c;可以使用以下方法切换界面&#xff1a; 1 使用快捷键切换&#xff1a;按下Ctrl Alt F2&#xff08;或F3&#xff0…

Android SurfaceFlinger——概述(一)

一、基础介绍 SurfaceFlinger 是 Android 系统中的一个关键组件&#xff0c;负责管理屏幕显示的合成和渲染。 服务角色&#xff1a;SurfaceFlinger 作为一个系统服务独立运行&#xff0c;它不依赖于任何应用程序进程&#xff0c;而是由系统启动并持续运行。窗口管理&#xff1a…

【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】007 - evb-rk3568_defconfig 配置编译全过程

【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】007 - evb-rk3568_defconfig 配置编译全过程 一、编译后目录列表二、make distclean三、生成.config文件:make V=1 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- evb-rk3568_defconfig四、开始编译:CROSS_COMPILE=aarch64-…

【星海出品】LogStash

智谱清言 ZhipuAI LogStash | Fluentd Logstash 依赖于JAVA Oracle 修复了 Java 超过一半的问题。所以我们到Oracle 官方网站上下载 java 的JDK&#xff0c;安装方法见之前的文档 LogStash 的核心价值就在于它将业务系统和数据展示系统隔离开来&#xff0c;屏蔽了各自系统变化对…

DOM 改变节点

DOM 改变节点 文档对象模型&#xff08;DOM&#xff09;是 HTML 和 XML 文档的编程接口。它提供了对文档的结构化表示&#xff0c;并定义了一种方式&#xff0c;允许程序和脚本动态地访问和更新文档的内容、结构和样式。在网页开发中&#xff0c;DOM 操作是核心技能之一&#…

单链表的反转

分数 5 作者 李卫明 单位 杭州电子科技大学 1.3 在第1题( 编写程序&#xff0c;建立2个带头结点单链表&#xff0c;输入若干整数将正整数插入第1个单链表&#xff0c;将负整数插入第2个单链表&#xff0c;插入前和插入后单链表保持递增或相等次序&#xff0c;显示2个单链表&…

为什么有人认为Linux不如macOS?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;首先要明确你说的是哪个Lin…

如何用SCA工具做好开源软件风险管理?

开源 三句半​​​​​​​ 开源风险难提防 管理策略多思量 SCA工具来帮忙 治理有方&#xff01; 随着开源软件被广泛应用&#xff0c;其带来的风险也日益凸显。往期内容我们探讨开源治理策略&#xff0c;其中风险管理成为了企业和开发者必须面对和解决的重要问题。本文将探讨…

Java练习题1

1.什么是MyBatis&#xff1f; MyBatis是一个支持普通SQL查询、存储过程以及高级映射的持久层框架&#xff0c;它消除了几乎所有的JDBC代码和参数的手动设置以及对结果集的检索&#xff0c;使用简单的XML或注解进行配置和原始映射&#xff0c;将接口和Java的POJO映射成数据库中…

快速自定义表单开发受欢迎的几个优势

为了满足业务需求&#xff0c;低代码技术平台带着更理想的优势特点&#xff0c;广泛用于各中大型企业中&#xff0c;是助力企业实现提质增效、提升开发效率的有力武器。那么&#xff0c;您知道快速自定义表单开发的优势体现在哪里吗&#xff1f;为了帮助大家了解这些详情&#…

路由框架 ARouter 原理及源码解析

文章目录 前言一、ARouter 简介二、ARouter 使用1.添加依赖和配置2.添加注解3.初始化SDK4.发起路由操作 三、ARouter 成员1. PostCard 明信片2. Interceptor 拦截器3. Warehouse 路由仓库4. ARouter 注解处理 四、ARouter 原理五、ARouter 源码分析1. ARouter 初始化1.1 ARoute…

基于SSM的足球联赛管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

go语言int64转字符串类型

在 Go 语言中&#xff0c;将 int64 类型转换为字符串类型通常使用 fmt.Sprintf 函数或者 strconv 包中的 strconv.Itoa64 和 strconv.AppendInt 方法。下面是两种常见的方法&#xff1a; 1. 使用 fmt.Sprintf&#xff1a; go package main import "fmt" func main…

分布式系列之限流组件

概述 在高并发场景下&#xff0c;请求量瞬间到达&#xff0c;后端服务器即使有缓存、集群主备、分库分表、容错降级等措施&#xff0c;也有可能扛不住这请求量&#xff0c;因此可考虑引入限流组件。限流的目的&#xff1a;防止恶意请求流量或流量超出系统承载。 应用场景&…

【Java核心技术9】Java控制流语句详解:if、switch、for、while

引言 控制流语句是编程语言中不可或缺的一部分&#xff0c;它们决定了程序的执行路径&#xff0c;使程序能够根据不同的条件执行不同的代码块。在Java中&#xff0c;主要的控制流语句包括if、switch、for、while和do…while。 本文将通过实际代码示例&#xff0c;详细介绍这些…

计算机毕业设计师hadoop+spark+hive知识图谱医生推荐系统 医生数据分析可视化大屏 医生爬虫 医疗可视化 医生大数据 机器学习 大数据毕业设计

流程&#xff1a; 1.Python爬虫采集中华健康网约10万医生数据&#xff0c;最终存入mysql数据库&#xff1b; 2.使用pandasnumpy/hadoopmapreduce对mysql中的医生数据进行数据分析&#xff0c;使用高德地图解析地理位置&#xff0c;并将结果转入.csv文件同时上传到hdfs文件系统&…

百度文心智能体平台(想象即现实):轻松上手,开启智能新时代!创建属于自己的智能体应用。

目录 1.1、文心智能体平台 1.2、创建智能体 1.3、智能体报名入口 1.4、古诗词小助手 1.5、访问我的智能体 我的智能体访问地址&#xff1a;https://mbd.baidu.com/ma/s/7u8kBFYA。 在这个全新的时代里&#xff0c;人工智能技术正以前所未有的速度发展&#xff0c;渗透到我…