CS:APP 第六章学习笔记

CS:APP 第六章 The Memory Hierarchy 的学习笔记

这章的主要内容有各种存储设备RAMROMHDDSSD的特点程序的局部性缓存的结构以及原理缓存对程序性能的影响

因为时间不太够本来我想先跳过这章以后再补的但学第九章的时候感觉还是跳不得否则第九章有些东西感觉学了个半懂虽然只用学一小部分就足以满足第九章的需求但我打算摆烂了该学的东西学不完就学不完我想学啥就学啥

Storage Technologies

RAM

Random access memory 分为 SRAM 和 DRAM 两种SRAM 有更快的访问速度但更加昂贵

SRAM

SRAM (Static RAM) 将每个 bit 存储在一个 bistable 的 memory cell 中每个 cell 由 6 个晶体管组成有两种可能的稳定态遇到微小的扰动也会迅速恢复到这两种状态之一

DRAM

DRAM (Dynamic RAM) 将每个 bit 存储在一个很小的电容中容易受到外界干扰所以需要周期性地将数据复制出去再复制回来以进行刷新可能还会配合纠错码来保证数据正确

DRAM 的设计使其存储密度更高但访问速度更慢SRAM 则更快但密度更低更贵更费电访问 DRAM 的用时大约是 SRAM 的 10 倍而 SRAM 的造价大约是 DRAM 的 1000 倍

Conventional DRAM

DRAM 芯片被分为若干 supercell每个 supercell 存储一个 word一般是 1 bytesupercell 排列为二维阵列可以用二维坐标 (i,j)(i, j) 定位

DRAM 通过 pin 连接到 memory controller 来和外界通信读取位于 (i,j)(i, j) 的 supercell 时memory controller 会依次发送 row access strobe (RAS) iicolumn access strobe (CAS) jj在收到 RAS 后 DRAM 会将第 ii 行复制到一个内部的 row buffer收到 CAS 后再从 row buffer 里将第 jj 列发送给 memory controller

Memory Module

DRAM 芯片会被组装为 memory module 来插到主板上

DIMM 是一种 memory module例如一个 DIMM 可以包含 8 个 DRAM 芯片每个 64-bit 的 word 在每个 DRAM 芯片的同一个地址上分别存一个 byte从而整个 DIMM 可以以 64-bit 为单位与外界通信

Enhanced DRAM

朴素的 DRAM 是比较慢的历史上曾经有过若干对 conventional DRAM 的优化

  1. FPM (fast page mode) DRAM: 如果连续两次 RAS 是一样的可以省略掉后续相同的 RAS直接发送 CAS
  2. EDO (extended data out) DRAM: 延长了数据输出的时间对 pipelining 有帮助
  3. SDRAM (synchronous): 通过时钟信号的 rising edge 同步地通信而非通过发送 RAS/CAS 异步通信
  4. DDR (double data-rate) SDRAM: 通过同时使用时钟信号的 rising edge 和 falling edge 达到 double data-rate分为 DDRDDR2DDR3DDR4DDR5 等
  5. VRAM (video): 一般用于显卡frame buffer 等它的输出是直接输出整个 buffer并且可以并行地同时读和写

ROM

RAM 会在断电后丢失数据所以是 volatile与之相对还有 nonvolatile 的存储器统称为 read-only memory (ROM)尽管有的 ROM 是可以写入的ROM 的写入称作 reprogram

  • PROM (programmable ROM) 只能被写入一次
  • EPROM (erasable PROM) 需要用特殊设备写入可以写入大约 1000 次
  • EEPROM (electrically EPROM) 不需要用特殊设备就可以写入可以写入大约 10510^5
  • flash memory 是一种基于 EEPROM 的 nonvolatile 存储器被广泛使用包括用于 SSD
  • 固件 (firmware) 往往存储于 ROM 中

访问 main memory

一个 bus 是一组用来通信的导线可以传输地址数据控制信号等CPU 和 main memory 之间的通信通过 bus transaction 进行

CPU 通过 system bus 连接 I/O bridgeI/O bridge 通过 memory bus 连接 main memoryI/O bridge 负责 system bus signal 和 memory bus signal 之间的转换

HDD

磁盘的结构

