计算机系统——第6章:存储器层次结构

存储器系统是一个具有不同容量,成本和访问时间的存储设备的层次结构。
存储器:
1.CPU寄存器:保存着最常用的数据
2.高速缓存存储器:作为一部分存储在相对慢速的主存储器中的数据和指令的缓冲区域。
3.主存:暂时存放存储在容量较大的,慢速磁盘上的数据,
4.磁盘:这些磁盘常常又作为存储在通过网络连接的其他机器的磁盘或磁带上的数据的缓冲区域
存储器层次结构由此从高到低依次排列,层次越高,速率越快。
CPU寄存器:零个周期
高速缓存:1-30个周期
主存:50-200个周期
磁盘:几千万个周期
计算机系统中一个基本而持久的思想:编写的应用程序使得它们的数据项存储在层次结构中较高的地方。这个思想围绕着计算机程序的一个称为局部性的基本属性。
具有良好局部性的程序倾向于一次又一次地访问相同的数据项集合或是访问临近的数据项集合,从存储器层次结构中较高层次中访问数据项,因此运行得更快。

6.1存储技术:
1.随机访问存储器(RAM):
包含两类:静态RAM(SRAM),动态RAM(DRAM),SRAM比DRAM更快,也更贵。
SRAM用来作为高速缓存存储器
DRAM用来作为主存以及图形系统的帧缓冲区。

SRAM将每个位存储在一个双稳态的存储器单元里,如同钟摆一样,当钟摆倾斜到最左边或最右边时,它是稳定的,在其他任何位置,钟摆都会倒向一边或另一边。所以由于SRAM存储器单元的双稳态特性,只要有电,它就会永远地保持它的值,即使有干扰,当干扰消除时,电路就会恢复到稳定值。
DRAM将每个位存储为对一个电容的充电。DRAM对干扰非常敏感,当电容的电压被扰乱后,它就永远不会恢复了。
传统DRAM:DRAM芯片中的单元被分成d个超单元,每个超单元都由w个DRAM单元组成。一个dxw的DRAM总共存储了dw位信息。
d个超单元被组织成一个r行c列的长方形阵列,rc=d,每个超单元有形如(i,j)的地址,i表示行,j表示列。
在一个16x8的DRAM芯片的组织中,有d=16个超单元,
分成了四行四列,w=8位
信息通过称为引脚的外部连接器流入和流出芯片,每个引脚携带位的信号
所以流入需要2个addr引脚,携带2位的行和列超单元地址
8个data引脚,他们能传送一个字节也就是8位到芯片或从芯片传出一个字节。
每个DRAM芯片都被连接到某个称为存储控制器的电路,这个电路可以一次传送w位到每个DRAM芯片或一次从每个DRAM芯片传出w位,存储控制器将行地址i发送到DRAM,然后是列地址j。DRAM
把超单元(i,j)的内容发回给控制器作为响应。行地址称为RAS请求,列地址称为CAS请求,
注意RAS和CAS请求共享相同的DRAM地址引脚。
就是先发送行地址i,DRAM将行i的整个内容都拷贝到一个内部行缓冲区,再发送列地址j,DRAM从行缓冲区拷贝出超单元(i,j)中的w位,并把它们发送到存储控制器。
电路设计者将DRAM组织成二维阵列而不是线性数组的原因是:降低芯片上地址引脚的数量,简化硬件设计,如果是线性数组会增加地址引脚。
但是二维阵列组织的缺点是必须分两步发送地址,这增加了访问时间。

存储器模块:多个DRAM芯片的组合
主存:存储器模块+存储控制器
DRAM芯片包装在存储器模块中,插到主板的扩展槽上
存储器模块基本思想:图示为8Mx8的DRAM芯片,8个芯片编号为0-7,每个超单元存储主存的一个字节,而用相应超单元为(i,j)的8个超单元来表示主存中字节地址A处的64位双字,DRAM0存储第一个字节,DRAM7存储最后一个字节。
要取出存储器地址A处的一个64位双字,存储控制器将A转换成一个超单元地址(i,j),将它发送到存储器模块,然后存储器模块将i,j广播到每个DRAM,每个DRAM输出它的(i,j)超单元的8位内容,模块中的电路收集这些输出,将他们合并成一个64位双字,再返回给存储寄存器。图示:

