06 The hierarchy of storage system

06 The hierarchy of memory system

在对系统的研究中,我们依赖于一个简单的计算机系统模型:CPU执行指令,而存储器系统为CPU存放指令和数据。 在简单模型中,存储器系统是一个线性的字节数组,而 CPU 能够在一个常数时间内访问每个存储器位置,这是一个有效的模型,但是它没有反映现代系统实际工作的方式。

存储器系统(memory system)是一个具有不同容量、成本和访问时间的存储设备的层次结构:CPU寄存器-cache memory-main memory-HDD

  1. CPU 寄存器保存着最常用的数据。
  2. 靠近 CPU 的小的、快速的高速缓存存储器(cache memory)作为一部分存储在相对慢速的主存储器(main memory)中数据和指令的缓冲区域。
  3. 主存缓存存储在容量较大的、慢速磁盘上的数据,而这些磁盘常常又作为 储在通过网络连接的其他机器的磁盘或磁带上的数据的缓冲区域
  4. 一个编写良好的程序倾向千更频繁地访问某一个层次上的存储设备。所以,下一层的存储设备可以更慢速一点,也因此可以更大,每个比特位更便宜。

Storage tech

Random-Access Memory(RAM)

随机访问存储器(Random-Access Memory, RAM)分为两类:静态的和动态的。静态 RAM(SRAM) 比动态 RAM(DRAM)更快,但也贵得多。 SRAM 用来作为高速缓存存储器,既可以在CPU芯片上,也可以在片下。 DRAM用来作为主存以及图形系统的帧缓冲区。

访问主存

数据流通过称为总线(bus)的共享电子电路在处理器和 DRAM 主存之间来来回回。 每 次 CPU 和主存之间的数据传送都是通过一系列步骤来完成的,这些步骤称为总线事务 (bus transaction) 。读事务(read transaction)从主存传送数据到 CPU。写事务(write transaction) 从 CPU 传送数据到主存。

image-20260729201913831

读事务是由三个步骤组成的:

  • 首先, CPU将地址A放到系统总线上。 I/0 桥将信号传递到内存总线(图 6-7a)。
  • 接下来,主存感觉到内存总线上的地址信号,从内存总线读地址,从 DRAM 取出数据字,并将数据写到内存总线。 I/0桥将内存总线信号翻译成系统总线信号,然后沿着系统总线传递(图 6-7b)。
  • 最后, CPU感觉到系统总线上的数据,从总线上读数据,并将数据复制到寄存器%rax(图 6-7c)。

image-20260729202145184

写事务同样有三个基本步骤:

  • CPU 将地址放到系统总线上。
  • 内存从内存总线读出地址,并等待数据到达(图 6-8a)。接下来, CPU 将%rax 中的数据字复制到系统总线(图 6-86)。
  • 最后,主存从内存总线读出数据字x,并且将这些位存储到 DRAM 中(图 6-8c)。

image-20260729202354072

Drive storage

磁盘是广为应用的保存大量数据的存储设备,存储数据的数量级可以达到几百到几于 千兆字节,而基千 RAM 的存储器只能有几百或几千兆字节。 不过,从磁盘上读信息的时 间为毫秒级,比从DRAM读慢了 10万倍,比从 SRAM读慢了 100 万倍。

image-20260729202939703

一个典型的磁盘表面的结构如上图,每个表面是由一组称为磁道(track)的同心圆组成的。 每个磁道被划分为一组扇区(sector)。每个扇区包含相等数量的数据位(通常 是 512 字节),这些数据编码在扇区上的磁性材料中。扇区之间由一些间隙(gap)分隔开, 这些间隙中不存储数据位。间隙存储用来标识扇区的格式化位。

磁盘是由一个或多个叠放在一起的盘片组成的,它们被封装在一个密封的包装里, 如图 6-9b 所示。整个装置通常被称为磁盘驱动器(disk drive) , 我们通常简称为磁盘(disk)。 有时,我们会称磁盘为旋转磁盘(rotating disk), 以使之区别于基于闪存的固态硬盘 (SSD), SSD 是没有移动部分的。

磁盘操作

image-20260729203247878

  1. 磁盘用读/写头(read/write head)来读写存储在磁性表面的位,而读写头连接到一个 传动臂(actuator arm)一端,如图 6-10a 所示。通过沿着半径轴前后移动这个传动臂,驱动器可以将读/写头定位在盘面上的任何磁道上。这样的机械运动称为寻道(seek)。
  2. 。一旦读/ 写头定位到了期望的磁道上,那么当磁道上的每个位通过它的下面时,读/写头可以感知 到这个位的值(读该位),也可以修改这个位的值(写该位)。
  3. 有多个盘片的磁盘针对每个盘 面都有一个独立的读/写头,读/写头垂直排列,一致行动。在任何时刻,所有的读写头都位于同一个柱面上