磁盘由若干 platter盘片组成每个 platter 有两个 surface表面每个 surface 上覆盖着磁性记录材料platter 由位于中心的 spindle主轴带动以某个一般是 5400~15000 RPM 的速度转动

每个 surface 被分成若干个称作 track磁道的同心圆环每个 track 被分为若干 sector扇区每个 sector 存有相同大小的数据一般是 512 bytes相邻的 sector 之间由 gap间隙隔开gap 不存储数据而是用来识别 sector

一个磁盘通常由多个堆叠在一起的 platter 构成这些 platter 共享一个 spindle对于某个距离 kk一个磁盘内所有 surface 上离转轴距离为 kk 的 track 的集合称作一个 cylinder柱面

整体结构如 CS:APP Figure 6.9 所示

磁盘结构示意图

磁盘的容量

磁盘的容量有三个衡量指标

  • recording density: 单位长度的 track 存储的 bit 数量
  • track density: 单位长度的半径上的 track 个数
  • areal density: 单位面积上存储的 bit 数量

早期的磁盘的所有 track 都有相同数量的 sector这样的话位于外部的 track 的 sector 就会更加稀疏后来为了提高容量将 cylinder 划分成了若干个 recording zone每个 recording zone 由若干相邻的 cylinder 组成同一个 recording zone 内的所有 track 有相同数量的 sector

磁盘的读写

磁盘通过连在传动臂上的读写头进行读写每次读写前需要先将读写头移动到相应的位置寻道并等待目标 sector 转动到读写头下再开始读写

寻道用时与读写头原本的位置到目标位置的距离有关等待转动的用时则看运气在 CS:APP 举的例子中寻道平均用时为 9 ms等待旋转平均用时为 4 ms读写一个 sector 用时 20 μs

也就是说磁盘读写的主要用时是寻道以及等待旋转用时也就是初次访问一段连续的 sector 的用时而与访问多少个连续的 sector 关系不大对于单个 sector磁盘访问的用时可以达到 SRAM 的 10410^4DRAM 的 10310^3但连续 sector 的读写用时仅为 DRAM 的不到十倍

Logical Disk Blocks

磁盘对外提供了 logical block 作为 sector 的抽象每个 logical block 的大小和一个 sector 相同由连续的非负整数索引通过 disk controller 翻译成形如 (surface, track, sector) 的坐标

I/O bus

不同的 I/O 设备通过 I/O bus 与 I/O bridge 连接例如显卡连接各种设备的 USB controller通过 SCSI/SATA 等接口连接磁盘的 host bus adapter 等都会连接到 I/O bus

访问磁盘

访问磁盘需要向磁盘发送三条指令

  1. 向磁盘发送一个信号告诉磁盘要读取数据
  2. 将要读取的 logical block number 发送给磁盘
  3. 告诉磁盘读取到的数据要放在 main memory 的哪个地址

发送完这些指令后CPU 会继续干其他事情磁盘读取到数据后会通过 I/O bus 直接将数据存放到 main memory 中而不经过 CPU这被称作 direct memory access (DMA)存放好数据后磁盘向 CPU 发送 interrupt signal 来跳转到处理磁盘读取完成的 signal handler

SSD

SSD 将一个或多个 flash memory 包装起来并且有一个 flash translation layer 来将输入的 logical block number 转换为对 flash memory 的访问对外表现出与 HDD 类似的接口

flash memory 由若干 block 组成每个 block 又由若干32-128 个page 组成每个 page 一般是 512B-4KB 大数据传输的最小单位是 page

SSD 的写入比较特殊一个 page 需要在所属的整个 block 都被擦除改为全 1后才能写入一次如果要写入第二次就得再把整个 block 擦除一遍在写入时为了擦除某个 block可能会需要把这个 block 存储的数据复制到其他 block擦除是一个耗时相对较长的操作需要约 1 ms并且每个 block 在擦除约 10510^5 次后就会损坏

这使得 SSD 的写入比读取略慢并且写入很多次后可能损坏flash translation layer 会通过 wear-leveling logic 来尽可能使得每个 block 的擦除次数相同以延长 SSD 的使用寿命