如果断电,DRAM和SRAM会丢失他们的信息,从这个意义上说,它们是容易丢失的
所以DRAM和SRAM都是易失性存储
非易失性存储器:断电后依然能保存其信息
有只读存储器(ROM):仅在生产时可以写入
可编程ROM(PROM):只能被编程一次
可擦写PROM(EPROM):可擦除和重编程数千次
电子可擦除PROM(EEPROM):课直接在印制电路卡上编程
闪存(FLASH):部分可擦除的EEPROM

非易失性存储器的使用:固件程序存储在ROM
固态硬盘
磁盘缓冲区

访问主存:数据流通过称为总线的共享电子电路在处理器和DRAM主存之间来来回回。
总线事务:每次CPU和主存之间的数据传送都是通过一系列步骤来完成的,这些步骤称为总线事务。
读事务:从主存传送数据到CPU
写事务:从CPU传送数据到主存
总线是一组并行的导线,能携带地址,数据和控制信号

示例计算机系统配置:
主要部件:CPU芯片,I/O桥的芯片组(其中包括存储控制器),以及组成主存的DRAM存储器模块,这些部件由一对总线连接起来,其中一条总线是系统总线,它连接CPU和I/O桥,另一条总线是存储器总线,它连接I/O桥和主存
计算机系统——第6章:存储器层次结构
我们考虑一下CPU执行一个加载操作会发生什么:
movl A,%eax
这里,A是地址,地址A的内容被加载到寄存器%eax中,CPU芯片上称为总线接口的电路发起总线上的读事务:
1.CPU将地址A放到系统总线上,I/O桥将信号传送到存储器总线
2.主存感觉到存储器总线上的地址信号,从存储器总线读地址,从DRAM取出数据字,并将数据写到存储器总线,I/O桥将存储器总线信号翻译成系统总线信号,然后沿着系统总线传递
3.CPU感觉到系统总线上的数据,从总线上读数据,并将数据拷贝到寄存器%eax中

磁盘:磁盘是由一个或多个叠放在一起的盘片组成,每个盘片有两面,盘片*有一个可以旋转的主轴
磁盘构造:磁道:当磁盘旋转时,磁头若保持在一个位置上,则每个磁头都会在磁盘表面划出一个圆形轨迹,这些圆形轨迹就叫做磁道,每个面上有若干同心环状的磁道
这些磁道用肉眼是根本看不到的,因为它们仅是盘面上以特殊方式磁化了的一些磁化区,磁盘上的信息便是沿着这样的轨道存放的
相邻磁道之间并不是紧挨着的,这是因为磁化单元相隔太近时磁性会相互产生影响,同时也为磁头的读写带来困难
扇区:磁盘上的每个磁道被间隙等分为若干个弧段,这些弧段便是磁盘的扇区
柱面:磁盘通常由重叠的一组盘片构成,每个盘面都被划分为数目相等的磁道,并从外缘的‘0’开始编号,具有相同编号的磁道形成一个圆柱,称之为磁盘的柱面
磁盘容量:
容量:被保存的最大位数,即最大容量
1GB=10^9bytes
容量取决于如下因素:
记录密度:磁道一英寸的段中可放入多少位数
磁道密度:自盘片中心出发半径为一英寸的段内可以有多少磁道数
面密度:记录密度与磁道密度的乘积
扇区的数量是固定的
磁盘面密度不断增长,外侧磁道固定扇区的间隙过大造成空间的浪费
旧的小容量硬盘:每个盘面上的每个磁道都划分为等量的扇区
优点:便于数据读取
缺点:对位于盘面外部的磁道太大,存在浪费

新硬盘:多区记录技术
多区记录技术将磁盘不同的磁道划分为多个区,同一区的磁道具有等量的扇区
扇区数由该区最里面的磁道包含的扇区数确定
位于盘面外侧区的磁道划分的扇区数量要比内侧的区的磁道多

