【CSAPP笔记】第六章 存储器层次结构

470 阅读8分钟

6.1 存储技术

6.1.1 RAM和ROM

  • RAM(Random-Access Memory):随机访问存储器,断电会丢失信息。分为静态(SRAM)和动态(DRAM)
    • SRAM:作为高速缓存(如用于CPU芯片上)。速度快但价格贵。电路含6个晶体管,有双稳态特性,对干扰不敏感
    • DRAM:作为主存(内存)及图形系统的帧缓存区。价格便宜但速度慢。电路每个单元由电容+访问晶体管组成,对干扰敏感

image-20221217184138597.png

  • 主存(DRAM)访问原理
    • DRAM芯片划分为d×wd \times w超单元(supercell),信息通过引脚(pin),经过内存控制器流向CPU(图6-3)
    • 内存控制器将行地址ii、列地址jj发送到DRAM,DRAM取出超单元(i,ji, j)的内容返回内存控制器
    • 内存模块包含N个DRAM芯片,取地址时向DRAM芯片广播,取出超单元后合并为字,最后返回内存控制器(图6-5)

image-20221217185450234.png

image-20221217190140650.png

  • ROM(Read-Only Memory):历史原因被称为只读存储器,实际可读可写。下电后仍保留信息

    • PROM:可编程ROM,但只能用高电流熔断编程1次
    • EPROM:可擦写可编程ROM,通过紫外线擦除,重编程次数达1000次
    • EEPROM:电子可擦除PROM,编程次数达10510^5
    • 闪存(flash memory):一类非易失性存储器,基于EEPROM,用于数码相机、手机、音乐播放器、PDA、笔记本等等。SSD也基于闪存
  • CPU访问主存movq A, %rax 读操作流程(写操作类似):

    • CPU将地址A放到系统总线上,IO桥将信号传递到内存总线
    • 主存收到内存总线的信号,从内存总线读地址,从DRAM取出数据字,将数据写到内存总线。IO桥将内存总线信号翻译成系统总线信号,沿系统总线传递
    • CPU收到系统总线的数据,从总线读数据,复制到寄存器%rax

image-20221217210945071.png

6.1.2 机械硬盘

  • 磁盘:大量数据的存储设备,数量级可达几百到几千千兆字节。速度比RAM存储器慢得多

  • 机械硬盘HDD:由盘片组成,围绕主轴以固定速率旋转(5400~1500RPM)。盘片表面(正反两面)划分为磁道的同心圆,磁道进一步划分成扇区。每个扇区包含相等数量的数据位(通常是512字节)。

    • 柱面:所有盘片到主轴中心距离相等的磁道的集合
    • 寻道:磁盘用读/写头读写磁盘上的数据位,这些读/写头连接到传送臂上。驱动器通过半径方向上的移动,将读写头定位到任何磁道上,此过程称为寻道
    • 磁盘控制器:将逻辑块翻译成(盘面, 磁道, 扇区)的三元素,将读/写头移动到适当的柱面等待读写。读写的位放到控制器的小缓冲区中,最后复制到主存。
  • 机械硬盘容量:一个磁盘可以就的最大位数称为容量

磁盘容量=字节数扇区×平均扇区数磁道×磁道数表面×表面数盘片×盘片数磁盘磁盘容量=\frac{字节数}{扇区} \times \frac{平均扇区数}{磁道} \times \frac{磁道数}{表面} \times \frac{表面数}{盘片} \times \frac{盘片数}{磁盘}
  • 机械硬盘访问时间:寻道时间+旋转时间+传送时间
    • 寻道时间:移动传送臂,读取到某个目标磁道的时间
    • 旋转时间:驱动器等待扇区旋转到读/写头下的时间
    • 传送时间:驱动器读取整个扇区内容的时间
  • CPU访问机械硬盘
    • CPU将命令、逻辑块号和内存地址写到磁盘相关联的内存映射地址,发起磁盘读
    • 磁盘控制器读扇区,并执行到主存的DMA传送
    • 当DMA传送完成时,磁盘控制器用中断的方式通知CPU

image-20221217211245525.png

image-20221218150906868.png

6.1.3 固态硬盘

  • 固态硬盘SSD:基于闪存的存储技术,由一个或多个闪存芯片和闪存翻译层组成。
    • 闪存芯片:代替旋转磁盘中的机械驱动器
    • 闪存翻译层:是一个固件代替磁盘控制器,将对逻辑块的请求翻译成对底层物理设备的访问。

image-20221218152900532.png

6.2 局部性原理

  • 局部性原理:良好的计算机程序的应遵循局部性原理,包含时间局部性原理和空间局部性原理
    • 空间局部性:一个内存位置被引用,程序可能在不久的将来引用附近的内存位置
    • 时间局部性:一个内存位置被引用,很可能在补救的将来被多次引用

【例】局部性原理:标量sum遵循时间局部性;向量v遵循空间局部性

int sumvec(int v[N]) {
    int i, sum = 0;
    for (i = 0; i < N; i++)
        sum += v[i];
    return sum;
}

【例】不遵循空间局部性原理,性能很差。第3、4行对调后使得性能提升

int sumarraycols(int a[M][N]) {
    int i, j, sum = 0;
    for (j = 0; j < N; i++)
        for (i = 0; i < M; i++)
            sum += a[i][j];
    return sum;
}