diskRAMCPU 速度差异的历史变化如 CS:APP Figure 6.16 所示其中 CPU cycle time 是单核的effective CPU cycle time 是多核的

disk、RAM、CPU 速度差异的历史变化

Locality

好的程序具有良好的 localitylocality 有两种表现形式temporal locality 指的是最近访问过的数据更有可能在不久的将来再次被访问spatial locality 指的是访问过一处的数据后更有可能在不久的将来访问邻近的其他数据

具有良好 locality 的程序跑得更快因为计算机系统设计的各个层面都利用 locality 做了优化

一些 locality 的例子

  • 重复引用同一个变量的程序有良好的 locality
  • 在一段连续内存数组中依次访问每个元素称作 stride-1 reference pattern每次间隔 k1k-1 个元素进行访问称作 stride-k reference patternkk 越小 locality 越好遍历高维数组时尤其要注意访问的顺序
  • 由于循环会重复访问同一段指令循环的指令读取局部性良好

The Memory Hierarchy

在硬件上不同存储技术之间存在性能价格容量的 trade-off在软件上程序具有 locality硬件和软件的这两条性质正好可以搭配在一起促使 memory system 采用了如 CS:APP Figure 6.21 所示的称作 memory hierarchy 的组织方式

The memory hierarchy

memory hierarchy 的构成并不一定和上图完全一致例如 SRAM 的级数可能不是三级DRAM 和 HDD 间可能还有 SSD磁带也可以作为 memory hierarchy 中比 HDD 更低的一级

Cache

caching 指的是用一个相对小而快的存储设备来存储一个相对大而慢的存储设备中最为活跃的部分这个小的存储设备称作大的存储设备的 cache

在 memory hierarchy 中每一级都是下一级的 cache数据会在各个相邻层级间不断地传输不同层级之间会以不同的 block size 作为数据传输的基本单位

从 cache 获取数据

想要从 memory hierarchy 的某一级获取数据时首先会尝试从它的 cache 获取数据如果成功获取则称作 cache hit否则称作 cache miss

发生 cache miss 时一般会先将数据从下一级复制到上一级从而最终还是表现为从 cache 中获取数据如果 cache 满了在从下一级获取数据时就需要删除 cache 中的一些数据来腾出空间这时需要在 cache 中选择被删除的数据被删除的 block 称作 victim block这个行为称作将 victim block evict而选择 victim block 是根据 replacement policy 进行的例如 random replacement policyleast recently used (LRU) replacement policy 等

Cache 的管理

cache 可能由硬件OS软件以及它们之间的相互配合来进行管理而这在大部分时候都是自动完成的无需应用程序的程序员操心

各级 cache 如 CS:APP Figure 6.23 所示

无处不在的各式各样的 cache

Cache 对 locality 的利用

temporal locality 使得重复使用的数据留存在 cache 中从而更容易 cache hitcache 中的数据按 block 存储则利用了 spatial locality使得一个数据被 cache 时与其邻近的处于同一个 block 的数据也被 cache

Cache Memories

随着 CPU 和 DRAM 的速度差异越来越大SRAM 被用来填充它们之间的 gap

在下面的讨论中为了简便假设只有 L1 cache没有 L2L3 cache或者也可以看成是在介绍 L3 cache 是如何工作的

Cache 的结构与读取

设 main memory 有 2m2^m 个地址每个地址存放一个 byte它的 cache 会分成 2s2^scache set每个 cache set 包含 EEcache line每个 cache line 存放一个大小为 2b2^b byte 的 data block一个 valid bit以及长度为 t=mbst = m-b-stag bits

每个地址会被分成三部分高位的 tt 位是 tag中间 ss 位是 set index低位 bb 位是 block offset获取存放在某个地址的数据时先根据其 set index 找到对应的 cache set再在 cache set 中找到 valid bit 为 1 且 tag 相符的 cache line最后通过 block offset 来从 block 中提取出单个 byte

在 cache miss 时需要从下一级获取数据存放到 cache 中如果对应的 cache set 所有 cache line 都满了就需要 evict 某个已有的 cache line

Conflict Miss

