作为一名优秀的程序员,理解计算机中的存储系统-----即计算机的层次存储结构,有利于我们更好的利用程序的局部性原理编写出高效的代码,所以话不多说,先附上一张图说明情况。
这就是我们计算机系统中真实的存储模型,它是一个具有不同容量,成本以及访问时间的存储设备的层次结构,cpu寄存器保存着最常用的数据,靠近cpu的小的,快的高速缓存存储器作为一部分存储在相对较慢的主存储器中数据和指令的缓冲区,主存缓存存储在容量较大,速度较慢的磁盘上的数据,而这些磁盘又常常作为存储在通过网络连接在其他机器或者设备上数据的缓冲区,是一个层次递进的关系。
随机访问存储器分为两类:静态(SRAM)和动态(DRAM),静态的比动态的更快,但是也更贵,SRAM常常用作高速缓存存储器,DRAM常用作主存以及图形系统的帧缓冲区。
SRAM:SRAM将每个位存储在一个双稳态的存储器单元里,每个单元是用一个六晶体管电路来实现的。这个电路有这样一个属性:它可以无限期地保持在两个不同的电压状态之一。其他任何状态都是不稳定的,电路会迅速转移到两个稳定状态中的一个。
由于SRAM的双稳态特性,只要有电,它就会永远地保持它的值。
SRAM对干扰不敏感。
DRAM:DRAM将每个位存储为对一个电容的充电。和SRAM不同,DRAM单元对干扰非常敏感。
存储器系统必须周期性地通过读出,然后重写来刷新存储器的每一位。
以下是SRAM与DRAM的比较:
每位晶体管数 | 向对访问时间 | 持续的? | 敏感 | 相对花费 | 应用 | |
SRAM | 6 | 1x | 是 | 否 | 1000x | 高速缓存存储器 |
DRAM | 1 | 10x | 否 | 是 | 1x | 主存 帧缓冲区 |
快页模式DRAM,扩展数据输出DRAM,同步DRAM,双倍数据速率同步DRAM,视频RAM等等。
如果断电,DRAM和SRAM会丢失它们的信息,从这个意义上说,他们是易失的。非易失性存储器即使在断电后仍然保存着他们的信息
可编程ROM(PROM):只可以被编程一次。
可擦写可编程ROM(EPROM):EPROM和电子可擦除PROM都可以被多次擦除和编程。
闪存(flash memory):提供相对于传统磁盘的一种更快速更强进和更低消耗的选择。
磁盘的构造
磁盘是由盘片构成的,每个盘面有两面或者称为表面,表面覆盖有磁性材料,盘片中央有一个可旋转的主轴,使得盘片以固定旋转速率旋转磁盘通常包含一个或者多个这样的盘片并封装在一个密封容器中。
磁盘容量
记录密度:(位/寸)磁道一英寸的段中可以放多少位。
磁道密度:(道/寸)从盘片中心出发半径上一英寸的段内可以有的磁道数。
面密度:(位/平方英寸)记录密度与磁道密度乘积。
磁盘容量=字节数(扇区)*平均扇区数(磁道)*磁道数(表面)*表面数(盘面)盘片数(磁盘)
固态硬盘是一种基于闪存的存储技术,一个SSD封装是有一个或者多个闪存芯片和闪存翻译层组成,闪存芯片替代了传统旋转磁盘的机械驱动器,而闪存翻译层是一个硬件设备,作用和磁盘控制器差不多,将逻辑块的请求翻译成对底层物理设备对的访问。读SSD比写要快,随机读和写的性能差别是由底层闪存基本属性决定的。
不同的存储技术有不同的价格和性能折中,SRAM比DRAM快,而DRAM比磁盘快很多,另一方面,快的总是比慢的贵很多。
两种不同形式:时间局部性和空间局部性,在一个具有良好时间局部性的程序中,被引用过一次的内存位置很可能在不远的将来在此多次引用,在一个具有良好空间局部性的程序中,如果一个内存位置被引用了一次,那么程序在不远的将来引用附近的一个内存位置。有良好局部性的程序比局部性差的程序运行的更快。下面看一个小栗子:
int sumvec(int v[n])
{
int i=0,sum=0;
for(i;i
地址 | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 |
内容 | v0 | v1 | v2 | v3 | v4 | v5 | v6 | v7 |
访问顺序 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
我们可以看到向量的访问顺序是按照顺序访问的按照他们存储在内存中的顺序,因此这个函数具有良好的空间局部性但时间局部性很差因为每个元素只被访问一次,我们说这样的访问具有步长为1顺序引用模式,一个向量中,每间隔k个元素进行访问,就称为步长为一的引用模式,一般而言随着步长增加,空间局部性逐渐下降。
让我们在来看另外一个例子:
int sumarraycols(int a[m][n])
{
int i,j,sum=0;
for(i=0;i
地址 | 0 | 4 | 8 | 12 | 16 | 20 |
内容 | a00 | a01 | a02 | a10 | a11 | a12 |
访问顺序 | 1 | 3 | 5 | 2 | 4 | 6 |
c语言里面对数组一般都是按照行优先存储,此代码仅仅交换了m和n的循环,所以访问数组时就不再按照行优先访问,结果就得到步长为n的引用模式,所以影响了程序的局部性,没有良好的空间局部性,虽说对一个很小的数组影响不大,但对于比较大的数组,这种影响就可想而知了。
局部性小结:
1,重复引用相同变量的程序有良好的时间局部性。
2,对于具有步长位k的引用模式的程序,步长越小,空间局部性越好,具有步长位1的引用模式的程序有很好的空间局部性,在内存中以大步长跳来跳去的程序空间局部性很差。
3,对于取指令来说,循环具有好的时间和空间局部性,循环体越小,循环迭代的次数越多局部性越好。
存储器层次结构的思想是对于每个k,位于k层更快更小的存储设备作为位于k+1层更大更慢存储设备的缓存,换句话说,层次结构中每一层都缓存来自较低一层的数据对象.
缓存命中:当程序需要第k+1层的某个数据对象d时,它首先在当前存储在第k层的一个块中查找d,如果d刚好缓存在k层时,那么我们就说是缓存命中程序直接从k层读取d。
缓存不命中:如果第k层中没有数据对象d,那么我们就说缓存不命中,当发生缓存不命中时,第k层的缓存就从k+1层缓存中取出包含d的那个块,放入第k层缓存中,然后程序在从第k层读出数据d。
缓存管理:存储器层次结构本质是每一层存储设备都是较低一层缓存,在每一层上,某种形式的逻辑必须管理缓存,既某个东西要将缓存划分成块,在不同层次之间传送快,判断是命中还是不命中并且处理他们,管理缓存的逻辑可以是硬件,软件或者硬件和软件的结合。
高速缓存存储器组织结构
1,通用的高速缓存存储器组织结构
其中每个存储器地址有m位,形成M=2^m个不同地址,高速缓存被组织成一个有S=2^s个高速缓存组,每个组包含E个高速缓存行,每个行是由一个B=2^b字节的数据块组成,一个有效位指明这个行是否包含有意义的信息,还有t=m-(b+s)个标记位,他们唯一的标识了存储在这个高速缓存行中的块。一般而言,高速缓存结构可以用元组(S,E,B,m)表示,高速缓存的大小C指的是所有块的大小和,标记位和有效位不包括在内。既C=S*E*B,当一条加载指令指示CPU从主存地址A读一个字时,他将地址A送到高速缓存,如果高速缓存正保存着地址A的那个字副本,他立即将那个字返回给CPU,高速缓存的结构使得他能够通过简单的检查地址位,找到所请求的字类似于使用极其简单的哈希函数的哈希表。
直接映射高速缓存:每个组只有一行(E=1)的高速缓存称为直接映射高速缓存。
1,直接映射高速缓存的组选择
高速缓存从w的地址中取出S个组索引位,这些位被解释成一个对应于一个组号的无符号整数。
2,直接映射高速缓存中的行匹配
应为直接映射高速缓存中每个组只有一行,当且仅当设置了有效位,所以我们知道标记和块中的位是有意义的应为这个这个高速缓存行中的标记位与地址中的标记位相匹配,所以我们得到了一个缓存命中,如果没有设置有效位,那就得到了一个缓存不命中。
3,直接映射高速缓存中的字选择
我们知道W就在这个块中的某个地方,最后确认所需要的字在块中是从哪里开始的,块偏移位提供了所需要的字的第一个字节偏移。
假设我们有一个直接映射高速缓存,描述如下:(S,E,B,m)=(4,1,2,4),即4个组,每个组1行,每个块2个字节,地址是4位。这里假设每个字都是单字节,每次都读1个字。由上得知,
,
示例过程如下:初始时,高速缓存是空的:
a)读地址0的字。因为组0的有效位是0,缓存不命中,所以高速缓存从存储器取出块0,,并存储在组0中。然后高速缓存返回取出的高速缓存行的m[0]。
b)读地址1的字。高速缓存命中。
c)度地址13的字。
d)读地址8的字。(组0的行确实有效,但标记不匹配)
e)读地址0的字。又发生缓存不命中,因为前面引用地址8时,刚好替换了块8。
组相联高速缓存:每个组都保存多于一个的高速缓存行,一个1 1,组相联高速缓存组选择与直接映射高速缓存组选择一样,组索引标识组。 2,组相联高速缓存行选择是一个(key ,value)对的数组,以key为输入,返回与输入的key相匹配的(key ,value)中的value值,因此我们可以把组相联高速缓存中的每个组看成一个小的相联存储器,key是标记和有效位,而value是块的内容。 全相联高速缓存 全相联高速缓存是由一个包含所有高速缓存行的组(E=C/B)组成的, 1,全相联高速缓存只有一个组,因此组选择十分简单,地址中没有组索引位,地址中只被划分位一个标记和一个块偏移。 2,全相联高速缓存中的行匹配和字选择与组相联高速缓存是一样的。 1,假设我们写一个已经缓存的字w(写命中),在高速缓存更新了w的副本后,如何更新w在层次结构中紧接着低一层的副本呢? 第一种方法:直写,就是立即将字w的高速缓存块写回紧接着的低一层中去。 第二种:写回,尽可能的推迟更新,只有替换算法要要驱逐这个更新过的块时,才把他写回紧接着低一层中去 另一个问题(写不命中) 写分配:加载低一层的块到高速缓存中,然后更新这个高速缓存块 非写分配:避开高速缓存,直接把字写回到低一层中去, (直写高速缓存通常是非写分配的,写回高速缓存通常是写分配的) 1、存储器山 存储器系统的性能不是一个数字就能描述的。相反,它是一座时间和空间局部性的山,这座山的上升高度差别可以超过一个数量级。要是程序运行在山峰而不是低谷。 2、示例:重新排列循环以提高空间局部性 一对nxn矩阵相乘问题:C=AxB。一下是矩阵乘法的六个版本 有关写的一些问题