1.层次结构图
由于
- SRAM虽然访问速度较快,但其价格相对较高,容量较小
- DRAM等其他存储设备访问速度相对较慢,但价格便宜,容量很大
- CPU与主存之间的速度差距在不断增大
- 为了提高对主存的访问速度
所以引入多层次存储结构(如上图),金字塔越往上走,容量越小,成本越高,但访问速度越快,如最高层的CPU寄存器,CPU可在一个时钟周期内访问它,主存(DRAM),CPU可在几十到几百个时钟周期访问它,中间的SRAM处在其中,可在几个CPU时钟周期内访问它
register关键字说明
- 作用:register请求编译器尽可能将变量存储在CPU寄存器,让该变量的访问速度达到最快,这样你就无法获取register变量的地址,因为该变量是没有地址的
- 缺陷:因CPU寄存器数量有限,且还需要存储由CPU处理后的数据以及从cache获取的数据,故只是让编译器尽可能将变量存储在寄存器中,而无法保证一定就能存储在CPU寄存器中,仅仅只是暗示而非命令
- 使用:register关键字说明被修饰的变量将被频繁地使用,常用在同一变量名频繁出现的地方,例如
for(i=0;i<num;i++)
中i将被频繁使用,故可将变量i声明为register - 注意:
- register变量必须是能被CPU所接受的类型,如果为32位CPU,则最多只能存储一个32位的变量
- 不论编译器最终是否将变量存储在寄存器中,你都不可以访问其地址
- 编译程序会自动地将超过限制数目的寄存器变量当作非寄存器变量处理
- 寄存器变量只适用于局部变量和函数的形参, 属于auto型变量
- 有些编译器会比程序员更懂得如何合理分配寄存器,对于这类编译器,使用register反而会降低效率,故建议一般不要使用
2.缓存
- 高速缓存:即 cache ,是一个容量小,速度快的存储设备, 一般系统都有三级高速缓存,L1 / L2 / L3 cache ,访问速度分别约在 4 / 10 / 50 个时钟周期
- 存储器层次结构的中心思想:层次结构中每一层都缓存来自较低一层的数据
- 下图展示了缓存的一般性概念:
(1) 一般情况下,每一层数据都会被划分为多个大小相同的数据块,每个块都有唯一的地址名称
(2) 相邻层之间的数据块可能相同,可能不同,但总体上,层数越高,块越小
(3) 第k层就相当于第k+1层的一个子集,数据总是以块为单位复制到较高的层级
3.缓存命中/不命中
- 当程序需要第k+1层的数据d时,首先在第k层的一个块中查找d,若d在第k层中,即称为缓存命中,程序直接从k层读取数据;
- 若k层中没有数据d,则称为缓存不命中,此时k层就从k+1层读取数据d所在的块,若k层缓存已满,可能就会覆盖现存的一个块,不同的替换策略决定着不同的牺牲块(可能是随机选择一个块牺牲,也可能选择最长时间未被访问的那个块牺牲,这完全取决于缓存的替换策略),将数据d拷贝到k层后,程序就读取k层的数据d
4.不命中种类
- 强制性不命中:若k层缓冲区为空,此时对数据的访问就会造成不命中,称为强制性不命中,或冷不命中
- 冲突不命中:对于高层缓存(靠近CPU),当发生不命中,对于数据的复制是通过硬件来实现放置策略的,硬件缓存通常采用的是更严格的放置策略,即将k+1层的某个块限制放置在k层的块的一个小子集中(有时只有一个块),如:下图k+1层中块0,4,8,12映射到k层的块0;1,5,9,13映射到块1;依此类推。当程序需要循环访问k+1层的块0,块8中的数据时,每次访问都会造成不命中,这种不命中就称为冲突不命中。这种情况后面还会详细讲到