CSAPP 内存与缓存

基础知识

随机存取存储器

随机存取存储器(RAM, Random-Access Memory)是一种易失性存储器,简单来说就是一旦不通电,所有的信息都会丢失。如果想要让数据持久化,可以考虑 ROM, PROM, EPROM, EEPROM 等非易失性存储器。

RAM有两种:

SRAM(Static RAM):SRAM 非常快,也不需要定期刷新,通常用在处理器做缓存,例如L1,L2,L3缓存,但是比较贵;

DRAM(Dynamic RAM):DRAM 稍慢一点(大概是 SRAM 速度的十分之一),需要刷新,通常用作主内存,相比来说很便宜(是 SRAM 价格的百分之一)

总线

总线,其实就是一组线路。我们的 CPU、内存以及输入和输出设备,都是通过这组线路,进行相互间通信的
在这里插入图片描述假设 CPU 需要从硬盘中读取一些数据,会给定指令,逻辑块编号和目标地址,并发送给磁盘控制器。然后磁盘控制器会读取对应的数据,并通过 DMA(direct memory access)把数据传输到内存中;传输完成后,磁盘控制器通过中断的方式通知 CPU,然后 CPU 完成之后的工作。

传统机械硬盘(HDD)

传统的机械硬盘有许多不同的部件:
在这里插入图片描述
机械硬盘有许多片磁盘(platter)组成,每一片磁盘有两面;每一面由一圈圈的磁道(track)组成,而每个磁道会被分隔成不同的扇区(sector)。这里概念层层递进,可以结合下图仔细辨析清楚。
在这里插入图片描述
上图是一个磁盘的视图,多个磁盘组合起来是这样的:
在这里插入图片描述
读磁盘的过程:
在这里插入图片描述
假设我们现在已经从蓝色区域读取完了数据,接下来需要从红色区域读,首先需要寻址,把读取的指针放到红色区域所在的磁道,然后等待磁盘旋转,旋转到红色区域之后,才可以开始真正的数据传输过程。

总的访问时间 Taccess = 寻址时间 Tavg seek + 旋转时间 Tavg rotation + 传输时间 Tavg transfer

可以看出,HDD硬盘由于使用物理的方式读磁盘的限制,与RAM用电访问相比,会慢上许多。硬盘比 SRAM 慢 40,000 倍,比 DRAM 慢 2500 倍。

固态硬盘(SSD)

接下来介绍一下固态硬盘,内部结构如下
在这里插入图片描述
SSD 硬盘都是 3D 封装的,由很多个裸片(Die)叠在一起的,一张裸片上可以放多个平面(Plane),一个平面上的存储容量大概在 GB 级别,一个平面上面,会划分成很多个块(Block),一般一个块(Block)的存储大小, 通常几百 KB 到几 MB 大小,一个块里面,还会区分很多个页(Page),就和我们内存里面的页一样,一个页的大小通常是 4KB。

与传统的机械硬盘相比,固态硬盘在读写速度上有很大的优势。但是因为设计本身的约束,连续访问会比随机访问快,而且如果需要写入 Page,那么需要移动其他 Page,擦除整个 Block,然后才能写入。

预留空间:一般 SSD 硬盘会预留出 7%-15% 左右的空间,用来进行各种数据的闪转腾挪,让你能够写满空间。
磨损均衡(Wear-Leveling):让 SSD 硬盘各个块的擦除次数,均匀分摊到各个块上。由由闪存转换层(FTL)实现,就像在管理内存的时候,我们通过一个页表映射虚拟内存页和物理页一样,在 FTL 里面,存放了逻辑块地址到物理块地址)的映射。

局部性原理 Locality

时间局部性(Temporal Locality): 如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。程序循环、堆栈等是产生时间局部性的原因。
空间局部性(Spatial Locality): 在最近的将来将用到的信息很可能与现在正在使用的信息在空间地址上是临近的
顺序局部性(Order Locality): 在典型程序中,除转移类指令外,大部分指令是顺序进行的。顺序执行和非顺序执行的比例大致是5:1。此外,对大型数组访问也是顺序的。指令的顺序执行、数组的连续存放等是产生顺序局部性的原因。

存储体系 Memory Hierarchy

一种介质的速度越快就会越贵,同时也消耗更多的电量,所以一般容量比较小。而 CPU 和内存之间的速度差距越来越大,所以好的程序都会尽可能利用局部性。而根据这些特性,引申出了安排存储的方式,称为金字塔式存储体系(Memory Hierarch)。
在这里插入图片描述这里就涉及到一个技术:缓存。缓存可以看作是把大且缓慢的设备中的数据的一部分拿出来存储到其中的更快的存储设备。在金字塔式存储体系[3]中,每一层都可以看作是下一层的缓存。利用局部性原理,程序会更倾向于访问第 k 层的数据,而非第 k+1 层,这样就减少了访问时间

缓存未命中的类型

