KIOSHIROI's CS-learning Road

实际上,存储器系统是一个具有不同容量,成本和访问时间的存储设备的层次结构。

CPU寄存器保存着最常用的数据。靠近CPU的小的、快速的高速缓存存储器(cache memory)是一个缓冲区,缓存的是存储在相对慢速的主存储器中数据和指令的一部分。主存缓存存储在容量较大,慢速磁盘上的数据,而这些磁盘常常作为存储在通用网络连接的其他机器的磁盘或磁带上的数据缓冲区域。

具有良好局部性的程序倾向于一次又一次地访问相同的数据项集合,或是倾向于访问邻近的数据项集合。

随机访问存储器(Random Access Memory, RAM)

分为两类:静态的和动态的。静态RAM(SRAM)比动态RAM(DRAM)更快,也贵得多。

SRAM 用来作为高速缓存存储器,既可以在 CPU 芯片上,也可以在片外

DRAM 用来作为主存以及图形系统的帧缓存区

SRAM 将每个位存储在一个双稳态的存储器单元里

由于 SRAM 存储器单元的双稳态特性,**只要有电,他就永远保持它的值。**即使有干扰(如电子噪音)来扰乱电压,当干扰消除时,电路就会恢复到稳定值。

image.png

DRAM 将每个位存储为对一个电容的充电

DRAM 存储器可以制造得非常密集——每个单元由一个电容和一个访问晶体管组成。

与 SRAM 不同,DRAM 存储单元对干扰非常敏感。当电容的电压被扰乱后,他就永远不会恢复了。暴露在光线下会导致电容电压改变。

每位晶体管数 相对访问时间 持续的 敏感的 相对花费 应用
SRAM 6 1 x 1000 x cache
DRAM 1 10 x 1 x 主存,帧缓冲区

传统的 DRAM

DRAM 芯片中的单元被分成 r * c = d 个超单元,每个超单元都由 w 个 DRAM 单元组成

一个 d * w 的 DRAM 总共存储了 dw 位信息。

image.png

例子:读超单元(2, 1)

  1. 读行2,复制行 2 到内部行缓冲区
  2. 读列1,从行缓冲区复制出 w 位信息发送到内存控制器

二维阵列组织的缺点是必须分两片发送地址,这增加了访问时间。

非易失性存储器

如果断电,DRAM 和 SRAM 会丢失他们的信息,从这个意义上说,他们是易失的。

只读存储器(ROM,Read-Only Memory)

ROM 是以他们能够被重编程(写)的次数和对他们进行重编程所用的机制来区分的。

PROM(Programmable ROM,可编程 ROM)只能被编程一次。PROM 的每个存储器单元有一种熔丝,只能用高电流熔断一次。

可擦写可编程 ROM(Erasable Programmable ROM,EPROM)。使用光(紫外线)来重编程。可复写 1000 次。

闪存(flash memory),SSD

存储在 ROM 设备中的程序通常被称为固件,当一个计算机系统通电以后,它会运行存储在 ROM 中的固件。

访问主存

数据流通过称为总线(bus)的共享电子电路在处理器和DRAM主存之间来来回回

每次CPU和主存之间的数据传送都是通过一系列步骤来完成,这些步骤称为总线事务。读事务从主存传送数据到CPU,写事务从CPU传送数据到主存。

image.png

局部性

步长为 1 的引用模式是程序中空间局部性常见和重要的来源。一般来说,随着补偿的增加,空间局部性下降。

高速缓存存储器

image.png

通用的高速缓存存储器组织结构

image.png

参数 S 和 B 将 m 个地址位分成三个字段

image.png

一旦我们知道这个字必须放到哪个组(根据组索引)中,A 中的 t 个标记位就告诉我们这个组中哪一行包含这个字(如果有的话)。当且仅当设置了有效位并且该行的标记位相匹配时,组中的这一行才包含这个字。一旦我们在由组索引标识的组中定位了由标号所标识的行,那么 b 个块偏移位给出了在 B 个字节的数据块中的字偏移。

直接映射高速缓存

根据每个组的高速缓存行数 E,高速缓存被分为不同的类。每个组只有 1 行(E=1)的高速缓存称为直接映射高速缓存。

高速缓存确定一个请求是否命中,然后抽取出被请求的字的过程,分为三步:

1)组选择;2)行匹配;3)字抽取

1. 组选择

image.png

2. 行匹配与字选择

image.png

  • 缓存命中的行替换
  • 冲突不命中

旁注:为什么用中间的位来做索引

使用中间位索引能够让高速缓存更快地被填满并有效利用。如果用高位索引则总是使用一个块(引起冲突不命中)

组相联高速缓存

直接映射高速缓存中冲突不命中造成的问题源于每个组只有一行,组相联高速缓存每个组都保存有多于一个的高速缓存行

一个 1 < E < C / B 的高速缓存通常称为 E 路组相联高速缓存

1. 组选择(与直接映射高速缓存一样)

2. 行匹配和字选择

搜索组中的每一行,寻找一个有效的行,其标记与地址中的标记相匹配

全相联高速缓存

  • 是由一个包含所有高速缓存行的组(即 E = C / B)组成的。

1. 组选择(无组索引位)

image.png

全相联高速缓存只适合做小的高速缓存,如虚拟内存系统中的快表(TLB)

有关写的问题

假设要写一个已经缓存了的字 w

最简单的方法:直写(立即将 w 的高速缓存块写回紧接着的第一层中)

缺点:每次写都会引起总线流量

另一种方法:写回(write-back),尽可能地推迟更新,只有当替换算法要驱逐这个更新过的块时,才把它写到紧接着的第一层。

由于局部性,写回能显著减少总线流量。

缺点:增加了复杂性(必须为每个高速缓存行维护一个额外的修改位)

如何处理写不命中

一种方法:写分配(write-allocate)

加载相应的第一层的块到高速缓存中,然后更新这个高速缓存块。

写分配试图利用写的空间(时间)局部性,但是缺点是每次不命中都会导致一个块从第一层传递到高速缓存。

另一种方法:非写分配(not-write-allocate)

避开高速缓存,直接把这个字写到第一层,直写高速缓存通常是非写分配的。写回高速缓存通常是写分配的。

真实的高速缓存层次结构

image.png

高速缓存参数的性能影响

有许多指标来衡量高速缓存的性能:

  • 不命中率
  • 命中率
  • 命中时间
  • 不命中处罚

旁注:高速缓存行、组和块有什么区别

  • 块是一个固定大小的信息包,在高速缓存和下一层缓存之间来回传递。
  • 行是高速缓存器中的一个容器,存储块和其他信息
  • 组是一个或多个行的集合

编写高速缓存友好的代码

  • 让最常见的情况运行得快。把注意力放在核心函数的循环上
  • 尽量减少每个循环内部的缓存不命中数量
    • 对局部变量的反复引用是好的,因为编译器能够将他们缓存在寄存器文件中(时间局部性)
    • 步长为1的引用模式是好的,因为存储层次结构中所有层次上的缓存都是将数据存储为连续的块(空间局部性)

在 Notion 参与讨论

本文托管在 Notion,欢迎到原文评论区留言交流

在 Notion 打开
笔记-CSAPP 第6章 存储器层次结构
https://kioshiroi.github.io/blog/csapp6
Author KIOSHIROI
Published at 2026年5月14日
Comment seems to stuck. Try to refresh?✨