一、储存技术
1.1 随机访问存储器
随机访问存储器【Random Access Memory,RAM】封装在芯片内,基本储存单位是单元,容量为1bit,多个RAM芯片组成了存储器。RAM分为静态随机访问存储器【Static Random Access Memory,SRAM】与动态随机访问存储器【Dynamic Random Access Memory,DRAM】。
SRAM将每个位存储在一个双稳态的存储器单元内,每个单元使用一个六晶体管电路实现的,其可以无限期的保持在两个不同的电压状态之一,而其他任何状态都是不稳定的。即使有电子噪声扰乱电压,当干扰消除时,电路就会恢复到稳定值。
DRAM将每个位储存位对一个电容充电,每个单元由一个电容和一个晶体管组成,对干扰非常敏感,当电容的电压被扰乱后就永远不会恢复了。DRAM在现代集成了更好的接口逻辑与更快的I/O传输接口,形成了双倍数据速率同步DRAM【DDR SDRAM】,以缓冲区划分为DDR,DDR2,DDR3等。
SRAM的取存比DRAM快,对诸如光和电噪声不敏感,代价是SRAM单元比DRAM单元使用更多的晶体管,密集度低,而且更贵,功耗更大。SRAM一般用于高速缓存,而DRAM一般用于帧缓冲区。
SRAM和DRAM都属于易失性储存器,在断电后数据丢失。此外,储存器还包括只读存储器【ROM】,可编程ROM【PROM】等。
CPU与储存器通过系统总线与储存器总线进行传输地址、数据和控制信号。
1.2 磁盘储存
磁盘是广为应用的保存大量数据的储存设备,储存的数据的数量级可以达到成百上千的千兆字节。而从磁盘上读信息的时间位毫秒级,比从RAM读慢了数万倍。
磁盘由盘片构成,每个盘面有两个表面,表面覆盖着磁性记录材料。盘片中央有一个可以旋转的主轴,使得盘面以固定的旋转速率旋转,常用单位为转每分钟【Revolution Per Minute,RPM】。磁盘通常包括一或多个盘片。
盘片的表面由一组称为磁道的同心圆组成,每个磁道被划分为一组扇区。每个扇区包含相等数量的数据位,编码在扇区上的磁性材料中。扇区之间由间隙分隔开,用于储存标识扇区的格式化位。
磁盘可以记录的最大位数称为其最大容量,简称为容量,一般以千兆字节GB为单位,在这里,
1
G
B
=
1
0
9
B
y
t
e
1GB = 10^9 Byte
1GB=109Byte。容量由如下技术因素决定:
-记录密度:磁道每单位长度段可放入的位数,单位为位/英寸;
-磁道密度:从盘片中心出发半径上单位长度段的磁道数,单位为道/英寸;
-面密度:记录密度与磁道密度的积,单位位/平方英寸。
那么容量为字节数每扇区×平均扇区数每磁道×磁道数每表面×表面数每盘片×盘片数每磁盘。
磁盘使用读/写头来读写储存在磁性表面的位。驱动器通过传动壁将读/写头定位到任何磁道上,称为寻道。在寻道完成后,读/写头在磁盘表面悬浮,盘面逆时针高速旋转,完成数据的读写。
磁盘以扇区为大小的块读写数据。对扇区的访问时间有如下三个主要部分:
-寻道时间,为了读取某个目标扇区的内容,传动壁首先将读/写头定位到包含目标扇区的磁道上;
-旋转时间,在寻道完成后,驱动器需要等待目标扇区的第一个位旋转到读/写头下,最坏的情况为读写头刚刚错过目标扇区,必须等待磁盘旋转一整圈;
-传送时间:旋转完成后,读或写该扇区内容的时间,依赖于旋转速度与每磁道的扇区数。
现代磁盘以简单的抽象来表示复杂的磁盘构造,将磁盘抽象为扇区大小的逻辑块,逻辑块与物理扇区之间的映射关系由磁盘控制器维护,其将逻辑块号转换为盘面, 磁道,扇区的三元组。
磁盘控制器对磁盘格式化后才能在磁盘上存储数据,包括用标识扇区的信息填写间隙,标识出有故障的柱面并不使用,并在每个扇区预留一组柱面备用,使得格式化容量小于最大容量。
CPU与磁盘通过系统总线与I/O总线连接。在CPU访问磁盘时,磁盘会将信息传输给储存器,并在传输结束后用过中断的方式通知CPU。
固态硬盘【SSD】基于电子可擦除PROM【EEPROM】,即闪存技术,其访问速度高于磁盘,但其闪存块会因为反复擦写后磨损。
二、局部性
解决CPU与存储器之间速度差距的关键时程序中特有的局部性特点。
2.1 局部性原理
程序倾向于引用邻近于其他引用过的数据项的数据项,或者最近引用过的数据本身的倾向性,称为局部性原理。其有两种不同的形式,分别是时间局部性,指被引用过一次的内存位置很可能在不远的将来再被多次引用;以及空间局部性,指一个内存位置被引用了一次,那么程序很可能在不远的将来引用附近的一个内存位置。
考虑如下代码
int sumarrayrows(int V[2][3]){
int i, j, sum = 0;
for (i = 0; i < 2; i++){
for (j = 0; j < 3; j++){
sum += V[i][j];
}
}
return sum;
}
其访问数组的顺序为 V [ 0 ] [ 0 ] V[0][0] V[0][0]、 V [ 0 ] [ 1 ] V[0][1] V[0][1]、 V [ 0 ] [ 2 ] V[0][2] V[0][2]、 V [ 1 ] [ 0 ] V[1][0] V[1][0]、 V [ 1 ] [ 1 ] V[1][1] V[1][1]、 V [ 1 ] [ 2 ] V[1][2] V[1][2];其访问地址的顺序为 V V V、 V + 4 V+4 V+4、 V + 8 V+8 V+8、 V + 12 V+12 V+12、 V + 16 V+16 V+16、 V + 20 V+20 V+20,其局部性良好。再考虑如下代码
int sumarraycols(int V[2][3]){
int i, j, sum = 0;
for (j = 0; j < 3; j++){
for (i = 0; i < 2; i++){
sum += V[i][j];
}
}
return sum;
}
其访问数组的顺序为
V
[
0
]
[
0
]
V[0][0]
V[0][0]、
V
[
1
]
[
0
]
V[1][0]
V[1][0]、
V
[
0
]
[
1
]
V[0][1]
V[0][1]、
V
[
1
]
[
1
]
V[1][1]
V[1][1]、
V
[
0
]
[
2
]
V[0][2]
V[0][2]、
V
[
1
]
[
2
]
V[1][2]
V[1][2];其访问地址的顺序为
V
V
V、
V
+
12
V+12
V+12、
V
+
4
V+4
V+4、
V
+
16
V+16
V+16、
V
+
8
V+8
V+8、
V
+
20
V+20
V+20,其局部性较差。
相比之下,按顺序访问向量的每个元素有更好的局部性,称为步长为1的引用模式,也称顺序引用模式。
2.2 存储器层次结构
软硬件具有一定的互补特性,硬件上,高度储存器成本高,功耗大,同时CPU与存储器之间的速度差距越来越大;软件上,良好的程序往往表现出良好的局部性。通过这些特性,可以得到一种组织储存系统的途径,即存储器层次结构。其层次包括:
-CPU寄存器;
-L1高速缓存SRAM;
-L2高速缓存SRAM;
-L3高速缓存SRAM;
-主存DRAM;
-本地二级储存磁盘;
-远程二级储存分布式文件系统与服务器
存储器层次结构的核心思想是高层的更快更小的存储设备作为低层更大更慢储存设备的缓存。由于局部性原理,程序访问高层的数据比访问低层的数据要频繁,从而使存储器层级结构构建了大容量的存储池,像低层存储器一样廉价,又达到了顶层存储器的速度。
2.3 高速缓存
高速缓存【cache】是一种小而快速的存储设备,作为更大、更慢的设备中的数据对象的缓冲区域。低层的存储器被划分为连续的数据对象组块,称为块,每个块都有唯一的地址或标识。高层的块被划分为较少的块的集合,其块的大小与低层一致。数据则以块为传输单元在高层与低层之间复制。
当程序需要低层的某个数据对象
d
d
d时,其会在高层的块中查找
d
d
d。若
d
d
d恰好在高层中,那么称为缓存命中。程序也从高层中直接读取
d
d
d,这要比从低层读取更快。
另一方面,如果高层中妹有缓存数据对象
d
d
d,即缓存不命中,高层会从低层中取出包含
d
d
d的块。当高层的缓存已满时,可能会覆盖现存的一个块,称为替换,被替换的块称为牺牲块。决定替换的块由替换策略控制。
如果高层缓存是空的,那么任何数据对象的访问都不会命中,缓存称为冷缓存,这种不命中称为强制不命中或冷不命中,其是一种短暂的状态。当发生任何不命中时,高层缓存都需要执行放置策略,确定低层取出的块如何放置,否则,随意的放置块,定位起来的代价很高。
放置策略可能会导致低层的某个块限定在高层的特定位置,这导致即使缓存足够大,但低层中被引用的对象在高层中均映射到同一个缓存块,此时会引起冲突不命中。
程序通常时按照一系列阶段来运行的,如果缓存大小不足以满足程序该阶段的数据访问需要,就会发生容量不命中。
三、高速缓存存储器
3.1 高速缓存通用组织
考虑计算机系统,其每个存储器地址有
m
m
m位,形成了
M
=
2
m
M = 2^m
M=2m个地址。这个系统的高速缓存被组织成一个有
S
=
2
s
S = 2^s
S=2s的高速缓存组,每个组包括
E
E
E个高速缓存行,每个行由
B
=
2
b
B = 2^b
B=2b字节的数据块组成,形如
其中,有效位【valid bit】指明该行是否包含有效信息;t位标记位【tag bit】唯一的标识了储存在该行的块。那么有
m
=
t
+
s
+
b
m = t + s+ b
m=t+s+b即一个地址被解释为行标记、组索引与块偏移三部分。而高速缓存的大小为
C
=
S
×
E
×
B
C = S × E × B
C=S×E×B的数据字节。
3.2 特殊高速缓存
每组缓存组只有一行缓存行的高速缓存称为直接映射高速缓存,其
E
=
1
E = 1
E=1。对于直接映射高速缓存,若发生不命中,那么新取出的行会直接替换缓存组的行。
每组缓存组有两行缓存行的高速缓存称为组相联高速缓存,其避免了直接映射高速缓存的冲突不命中。
一个缓存组包含全部行的高速缓存称为全相联高速缓存。
高速缓存的访问均由如下步骤进行:
-组选择,将组索引解释为无符号整数,根据索引寻找缓存组;
-行匹配,通过标记位确认行的选择,并要求有效位置1;
-字选自,块偏移提供了所需要的字的第一个字节的偏移。
3.3 高速缓存读写
高速缓存对于读的操作非常简单。首先,在高速缓存中查找所需数据的副本,如果命中,则返回给CPU;否则从层次较低层中查找所需数据的副本,并将该块储存到高层的某个缓存行中,这个过程可能会产生替换,然后返回给CPU。
高速缓存对于写的操作比较复杂。当更新了已经缓存的字时,低层也需要进行更新。最简单的方法称为直写,其在高层数据更新时立即引起低层的更新,其每次写都会引起总线流量;另一种方法称为写回,其尽可能的推迟更新,使用修改位,在该块被替换时,根据修改位更新低层,称为写回,其增加了复杂度,但减少了总线流量。
当需要更新数据,但数据没有缓存在高速缓存中,就会发生写不命中。处理写不命中的一种方法称为写分配,其加载相应的低层块到高层中,并进行更新;另一种方法成为非写分配,其直接写到内存中,而不加载缓存。一般的,直写高速缓存一般是非写分配的,而写回高速缓存一般是写分配的。
3.4 高速缓存性能
衡量高速缓存性能影响的参数包括:
-不命中率:一部分内存引用在缓存中发生不命中的概率,典型的L1高速缓存为3%~10%;
-命中时间:从高速缓存传送字到CPU的时间,包括组选择、行匹配、字选择的时间,典型的L1高速缓存为4个时钟周期;
不命中处罚:由于不命中所需要的额外时间,通常主存为50-200个时钟周期。
高速缓存友好代码的核心为对高速缓存储存其的理解来对局部性概念进行量化。其基本方法如下:
-让内部循环运行的更快,大部分计算和内存访问都发生在循环中;
-按照数据对象储存在内存的顺序,以步长为1读数据,使程序的空间局部性最大,减少不命中;
-一旦从内存中读入一个数据对象,就尽可能的多使用,使得程序的时间局部性最大。