6.3 存储器层次结构

  • 为了兼顾性能和成本,计算机系统采用层次结构的方式访问数据:从高层往底层走,存储设备更慢、更便宜、更大

image-20221218154549185.png

6.3.1 高速缓存

  • 高速缓存(cache):层次结构中,第k层作为k+1层更大更慢存储设备的缓存
    • 缓存命中:程序需要第k+1层某个数据时,优先从第k层缓存查找。如果找到即缓存命中;否则缓存不命中
    • 替换策略:缓存不命中后,需要使用**替换算法(如LRU)**替换或驱逐一个块。
      • LRU:最近最少使用替换策略,选择最后被访问的块用于替换

image-20221218163321090.png

6.4 高速缓存存储器

【背景】随着CPU的性能和主存性能差距不断增大,系统设计者在CPU寄存器文件和主存间插入了L1缓存(4个时钟周期)、L2缓存(10个时钟周期)、L3缓存(50个时钟周期)。书中后续主要讨论L1缓存

image.png

6.4.1 高速缓存存储器的组织结构

  • cache line:计算机系统的高速缓存有mm位,其被组织成SS高速缓存组(cache set) 的数组,每个组中包含EE高速缓存行(cache line)。每行由B=2bB=2^b字节的数据块(block) 组成。另外,每行有1个有效位(valid bit) 标志这个行是否有意义;有tt标记位(tag bit) 唯一标识存储在高速缓存行中的块
  • block:用元组(S,E,B,mS, E, B, m)唯一标识
  • 高速缓存大小:所有block的和:C=S×E×BC=S \times E \times B

image-20221218165409668.png

image-20221218165919254.png

6.4.2 直接映射高速缓存

image-20221218174547816.png

  • 结构:每个组只有一行(E=1E=1)的高速缓存,内存地址和缓存位置关系是事先划分好的
  • 读缓存过程:组选择、行匹配、字选择
    • 组选择:从内存地址中间部分抽取s位,作为组索引,找到高速缓存对应的组(图6-28表示选择组1)
      • 为啥用中间部分作为索引?与高速缓存效率有关,参考书P432节旁注
    • 行匹配:确定缓存是否命中,1)有效位为1;2)内存地址的前t位与高速缓存的tag是否匹配(图6-29表示地址有效)
    • 字选择:确定所需要的字的偏移量(图6-29表示从块的字节4开始读取)
  • 不命中处理:将被请求的块放到对应的cache line即可

image-20221218175046840.png

image-20221218175107603.png

【例】高速缓存(S,E,B,m) = (4,1,2,4),则内存地址00002111120000_2 \sim 1111_2被映射到高速缓存的位置如下:

image-20221218180033440.png

  • 一开始高速缓存是空

image-20221218180102876.png

  • 读取地址0的字,不命中。将块0加载到高速缓存的组0中,组包含地址0和地址1的数据

image-20221218180115601.png

  • 读取地址1的字,命中,直接返回(局部性原理)
  • 读取地址13的字,不命中。将块6加载到高速缓存的组2中

image-20221218180127798.png

  • 读取地址8的字,不命中。将块4加载到高速缓存的组0中

image-20221218180141755.png

  • 读取地址0的字,不命中。将块0加载到高速缓存的组0中(这里虽然高速缓存还有空间,但是产生了冲突不命中现象)

image-20221218180153480.png

6.4.3 组相联高速缓存

image-20221218181921124.png

  • 结构:每个组有多个cache line1<E<C/B1<E<C/B)。可以解决冲突不命中问题

  • 读缓存过程:组选择、行匹配、字选择

    • 组选择:与直接映射一致
    • 行匹配遍历组cache line,确定缓存是否命中,1)有效位为1;2)内存地址的前t位与高速缓存的tag是否匹配
    • 字选择:与直接映射一致
  • 不命中处理:如果有空行(有效位为0),直接替换;如果没有空行,使用LRU等策略替换非空行

image-20221218183247512.png

6.4.4 全相联高速缓存

image-20221218182544377.png

  • 结构:整个cache只有1个set,包含所有cache lineE=C/BE=C/B)。C表示cache容量,B表示数据块大小

  • 读缓存过程:组选择、行匹配、字选择

    • 组选择:没有组索引位,无需组选择
    • 行匹配:与组相联一致
    • 字选择:与组相联一致
  • 不命中处理:如果有空行(有效位为0),直接替换;如果没有空行,使用LRU等策略替换非空行

image-20221218183217023.png

6.4.5 写缓存问题

  • 写命中(write hit):cache中找到写的副本

    • 直写(write-through):立即写入内存(低层次的存储)中。好处是内存数据永远是最新的,坏处是每次写都有总线流量
    • 写回(write-back):只写缓存不写内存,尽可能地推迟更新。好处是减少总线流量,坏处是增加了复杂性(需增加dirty位)
  • 写不命中(write miss):cache中没有找到写的副本

    • 写分配(write-allocate):内存的块加载到cache中,然后写cache,对应write-back
    • 非写分配(not-write-allocate):直接写到内存中,对应write-through

总结

利用局部性原理,可写出更有效的程序:

  • 大部分计算和对内存的访问集中在程序的内循环上
  • 按照数据对象存储在内存的顺序、以步长为1来读数据,可使程序的空间局部性最大
  • 存储器读入数据对象,就尽可能多使用它,可使程序的时间局部性最大