磁盘容量:
(字节数/扇区)x(平均盘区数/磁道)x(磁道数/表面)x(表面数/盘片)x(盘片数/磁盘)
磁盘以扇区为单位来读写数据,对扇区的访问时间由三个部分组成:寻道时间,旋转时间,传送时间
寻道:读/写头移动,定位到期望的(目标扇区所在的)磁道上方
寻道时间:是指传动手臂将读写头移动到同心圆对应磁道的时间
盘面逆时针旋转,使得读/写头能位于目标扇区上方
旋转时间:指的等待目标扇区第一位数据到达读/写头下部的时间
如果手臂移动到对应磁道的时候读写位置刚过,就要等磁盘旋转一圈之后再读取;最大旋转时间为1/RPM(转速),平均旋转时间为1/RPM的一半(旋转延迟和寻道时间大致相当)
传送时间:从扇区第一个位处于读写头的时候,读写该扇区的时间。很明显与旋转速度和每条磁道的扇区数目有关

磁盘访问时间:
平均访问时间=平均寻道时间(厂商给出)+平均旋转时间+平均传送时间
转速是每分钟多少转
平均旋转时间:1/21/转速60 s
传送时间:在目标扇区读出所需数据的时间:1/转速一个磁道上平均扇区数目分之一60秒
访问时间主要由寻道时间和旋转时间确定,扇区中的第一位是最为“昂贵”的,剩下的同扇区其他位都是“免费”
逻辑磁盘块:
现代磁盘将其复杂的扇区结构简单抽象为:一组可用的扇区被视为一个逻辑块序列,编号为0,1,2
磁盘控制器维护着逻辑块号和实际物理磁盘扇区之间的映射关系
磁盘控制器是一个硬件/固件设备
控制器上的固件执行一个快速表查找,将一个逻辑块号翻译成一个(盘面,磁道,扇区)的三元组,以唯一表示对应的物理扇区
控制器格式化磁盘时会给每一个区预留一组柱面作为备用
当一个扇区不能访问的时候,磁盘控制器启用备用扇区,这样使得磁盘不会因为一个或多个柱面损坏而不能用了
这就是为什么磁盘硬件商所说的格式化容量比最大容量要小
访问磁盘:(磁盘-主存-CPU)
对磁盘的数据访问,并不是直接从磁盘到CPU,而是通过存储器作为桥梁,达到快速访问
CPU采用存储器映射I/O技术向I/O设备发送命令
I/O端口:内存地址空间有一块地址用于与I/O设备通信保留的,这块地址称为内存的I/O端口
设备与I/O端口的映射:设备连接到总线后,该设备与内存的一个或多个I/O端口相关联(由CPU帮助完成),从而实现映射
磁盘控制器被映射到端口0xa0,CPU对地址0xa0发出三条指令,发起磁盘读
发送一个命令字,要求读磁盘内容,要求读完以后报告给CPU(中断)
指明要读取的具体逻辑块号码
指明拷贝到主存的地址
磁盘控制器异步读取磁盘数据到主存
通过中断信号来通知CPU读取操作完成
高速CPU不用一直等待低速的外设读取

三个步骤:1.CPU发起一个磁盘读,将命令,逻辑块号和目的存储器地址写到与磁盘相关联的存储器映射地址
2.磁盘控制器读扇区,并执行到主存的DMA传送
3.当DMA传送完成,磁盘控制器用中断的方式通知CPU
计算机系统——第6章:存储器层次结构
CPU没有干预磁盘->主存的数据传送
CPU比磁盘速度快几千万倍
CPU发起读指令以后,就不用管了,等到磁盘控制器将内容全部复制到主存中,磁盘控制器发起一个中断,高速CPU,不要做自己的其他事情了,你之前让我读磁盘的内容已经全部读到主存中去了

DMA传送:设备可以自己执行读或写总线事务,不需要CPU干涉的过程,这个过程就是DMA(直接存储器访问)