缓存未命中有三种,这里进行简要介绍

  • 强制性失效(Cold/compulsory Miss): CPU 第一次访问相应缓存块,缓存中肯定没有对应数据,这是不可避免的
  • 冲突失效(Confilict Miss): 在直接相联或组相联的缓存中,不同的缓存块由于索引相同相互替换,引起的失效叫做冲突失效
    假设这里有 32KB 直接相联的缓存
    如果有两个 8KB 的数据需要来回访问,但是这两个数组都映射到相同的地址,缓存大小足够存储全部的数据,但是因为相同地址发生了冲突需要来回替换,发生的失效则全都是冲突失效(第一次访问失效依旧是强制性失效),这时缓存并没有存满
  • 容量失效(Capacity Miss): 有限的缓存容量导致缓存放不下而被替换,被替换出去的缓存块再被访问,引起的失效叫做容量失效
    假设这里有 32KB 直接相联的缓存
    如果有一个 64KB 的数组需要重复访问,数组的大小远远大于缓存大小,没办法全部放入缓存。第一次访问数组发生的失效全都是强制性失效。之后再访问数组,再发生的失效则全都是容量失效,这时缓存已经存满,容量不足以存储全部数据
高速缓冲存储器 Cache Memory

高速缓存存储器(Cache Memory)是 CPU 缓存系统甚至是金字塔式存储体系中最有代表性的缓存机制,前面我们了解了许多概念,这一节我们具体来看看高速缓存存储器是如何工作的。
首先要知道的是,高速缓存存储器是由硬件自动管理的 SRAM 内存,CPU 会首先从这里找数据,其所处的位置如下(蓝色部分):
在这里插入图片描述然后我们需要关注高速缓冲存储器的三个关键组成部分(注意区分大小写):

  • S 表示集合(set)数量
  • E 表示数据行(line)的数量
  • B 表示每个缓存块(block)保存的字节数目

在图上表示出来就是
在这里插入图片描述所以缓存中存放数据的空间大小为:
C=S×E×B

实际上可以理解为三种层级关系,对应不同的索引,这样分层的好处在于,通过层级关系简化搜索需要的时间,并且和字节的排布也是一一对应的(之后介绍缓存的时候就体现得更加明显)。

当处理器需要访问一个地址时,会先在高速缓冲存储器中进行查找,查找过程中我们首先在概念上把这个地址划分成三个部分:
在这里插入图片描述伪共享
伪共享指的是在多个线程同时读写同一个缓存行的不同变量的时候,尽管这些变量之间没有任何关系,但是在多个线程之间仍然需要同步,从而导致性能下降的情况。在对称多处理器结构的系统中,伪共享是影响性能的主要因素之一,由于很难通过走查代码的方式定位伪共享的问题,因此,大家把伪共享称为“性能杀手”。

读取

具体在从缓存中读取一个地址时,首先我们通过 set index 确定要在哪个 set 中寻找,确定后利用 tag 和同一个 set 中的每个 line 进行比对,找到 tag 相同的那个 line,最后再根据 block offset 确定要从 line 的哪个位置读起(这里的而 line 和 block 是一个意思)。

在这里插入图片描述三种高速缓存的简单解释
直接映射高速缓存(Direct Mapped Cache):有多个Set,一个Set只有一行
组相联高速缓存(set associative cache):有多个Set,一个Set有多行
全相联高速缓存(fully associative cache):只有一个Set,一个Set有多行
因为空间大小一定,所以三种高速缓存主要就是在集合Set和行Line之间做一个平衡取舍。
目前主要采用组相联的方式,原因有两:

  • 直接映射Cache的存储空间得不到充分利用,每个主存块只有一个固定位置可存放,容易产生冲突,使Cache效率下降。
  • 全相联由于Cache比较电路的设计和实现比较困难,这种方式只适合于小容量Cache采用

组相联是前两种方法的折中方案,适度兼顾二者的优点,尽量避免二者的缺点,因而得到普遍采用。

写入

在整个存储层级中,不同的层级可能会存放同一个数据的不同拷贝(如 L1, L2, L3, 主内存, 硬盘)。如果发生写入命中的时候(也就是要写入的地址在缓存中有),有两种策略:

  • Write-through: 命中后更新缓存,同时写入到内存中
  • Write-back: 直到这个缓存需要被置换出去,才写入到内存中(需要额外的 dirty bit
    来表示缓存中的数据是否和内存中相同,因为可能在其他的时候内存中对应地址的数据已经更新,那么重复写入就会导致原有数据丢失

在写入 miss 的时候,同样有两种方式:

  • Write-allocate: 载入到缓存中,并更新缓存(如果之后还需要对其操作,这个方式就比较好)
  • No-write-allocate: 直接写入到内存中,不载入到缓存

这四种策略通常的搭配是:

  • Write-through + No-write-allocate
  • Write-back + Write-allocate

其中第一种可以保证绝对的数据一致性,第二种效率会比较高(通常情况下)。

说到底,缓存就是要尽量利用局部性原理,这个是一个非常值得探讨的话题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值