cache set 的设计基于一个假设即在局部内访问的数据地址的低位往往是不同的但实际上可能并非如此如果以 2s+b2^{s+b} 的倍数为地址间隔访问数据就可能连续访问同一个 cache set 内的数据导致 cache missEE 较小尤其是 E=1E=1这种情况更可能触发例如数组的大小是 22 的次幂而交替访问相邻数组的同一个下标时就可能这样这大概在 APIO2019 讲过当时我自然是啥都没听懂就只记得数组不要开 22 的次幂

Cache 的分类

E=1E=1 的 cache 称作 direct-mapped cache书上在这仔细解释了半天感觉废话好多啊

E>1E > 1 的 cache 称作 set associative cache其中s>0s > 0 的称作 E-way set associative caches=0s = 0 的称作 fully associative cache

Cache 的写入

在 cache hit 时有两种处理方式

  • write-through: 既修改 cache又修改下一级
  • write-back: 只修改 cache并且在每个 cache line 中添加一个 dirty bit用来记录是否被修改过在被 evict 时若 dirty 则写入下一级

在 cache miss 时也有两种处理方式

  • write-allocate: 先从下一级获取数据然后用与 cache hit 相同的处理方式
  • no-write-allocate: 直接写入下一级不获取到 cache 中

一般 write-through 和 no-write-allocate 搭配write-back 和 write-allocate 搭配

实际上cache 写入的优化是非常复杂的问题这里只是简单介绍了一下作为程序员可以把 cache 写入当成是 write-backwrite-allocate 的

i-cache 和 d-cache

只存放指令的 cache 称作 i-cache只存放数据的 cache 称作 d-cache都存放的 cache 称作 unified cache

将 i-cache 和 d-cache 分开就可以对它们分别进行优化例如 i-cache 是只读的二者可以有不一样的大小不一样的 cache set 设置将两者分开还可以一定程度上避免 conflict miss

在 Core i7 处理器中每个核有自己的 L1 i-cacheL1 d-cacheL2 unified cache所有核共享一个 L3 unified cache

Cache 的性能

cache 性能的衡量指标有

  • miss rate
  • hit rate
  • hit time: cache hit 时的访问用时
  • miss penalty: cache miss 时的访问用时与最终从哪一级获取到数据有关

一般来说cache 的参数对性能的影响是

  • cache size 越大hit rate 就越高但速度会慢
  • 增大 block size 可以更好地利用 spatial locality但也有可能因 cache line 数量减少而降低 hit rate并且会因为每次需要传递的数据变多而增大 miss penalty
  • 更大的 EE 可以降低 conflict miss 的可能性但也会使得 tag 匹配以及 victim line 的选择更加复杂从而增大 hit time 和 miss penalty在 Core i7 处理器中L1L2 cache 是 8-way 的L3 cache 是 16-way 的
  • write-through 实现起来更加容易并且在 read miss 时不会触发写入而 write-back 可以减少数据传递的总量降低 I/O bus 带宽的占用也可能降低数据传递的用时一般来说memory hierarchy 中较低的层级更倾向于使用 write-back

The Impact of Caches on Program Performance

The Memory Mountain

对一定 size 的数据按照一定的 stride 进行访问将 sizestride 与数据吞吐量的关系画成三维图像就得到了 memory mountain

CS:APP Figure 6.41 展示了一座 Core i7 的 memory mountain:这也是 CS:APP 的封面

Core i7 的 memory mountain

Memory mountain 较为完整地呈现了一个 memory system 的性能以及 temporal locality 和 spatial locality 对性能的影响

在每级 cache 的容量处吞吐量会发生明显的突变

在 size 相同时stride 越小吞吐量越高在 stride 接近 1 时变化尤其明显这和 Core i7 系统的 prefetching 技术息息相关处理器能够识别出 stride-1 reference pattern 并在实际访问到数据之前就进行 prefetch

矩阵乘法的循环顺序

书上在这讲了半天感觉废话好多我就放个测试结果上来吧CS:APP Figure 6.46

Core i7 矩阵乘法性能


  • 文章作者:
  • 原文链接:https://ouuan.moe/post/2022/12/csapp-6
  • 许可协议: 本文采用 CC BY-SA 4.0 许可协议进行授权,未满足 许可协议要求 不得转载。
  • 额外说明:本文包含若干截自 CS:APP 中的图片,本文作者对其不拥有版权。