固态硬盘SSD-不再依靠旋转
计算机系统——第6章:存储器层次结构
一个闪存由B个块组成
每个块由P页组成
数据以页为单位读写
只有在一页所属的块整个被擦除后,才能写这一页

固态硬盘性能参数
读:顺序读吞吐量,随机读吞吐量,随机读访问时间
写:顺序写吞吐量,随机写吞吐量,随机写访问时间
随机写为什么这么慢
擦除块需要相对较长的时间
只有在一页所属的块整个被擦除之后,才能写这一页
写操作试图更新已包含数据的页P,块中的所有带有用数据的页必须事先被拷贝到一个新块
然后进行对页p的写操作
固态硬盘vs旋转硬盘
固态硬盘无移动的部件,所以访问更快,能耗更低,更结实
但是可能会磨损,比机械硬盘贵
所以数据硬盘用机械硬盘,系统硬盘用固态硬盘,所以下载最好下载到数据硬盘,因为下载会进行频繁的读写

6.2局部性:
缓解了CPU-主存执行速度差异
局部性原理:一个编写良好的程序倾向于引用最近引用过的数据本身,或者引用的数据项邻近于其最近引用过的数据项
一个便携良好的计算机程序常常具有良好的局部性,
局部性通常由两种不同的形式:
1.时间局部性:在一个具有良好时间局部性的程序中,被引用过一次的存储器位置很可能在不远的将来再被多次引用
2.空间局部性:在一个具有良好空间局部性的程序中,如果一个存储器位置被引用过了一次,那么程序很可能在不远的将来来引用附近的一个存储器位置。
重复引用同一个变量的程序有良好的时间局部性
对于步长为k的引用模式的程序,步长越小,空间局部性越好
在存储器中以大步长跳来跳去的程序空间局部性会很差
对于取指令来说,循环有好的时间和空间局部性
循环体越小,循环迭代次数越多,局部性越好
6.2.1对程序数据引用的局部性
计算机系统——第6章:存储器层次结构
变量sum在每次循环迭代中被引用一次,因此,对于sum来说,有好的时间局部性,但没有空间局部性。
向量v的元素是被顺序读取的,一个接着一个,按照它们的存储器顺序,因此具有很好的空间局部性,但是时间局部性很差,因为每个向量元素只被访问一次。
因此对于循环体中的每个变量,要么有好的空间局部性,要么有好的时间局部性,所以这个函数有好的局部性。
我们说像这个函数一样顺序访问一个向量每个元素的函数,具有步长为1的引用模式。一个连续向量中,每隔k个元素访问,就被称为步长为k的引用模式,一般而言,随着步长的增加,空间局部性下降。
比如双重嵌套循环,如果先读第一行的元素,再读第二行,就是步长为1的引用模式,具有良好的空间局部性
如果先读列的haul,就是步长为N的引用模式,空间局部性下降
6.2.2取指令的局部性
因为程序指令存放在存储器中,CPU必须取出这些指令,例如,上述图片for循环体里的指令是按照连续的存储器顺序执行的美因茨循环具有良好的空间局部性,因为循环体被执行多次,也有很好的时间局部性。

存储器层次结构
速度越快的存储技术每字节成本要比速度慢的技术高,且容量较小,也需要更多能耗
L0:寄存器:保存从L1级高速缓存中取出的字
L1:L1高速缓存(SRAM):L1高速缓存保存着从L2高速缓存取出的缓存行
L2:L2高速缓存(SRAM):L2高速缓存保存着从L3高速缓存取出的缓存行
L3:L3高速缓存(SRAM):L3高速缓存保存着从主存取出的缓存行
L4:主存(DRAM):主存保存着从本地磁盘取出的数据块
L5:本地二级存储(本地磁盘):保存着从远程网络服务器上取出的文件
L6:远程二级存储(磁带,分布式文件系统,web复位器)
越上面更快,更小,每字节成本更高