磁盘以扇区大小的块来读写数据。对扇区的访问时间(access time)有三个主要的部 分:寻道时间(seek time) 、旋转时间 (rotational latency) 和传送时间 (transfer time) :

  • 寻道时间:为了读取某个目标扇区的内容,传动臂首先将读/写头定位到包含目标 扇区的磁道上。
  • 旋转时间:一旦读/写头定位到了期望的磁道,驱动器等待目标扇区的第一个位旋 转到读/写头下。
  • 传送时间:当目标扇区的第一个位位千读/写头下时,驱动器就可以开始读或者写该扇区的内容了。一个扇区的传送时间依赖千旋转速度和每条磁道的扇区数目。

逻辑磁盘块:现代磁盘构造复杂,有多个盘面,这些盘面上有不同的记录区。为了对操作系统隐藏这样的复杂性,现代磁盘将它们的构造呈现为一个简单的视图,一个 B 个扇区大小的逻辑块的序列,编号为 o, 1, …, B—1。 磁盘封装中有一个小的硬件/固件设备,称为磁盘控制器,维护着逻辑块号和实际(物理)磁盘扇区之间的映射关系:控制器上的固件执行一个快速表查找,将一个逻辑块号翻译成一个(盘面,磁道,扇区)的三元组,这个三元组唯一地标识了对应的物理扇 区。

connect I/O Device

例如图形卡、监视器、鼠标、键盘和磁盘这样的输入/输出(I/0)设备,都是通过 I/0 总线,例如 Intel 的外围设备互连(Peripheral Component Interconnect, PCI) 总线连接到 CPU 和主存的。虽然 I/0 总线比系统总线和内存总线慢,但是它可以容纳种类繁多的第三方 I/0设 备。

image-20260729205515182

Access Disk Drive

当 CPU从磁盘读数据时发生的步骤如下:

image-20260729205719962

  1. CPU 使用一种称为内存映射 I/O(memory-mapped I/O)的技术来向 I/0 设备发射命令 (图 a) 。,假设磁盘控制器映射到端口 OxaO。随后, CPU 可能通过执行三 个对地址 OxaO 的存储指令,发起磁盘读:第一条指令是发送一个命令字,告诉磁盘发起一 个读,同时还发送了其他的参数;第二条指令指明应该读的逻辑块号。第三条指令指明应该存储磁盘扇区内容的主存地址。

    在使用内存映射 I/O的系统中,地址空间中有一块地址是为与 I/O设备通信保留的。每个这样的地址称为一个 I/O端口(I/O port)。当一个设备连接到总线时,它与一个或多个端口相关联(或它被映射到一个或多个端口)。

    • MMIO 的“端口”(I/O port)是物理内存地址上的设备寄存器或缓冲区,不是网络/套接字的端口号,两者完全不同。
    • 物理 MMIO 地址在系统中是全局的、属于机器的资源,不属于某一个进程。
  2. 在磁盘控制器收到来自 CPU 的读命令之后,它将逻辑块号翻译成一个扇区地址,读该扇区的内容,然后将这些内容直接传送到主存,不需要CPU 的干涉.设备可以自己执行读或者写总线事务而不需要 CPU 干涉的过程,称为直接内存访问(Direct Memory Access, DMA) 。这种数据传送称为 DMA 传送CDMA transfer) 。

  3. 在 DMA传送完成,磁盘扇区的内容被安全地存储在主存中以后,磁盘控制器通过给 CPU 发送一个中断信号来通知 CPU(图 6-l 2c) 。 基本思想是中断会发信号到 CPU 芯片的 一个外部引脚上。这会导致 CPU 暂停它当前正在做的工作,跳转到一个操作系统例程。 这个程序会记录下 1/0 已经完成,然后将控制返回到 CPU被中断的地方。

Locality

一个编写良好的计算机程序常常具有良好的局部性Clocality)。也就是,它们倾向于引 用邻近于其他最近引用过的数据项的数据项,或者最近引用过的数据项本身。

局部性通常有两种不同的形式:

  • 时间局部性(temporal locality):在一个具有良好时间局部性的程序中,被引用过一次的内存位置很可能在不远 的将来再被多次引用。
  • 空间局部性(spatial locality) :在一个具有良好空间局部性的程序中,如果一个内存位置被引用了 一次,那么程序很可能在不远的将来引用附近的一个内存位置。

