6.1 存储技术
6.1.1 RAM和ROM
- RAM(Random-Access Memory):随机访问存储器,断电会丢失信息。分为静态(SRAM)和动态(DRAM)
- SRAM:作为高速缓存(如用于CPU芯片上)。速度快但价格贵。电路含6个晶体管,有双稳态特性,对干扰不敏感
- DRAM:作为主存(内存)及图形系统的帧缓存区。价格便宜但速度慢。电路每个单元由电容+访问晶体管组成,对干扰敏感
- 主存(DRAM)访问原理
- DRAM芯片划分为个超单元(supercell),信息通过引脚(pin),经过内存控制器流向CPU(图6-3)
- 内存控制器将行地址、列地址发送到DRAM,DRAM取出超单元()的内容返回内存控制器
- 内存模块包含N个DRAM芯片,取地址时向DRAM芯片广播,取出超单元后合并为字,最后返回内存控制器(图6-5)
-
ROM(Read-Only Memory):历史原因被称为只读存储器,实际可读可写。下电后仍保留信息
- PROM:可编程ROM,但只能用高电流熔断编程1次
- EPROM:可擦写可编程ROM,通过紫外线擦除,重编程次数达1000次
- EEPROM:电子可擦除PROM,编程次数达次
- 闪存(flash memory):一类非易失性存储器,基于EEPROM,用于数码相机、手机、音乐播放器、PDA、笔记本等等。SSD也基于闪存
-
CPU访问主存:
movq A, %rax读操作流程(写操作类似):- CPU将地址A放到系统总线上,IO桥将信号传递到内存总线
- 主存收到内存总线的信号,从内存总线读地址,从DRAM取出数据字,将数据写到内存总线。IO桥将内存总线信号翻译成系统总线信号,沿系统总线传递
- CPU收到系统总线的数据,从总线读数据,复制到寄存器
%rax上
6.1.2 机械硬盘
-
磁盘:大量数据的存储设备,数量级可达几百到几千千兆字节。速度比RAM存储器慢得多
-
机械硬盘HDD:由盘片组成,围绕主轴以固定速率旋转(5400~1500RPM)。盘片表面(正反两面)划分为磁道的同心圆,磁道进一步划分成扇区。每个扇区包含相等数量的数据位(通常是512字节)。
- 柱面:所有盘片到主轴中心距离相等的磁道的集合
- 寻道:磁盘用读/写头读写磁盘上的数据位,这些读/写头连接到传送臂上。驱动器通过半径方向上的移动,将读写头定位到任何磁道上,此过程称为寻道。
- 磁盘控制器:将逻辑块翻译成(盘面, 磁道, 扇区)的三元素,将读/写头移动到适当的柱面等待读写。读写的位放到控制器的小缓冲区中,最后复制到主存。
-
机械硬盘容量:一个磁盘可以就的最大位数称为容量
- 机械硬盘访问时间:寻道时间+旋转时间+传送时间
- 寻道时间:移动传送臂,读取到某个目标磁道的时间
- 旋转时间:驱动器等待扇区旋转到读/写头下的时间
- 传送时间:驱动器读取整个扇区内容的时间
- CPU访问机械硬盘
- CPU将命令、逻辑块号和内存地址写到磁盘相关联的内存映射地址,发起磁盘读
- 磁盘控制器读扇区,并执行到主存的DMA传送
- 当DMA传送完成时,磁盘控制器用中断的方式通知CPU
6.1.3 固态硬盘
- 固态硬盘SSD:基于闪存的存储技术,由一个或多个闪存芯片和闪存翻译层组成。
- 闪存芯片:代替旋转磁盘中的机械驱动器
- 闪存翻译层:是一个固件代替磁盘控制器,将对逻辑块的请求翻译成对底层物理设备的访问。
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 存储器层次结构
- 为了兼顾性能和成本,计算机系统采用层次结构的方式访问数据:从高层往底层走,存储设备更慢、更便宜、更大
6.3.1 高速缓存
- 高速缓存(cache):层次结构中,第k层作为k+1层更大更慢存储设备的缓存
- 缓存命中:程序需要第k+1层某个数据时,优先从第k层缓存查找。如果找到即缓存命中;否则缓存不命中。
- 替换策略:缓存不命中后,需要使用**替换算法(如LRU)**替换或驱逐一个块。
- LRU:最近最少使用替换策略,选择最后被访问的块用于替换
6.4 高速缓存存储器
【背景】随着CPU的性能和主存性能差距不断增大,系统设计者在CPU寄存器文件和主存间插入了L1缓存(4个时钟周期)、L2缓存(10个时钟周期)、L3缓存(50个时钟周期)。书中后续主要讨论L1缓存
6.4.1 高速缓存存储器的组织结构
- cache line:计算机系统的高速缓存有位,其被组织成个高速缓存组(cache set) 的数组,每个组中包含个高速缓存行(cache line)。每行由字节的数据块(block) 组成。另外,每行有1个有效位(valid bit) 标志这个行是否有意义;有个 标记位(tag bit) 唯一标识存储在高速缓存行中的块
- block:用元组()唯一标识
- 高速缓存大小:所有block的和:
6.4.2 直接映射高速缓存
- 结构:每个组只有一行()的高速缓存,内存地址和缓存位置关系是事先划分好的
- 读缓存过程:组选择、行匹配、字选择
- 组选择:从内存地址中间部分抽取s位,作为组索引,找到高速缓存对应的组(图6-28表示选择组1)
- 为啥用中间部分作为索引?与高速缓存效率有关,参考书P432节旁注
- 行匹配:确定缓存是否命中,1)有效位为1;2)内存地址的前t位与高速缓存的tag是否匹配(图6-29表示地址有效)
- 字选择:确定所需要的字的偏移量(图6-29表示从块的字节4开始读取)
- 组选择:从内存地址中间部分抽取s位,作为组索引,找到高速缓存对应的组(图6-28表示选择组1)
- 不命中处理:将被请求的块放到对应的cache line即可
【例】高速缓存(S,E,B,m) = (4,1,2,4),则内存地址被映射到高速缓存的位置如下:
- 一开始高速缓存是空的
- 读取地址0的字,不命中。将块0加载到高速缓存的组0中,组包含地址0和地址1的数据
- 读取地址1的字,命中,直接返回(局部性原理)
- 读取地址13的字,不命中。将块6加载到高速缓存的组2中
- 读取地址8的字,不命中。将块4加载到高速缓存的组0中
- 读取地址0的字,不命中。将块0加载到高速缓存的组0中(这里虽然高速缓存还有空间,但是产生了冲突不命中现象)
6.4.3 组相联高速缓存
-
结构:每个组有多个cache line()。可以解决冲突不命中问题
-
读缓存过程:组选择、行匹配、字选择
- 组选择:与直接映射一致
- 行匹配:遍历组cache line,确定缓存是否命中,1)有效位为1;2)内存地址的前t位与高速缓存的tag是否匹配
- 字选择:与直接映射一致
-
不命中处理:如果有空行(有效位为0),直接替换;如果没有空行,使用
LRU等策略替换非空行
6.4.4 全相联高速缓存
-
结构:整个cache只有1个set,包含所有cache line()。C表示cache容量,B表示数据块大小
-
读缓存过程:组选择、行匹配、字选择
- 组选择:没有组索引位,无需组选择
- 行匹配:与组相联一致
- 字选择:与组相联一致
-
不命中处理:如果有空行(有效位为0),直接替换;如果没有空行,使用LRU等策略替换非空行
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来读数据,可使程序的空间局部性最大
- 存储器读入数据对象,就尽可能多使用它,可使程序的时间局部性最大