高速缓存:一个小而快的存储设备,它作为存储在更大,更慢的设备中的数据对象的缓冲区域
存储层次结构中心思想:对于每个k,位于k层的更快,更小的存储设备作为位于k+1层的更大,更慢的存储设备的缓存
存储层次为什么有效?
由于局部性,相对于层次k+1的数据,程序趋向于更频繁访问层次k的数据
因此,允许层次k+1的存取速度更慢,空间更大,单位价格更便宜
这种存储层次构建了一个价格接近最底层存储层次,大容量存储,而读取数据的速率接近最顶层的存储层次
高速缓存的基本概念:
计算机系统——第6章:存储器层次结构
高速缓存是主存的子集
高速缓存的基本概念:命中
需要块b中的数据
块b就在高速缓存中,命中
块b不在高速缓存中,不命中,从主存中取出块b,块b被存入到高速缓存
放置策略:决定b被放在哪,随机?
替换策略:决定驱逐哪一块(牺牲者)

不命中分类:
冷不命中
如果k层缓存是空的,则对任何数据的访问都是不命中的,属于短暂事件,在缓存暖身后不会出现

冲突不命中:
k层缓存比k+1层缓存空间更小,只能存放k+1层缓存数据块的子集
例如,k+1层的块i必须放在k层的(i mod 4)块中(严格放置策略)
缓存够大,但是所需的多个数据块都被映射到同一个缓存块中,导致一直发生冲突不命中
例如,块0和块8映射到同一个缓存块,反复引用块0,8,0,8,0,8那么每次都会产生冲突

容量不命中:
需访问相对稳定不变的数据集合,这个块的集合称之为工作集,其大小超过缓存大小

存储层次中的各种缓存
寄存器:缓存4-8字节
TLB:缓存地址翻译
L1-L3高速缓存:缓存64位的块
虚拟内存:缓存主存中的页

小结:CPU与主存及大容量存储设备之间的速度差异过大,编写良好的程序应具有良好的局部性,存储层次正是应用局部性原理,基于缓存,以缩小前述差异

高速缓存:
高速缓存存储器是什么,高速缓存的结构,读与写
高速缓存与性能(存储器山,提高空间局部性(重新排列循环),提高时间局部性(分块传输))

高速缓存存储器是什么?
小而快的SRAM存储器
代替频繁访问主存数据块
保存内容是主存数据的子集

CPU首先在L1,L2和L3高速缓存中寻找所需数据,若找不到再访问主存

计算机系统——第6章:存储器层次结构
高速缓存的结构,读和写
高速缓存在CPU内
L1高速缓存包括指令高速缓存和数据高速缓存
L3三级缓存是多个CPU共享的

32位处理器的寻址空间
32位处理器:32位是指CPU操作总线的数据宽度和地址表示都是32位
32位的数据总线,一次操作最大可执行32位的计算(字长)
32位的地址总线,能够寻址的地址范围是2^32个地址
地址范围从[0x00000000,0xFFFFFFFF],在此范围内的地址空间一个有2^32个,也就是我们常说的4G个地址
CPU里处理地址中的值,每个地址对应一个字节(Byte)大小的存储空间,一个字节由8个位(bit)构成,也就是说,每个地址代表8位存储空间,因此有共有4GB存储空间

高速缓存的存储空间:
内存存储空间:一共2^32个地址,每个地址存储一个字节
高速缓存总是比内存小得多得多,L1一般就几十上百KB而已,离4GB远着呢,那么CPU的这32位地址怎么用呢?
高速缓存既然小,那一定是被地址空间所共享,4GB中任何一个字节都可能被缓存在L1中,因此,需要有办法进行区分
高速缓存存储空间设计:存储分组,组中分行,行中分字节

通用的高速缓存存储器结构
(S,E,B,m)
计算机系统——第6章:存储器层次结构
高速缓存被划分成S个组,每个组有E行数据
每行中有一个高速缓存块
每个高速缓存块的大小是B个字节
块中有有效位:表明这个块的数据是否有效,标记位:标记着对应内存哪个地址
但是有效位和标记位不占用高速缓存大小
若存储器地址有m=32位
高速缓存大小C=SEB data bytes