局部性小结

  • 重复引用相同变量的程序有良好的时间局部性。
  • 对于具有步长为 K 的引用模式的程序,步长越小,空间局部性越好。具有步长为l 的引用模式的程序有很好的空间局部性。在内存中以大步长跳来跳去的程序空间局部性会很差。
  • 对于取指令来说,循环有好的时间和空间局部性。循环体越小,循环迭代次数越多,局部性越好。

memory hierarchy

  • 存储技术:不同存储技术的访问时间差异很大。速度较快的技术每字节的成本要比速度较慢的技术高,而且容量较小。 CPU和主存之间的速度差距在增大。
  • 计算机软件:一个编写良好的程序倾向于展示出良好的局部性。

硬件和软件的这些基本属性互相补充得很完美。 它们这种相互补充的性质使人想到一种组织存储楛系统的方法,称为存储器层次结构 (memory hierarchy) , 所有的现代计算机系统中都使用了这种方法。

image-20260729211120168

一般而言,从高层往底层走,存储设备变得更慢、更便宜和更大。

  1. 在最高层 (LO), 是少量快速的 CPU 寄存器, CPU 可以在一个时钟周期内访问它们。
  2. L1-L3是一个或多个小型到中型的基基于SRAM 的高速缓存存储器,可以在几个 CPU 时钟周期内访问它们
  3. 然后是一个大的基于 DRAM 的主存,可以在几十十到几百个时钟周期内访问它们。
  4. 接下来是慢速但是容拭很大的本地磁盘。
  5. 最后,有些系统甚至包括了一层附加的远杜服务器 上的磁盘,要通过网络来访问它们。。例如,像安德鲁文件系统(Andrew File System, AFS) 或者网络文件系统(Network File System, NFS)这样的分布式文件系统,允许程序 访问存储在远程的网络服务器上的文件。

Cache in memory hierarchy

高速缓存(cache, 读作 "cash")是一个小而快速的存储设备,它作为存储在更大、也更慢的设备中的数据对象的缓冲区域。层次结构中的每一层都缓存来自较低一层的数据对象。例如,本地磁盘作为通过网络从远程磁盘取出的文件(例如,Web页面)的缓存, 主存作为本地磁盘上数据的缓存,依此类推,直到最小的缓存 CPU寄存器组。

image-20260729211632179

图 6-22 展示了存储器层次结中缓存的一般性概念。

  1. 第 k+1层的存储器被划分成连续的数据对象组块(chunk), 称为块(block) 。每个块都有一个唯一的地址或名字,使之区别于其他的块。块可以是固定大小的(通常是这样的),也可以是可变大小的(例如存储在 Web 服务器上的远程 HTML文件)。
  2. 第k层的存储器被划分成较少的块的集合,每个块的大小与 k+1 层的块的大小一样。在任何时刻,第k层的缓存包含第k+1 层块的一个子集的副本。例如,在图 6-22 中,第 K 层的缓存有 4个块的空间,当前包含块 4、 9、 14 和 3 的副本。
  3. 数据总是以块大小为传送单元(transfer unit)在第 K 层和第 k+l 层之间来回复制的。
  4. 在层次结构中任何一对相邻的层次之间块大小是固定的,但是其他的层次对之间可以有不同的块大小。例如, L1 和 L0 之间的传送通常使用的是 1 个字大小的块。 L2 和 L1之间(以及 L3 和 L2 之间、 IA 和 L3 之间)的传送通常使用的是几十个字节的块。一般而言,层次结构中较低层(离 CPU 较远)的设备的访问时间较长,因此为了补偿这些较长的访问时间,倾向于使用较大的块。

缓存命中与不命中:

  • 缓存命中(cache hit):当程序需要第 k+I 层的某个数据对象 d 时,它首先在当前存储在第 K 层的一个块中 查找d。如果 d 刚好缓存在第k 层中,那么就是我们所说的缓存命中(cache hit)。
  • 缓存不命中 (cache miss):如果第 K 层中没有缓存数据对象d, 那么就是我们所说的缓存不命中 (cache miss) 。当发生缓存不命中时,第 K 层的缓存从第k+l 层缓存中取出包含 d 的那个 块,如果第 k 层的缓存已经满了,可能就会覆盖现存的一个块。
  • 决定该替换哪个块是由缓存的替换策略(replacement policy) 来控制的。例如,一个具有随机替换策略的缓存会随机选择一个牺牲块。一 个具有最近最少被使用CLRU)替换策略的缓存会选择那个最后被访问的时间距现在最远的块。