访问高速缓存存储空间中的字节
基本思路:先找到组,然后找到行,接着找到块,最后再通过偏移找到块中的具体字节

CPU在访问高速缓存时,需要找上述这么多信息,那么在定义高速缓存的地址概念时,就必须得相应的字段来标识
对于m位CPU数据宽度。有M=2^m个地址,可标识M字节的存储空间

标识组:要在S个组里面找出唯一的一个组信息,需要占用m位地址表示(CPU数据宽度)的多少位呢?如果S=2^s,那我们就需要在m位地址表示中划分出s位来标识组信息
标识行:每组有E行,在m位地址表示中划分t位来标识行信息
标识块字节偏移:行中的缓存块字节偏移(每行一个块,每块多个字节),既然有B字节,而B=2^b字节,也就是说还要从m位地址表示中分配b位来标识块字节偏移
完整的m位地址表示被分开,而且它还得保证刚好够用,很显然我们就能得到t=m-(s+b)

标记位的作用:
t位标记位用于唯一标识组内的行
当该行的标记位与内存地址A中的标记位相匹配时,组中这行才包括地址A中存储的字
一次存取,加工和传送的数据长度称为字word,一个字通常由一个或多个字节构成

存储层次的四个问题
当把一个块调入高一层(靠近CPU)存储器时,可以放在哪些位置上(映射规则)
当所要访问的块在高一层存储器中时,如何找到该块(查找算法)
当发生失效时,应替换哪一块?(替换算法)
当进行写访问时,应进行哪些操作(写策略)

映射规则1:全相联映射
所谓全相联:主存中的任一块可以被放置到高速缓存中的任意一个位置:空间利用率最高,冲突概率最低,实现最复杂

映射规则2:直接映射
所谓直接映射:主存中的每一块只能被放置到高速缓存中唯一的一个地址
特点:空间利用率最低,冲突概率最高,实现最简单

映射规则3:组相连映射
所谓组相连映射:主存中的每一块可以被放置到高速缓存中唯一的一个组中的任何一个位置
一种直接映射和全相联的折中

根据E(每个组的高速缓存行数)高速缓存被分为不同的分类
直接映射(每个组只有一行的简单访问模式)
组相连高速缓存(每组多于1行的高速缓存)
全相连高速缓存(只有一个组)

读高速缓存:
组选择,行匹配,字抽取
定位到组(根据组索引)
检查组内任意行是否与标记匹配
如果匹配且行有效,则命中,以偏移量定位数据

行匹配:检查有效位是否为1,如果标记位与高速缓存中的标记位相匹配,这就是命中

字抽取:块偏移选择起始字节,假设字长为4字节

如果标记位不匹配,旧行被驱逐并被替换

E路组相连高速缓存:如果不命中,选择组内一行进行驱逐和替换
替换策略:随机,最不常使用,最近最少使用

如何写入高速缓存
数据存在多重拷贝:L1,L2,主存,硬盘
如果写命中
直写:立即将数据写回到主存
写回:延迟写回主存,直到数据被替换时才更新主存内容,那么每个高速缓存行维护一个额外的修改位dirty bit
如果写不命中:
写分配:加载相应的低一层中的块到高速缓存,然后更新高速缓存:若空间局部性好,则性能好
非写分配:直接写到低一层的主存中

典型:直写+非写分配
写回+写分配(较低层次的缓存更可能使用,主存–>磁盘)

存储器山:
读吞吐量:单位时间从存储器读出字节的数量
存储器山:通过空间和时间局部性的函数测量的读吞吐率:表明存储系统性能特点
尽量让程序从L1中得数据

在一行中访问列数据
for(i=0;i<N;i++)
sum+=a[0][i]
连续访问数组元素,若块大小B大于a[i][j]的大小,利用空间局部性
不命中率=sizeof(a[i][j]/B

在一列中依次访问行数据
for(i=0;i<n;i++)
sum+=a[i][0];
远距离访问数据
无空间局部性
不命中率=1