缓存不命中的种类

  1. 如果第 K层的缓存是空的,那么对 任何数据对象的访问都会不命中。一个空的缓存有时被称为冷缓存(cold cache) , 此类不命中称为强制性不命中(compulsory miss)或冷不命中 (cold miss) 。

  2. 只要发生了不命中,第 K 层的缓存就必须执行某个放置策略(placement policy), 确定 把它从第 k+I 层中取出的块放在哪里。硬件缓存通常使用的是严格的放置策略,这个策略将第 k+l 层的某个块限制放置在第k层块的一个小的子集中(有时只是一个块)。例如,在图 6-22 中,我们可以 确定第 k+l 层的块 t 必须放置在第k 层的块(i mod 4)中。例如,第 k+l 层的块 0、 4、 8 和 12 会映射到第 k 层的块 0; 块 1、 5、 9 和 13 会映射到块 1; 依此类推。注意,图 6-22 中的示例缓存使用的就是这个策略。

  3. 限制性的放置策略会引起一种不命中,称为冲突不命中(conflict miss), 在这种 情况中,缓存足够大,能够保存被引用的数据对象,但是因为这些对象会映射到同一个缓 存块,缓存会一直不命中。例如,在图 6-22 中,如果程序请求块0, 然后块 8然后块0, 然后块8, 依此类推,在第 k层的缓存中,对这两个块的每次引用都会不命中,即使这个 缓存总共可以容纳 4 个块。 0-0,8-0,0-0,一直反复刷缓存,不命中。

Cache

早期计算机系统的存储器层次结构只有三层: CPU 寄存器、 DRAM 主存储器和磁盘 存储。不过,由千 CPU和主存之间逐渐增大的差距,系统设计者被迫在 CPU寄存器文件和主存之间插入几层高速缓存:

  1. 一个小的 SRAM 高速缓存存储器,称为 L1 高速缓存(一级缓存),访问速度几乎和寄存器一样快,典型地是大约 4个时钟周期。
  2. 随着 CPU 和主存之间的性能差距不断增大,系统设计者在 L1 高速缓存和主存之间又 插入了一个更大的高速缓存,称为 L2 高速缓存,可以在大约 10 个时钟周期内访问到它。
  3. 有些现代系统还包括有一个更大的高速缓存,称为 L3 高速缓存,可以在大约 50个周期内访问到它。
    image-20260729213203158

General Cache memory structure

其中每个存储器地址有 m 位,形成$M=2^m$个不同的地址。如图 6-25a 所示,这样一个机器的高速缓存被组织成一个有$S=2^s$个高速缓存组(cache set)的数组。每个组包含 E个高速缓存行(cache line)。每个行是由一个 $B=2^b$字节的数据块 (block) 组成的,一个有效位(valid bit) 指明这个行是否包含有意义的信息,还有$t=m— (h+s)$ 个标记位(tag bit)(是当前块的内存地址的位的一个子集),它们唯一地标识存储在这个高速缓存行中的块: set->line->block(line由block+有效位+标记位组成)
image-20260729213542495
一般而言,高速缓存的结构可以用元组$(S, E, B, m)$来描述。高速缓存的大小(或容量)C指的是所有块的大小的和。标记位和有效位不包括在内。因此,$C=SXEXB$。

参数 S 和B 将m 个地址位分为了三个字段,如图 6-25b所示:

  1. 组索引:a 中s 个组则组索引位是一 个S个组的数组的索引。第一个组是组0, 第二个组是组 1, 依此类推,要有s位。
  2. 标记位:告诉我们这个组中的哪一行包含这个字,直接跟行中的标记位一致,。当且仅当设置了有效位并且该行的标记位与地址A 中的标记位相匹配时,组中的这 一行才包含这个字。
  3. 块偏移:一旦我们在由组索引标识的组中定位了由标号所标识的行,那么b个 块偏移位给出了在B 个字节的数据块中的字偏移。

(S,E,B,m) = (4,1,2,4)

高速缓存有 4个组, 每个组一行,每个块 2个字节,而地址是4位的。

Set&Line&Block

  • 块是一个固定大小的信息包,在高速缓存和主存(或下一层高速缓存)之间来回传送。
  • 行是高速缓存中的一个容器,存储块以及其他信息(例如有效位和标记位)。
  • 组是一个或多个行的集合。 直接映射高速缓存中的组只由一行组成。 组相联和全 相联高速缓存中的组是由多个行组成的

Directly mapping to cache

根据每个组的高速缓存行数E, 高速缓存被分为不同的类。每个组只有一行(E=l)的 高速缓存称为直接映射高速缓存(direct-mapped cache).
image-20260729215529438
假设我们有这样一个系统,它有一个 CPU、一个寄存器文件、一个 L1 高速缓存和一 个主存。当 CPU 执行一条读内存字w 的指令,它向 L1高速缓存请求这个字:

  1. 如果 L1高 速缓存有 w 的一个缓存的副本,那么就得到 L1 高速缓存命中,高速缓存会很快抽取出 w, 并将它返回给 CPU。
  2. 否则就是缓存不命中,当 L1 高速缓存向主存请求包含 w 的块的 一个副本时, CPU 必须等待。
  3. 当被请求的块最终从内存到达时, L1高速缓存将这个块存放在它的一个高速缓存行里,从被存储的块中抽取出字 W, 然后将它返回给 CPU。

高速缓存确定一个请求是否命中,然后抽取出被请求的字的过程. 分为三步: 1) 组选择; 2) 行匹配; 3) 宇抽取。

1.高速缓存块命中过程:直接映射高速缓存中的组选择

高速缓存从w的地址中间抽取出 s个组索引位。这些位被解释成一个对应一个组号的无符号整数。
image-20260729220431137

2.高速缓存块命中过程:直接映射高速缓存中的行匹配

在上一步中我们已经选择了某个组 i接下来的一步就要确定是否有字w的一个副本 存储在组1 包含的一个高速缓存行中。在直接映射高速缓存中这很容易,而且很快,这是因为每个组只有一行,否则我们要根据标记位来选择行。

3.高速缓存块命中过程:直接映射高速缓存中的字选择

一旦命中,我们知道w就在这个块中的某个地方。最后一步确定所需要的字在块中是 从哪里开始的。 如图 6-29 所示,块偏移位提供了所需要的字的第一个字节的偏移。

image-20260729220522380

缓存不命中

如果缓存不命中,那么它需要从存储器层次结构中的下一层取出被请求的块,然后将 新的块存储在组索引位指示的组中的一个高速缓存行中。 一般而言,如果组中都是有效高速缓存行了,那么必须要驱逐出一个现存的行。 对千直接映射高速缓存来说,每个组只包含有一行,替换策略非常简单:用新取出的行替换当前的行。

Simulation of cache hit

初始时,高速缓存是空的(即每个有效位都是0):

image-20260730192846564

  1. 读地址 0 的字。因为组 0 的有效位是 o, 是缓存不命中。高速缓存从内存(或低一 层的高速缓存)取出块o, 并把这个块存储在组 0 中。
    image-20260730192932748

  2. 读地址 1 的字。这次会是高速缓存命中。高速缓存立即从高速缓存行的块[1]中返 回 m[l]。高速缓存的状态没有变化。

  3. 读地址 13 的字。由于组 2 中的高速缓存行不是有效的,所以有缓存不命中。高速 缓存把块 6 加载到组 2 中,然后从新的高速缓存行的块[1]中返回 m[13].mod4 映射
    image-20260730193115861

  4. 读地址 8 的字。这会发生缓存不命中。组 0 中的高速缓存行确实是有效的,但是标1 记不匹配。高速缓存将块4 加载到组0 中(替换读地址0 时读入的那一行),然后从新的高 速缓存行的块[OJ中返回 m[8]
    image-20260730193605121

  5. 读地址 0 的字。又会发生缓存不命中,因为在前面引用地址 8 时,我们刚好替换了 块 0。这就是冲突不命中的一个例子,也就是我们有足够的高速缓存空间,但是却交替地 引用映射到同一个组的块。

组相联高速缓存匹配:每个组都保存有多于一个的高速缓存行,
image-20260730193713978
全相联高速缓存:只由一个包含所有高速缓存行的组组成。
image-20260730193839203

About write

写命中:要写一个已经缓存了的字w(写命中, write hit)。

在高速缓存更新了它的 w的副本之后,怎么更新 w在层次结构中紧接着低一层中的副本呢?

  • 直写(write-through):立即将 w 的高速缓存块写回到紧接着的低一层中
  • 写回(write-back):尽可能地推迟更新,只有当替换算法要驱逐这个更新过的块时,才把 它写到紧接着的低一层中

写不命中:

  • 写分配(write-allocate), 加载相应 的低一层中的块到高速缓存中,然后更新这个高速缓存块。
  • 非写分配(not-write-allocate) , 避开高速缓存,直接把这个字写到低一层中。

缓存命中效果

99%命中速度是97%命中的两倍:

97 hit: 1cycle+0.03x100cycle=4
99 hit 1cycle=0.01x100cycle=2
posted @ 2026-11-10 19:43  Typing_with_toes  阅读(5)  评论(0)    收藏  举报