操作系统缓存全面解析(上)-存储体系结构及工作原理

227 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情

不同物理器件的访问速度不一:速度快的代价高、容量小;代价低且容量大,速度较慢。

为充分发挥各种器件优点,计算机存储数据的物理器件不会只选择一种,而是以CPU为核心,由内而外地组建一整套存储体系结构。它将各种不同的器件组合成一个体系,让各种器件扬长避短,从而形成一种快速、大容量、低成本的内存系统。

写高性能程序,须理解存储体系结构并运用好。

1 存储体系结构的核心

成本角度,计算机的存储结构被设计成分层,包括寄存器、缓存、内存、磁盘等。

缓存让内存访问速度接近于寄存器访问速度。

过去几十年,处理器速度增长远超内存速度增长。尤其是在2001~2005年间,处理器的时钟频率在以55%的速度增长,而同期内存速度的增长仅为7%。为了缩小处理器和内存之间的速度差距,缓存被设计了。

距离处理器越近,访问速度越快,造价越高,容量就更小。缓存是处理器、内存间的桥梁,分为多层,包括L1、L2、L3层等。缓存速度介于处理器、内存间:

  • 访问处理器内部寄存器的速度在1ns内(一个时钟周期)
  • 访问内存速度通常在50~100ns(上百个时钟周期)
  • 缓存
    • 靠近处理器最近的L1层缓存的访问速度在1ns~2ns(3个时钟周期)
    • 外层L2和L3层的访问速度在10ns~20ns(几十个时钟周期)

根据空间局部性、时间局部性原理,一个处理得当程序,缓存命中率要想达到70~90%不难。因此,存储系统加入缓存,可让整个存储系统的性能接近寄存器,且每字节的成本都接近内存,甚至磁盘。

可见缓存结合寄存器速度快、内存造价低,是整个存储体系的灵魂。

2 缓存的物理架构

缓存由SRAM(静态随机存储)组成,本质是一种时序逻辑电路,具体的每个单元(比特)由一个个锁存器构成,锁存器就是让电路具有记忆功能。

SRAM单位造价较高,远高于内存的组成结构“DRAM(动态随机存储)”。因为:

  • 实现一个锁存器需六个晶体管
  • 实现一个DRAM仅需一个晶体管和一个电容

但DRAM因为结构简单,单位面积可以存放更多数据,所以更适合做内存。为了兼顾这两者的优缺点,于是它们中间需要加入缓存。

DRAM因有电容,不是单纯逻辑电路,所以不能用CMOS工艺,而SRAM可以。所以缓存能集成到芯片内部,而内存和芯片分开制造。

缓存怎样集成到芯片

过去单核时代,处理器和各级缓存都只有一个,因此缓存集成方式单一,即把处理器和缓存直连。2004年,Intel取消4GHz奔腾处理器研发,即处理器以提升主频榨取性能时代结束,多核处理器成为主流。

多核芯片,缓存集成方式:

  • 集中式缓存:一个缓存和所有处理器直接相连,多核共享这个缓存
  • 分布式缓存:一个处理器仅和一个缓存相连,一个处理器对应一个缓存
  • 混合式缓存:在L3采用集中式缓存,在L1和L2采用分布式缓存

多核处理器大多采用混合式:

  • L3,所有处理器核共享
  • L1、L2,每个处理器核特有

3 缓存的工作原理

3.1 cache line

缓存进行管理的一个最小存储单元,也叫缓存块。从内存向缓存加载数据也是按缓存块进行加载的,一个缓存块和一个内存中相同容量的数据块(下称内存块)对应。

管理缓存块的角度看缓存块的组织形式:

小方框代表一个缓存块。整个缓存由组(set)构成,每组由路(way)构成。所以

整个缓存容量 = 组数 * 路数 * 缓存块大小

为简化寻址方式,内存地址确定的数据块总被放在一个固定组,但可放在组内任意路,即对特定地址数据的访问,它若要载入缓存,则它放在上图中的行数固定,但具体放到哪列不固定。

根据组、路数不同:

缓存映射方式分类

  • 直接相连映射:缓存只有一个路,一个内存块只能放置在特定的组上

    当多个内存块映射到同一组,会冲突,因为只有一列,就需将旧缓存块换出,新缓存块放入,这会导致缓存块被频繁替换

  • 全相连映射:缓存只有一个组,所有的内存块都放在这一个组的不同路上

    大程度避免冲突,不过,当查询某缓存块时,需逐个遍历每路,且电路实现较难。折中办法就是,采用组组相连映射

  • 组组相连映射:缓存同时由多个组和多个路

    与直接相连映射相比,产生冲突可能性更小,与全相连映射相比,查询效率更高,实现更简单

缓存组数一直是2^n。虽这样利于查询和定位,但若一个程序刚好以2^{n}间隔寻址,就会导致地址更多的被映射到同组,而另外一些组就会被映射很少。因此,也有些缓存的组数设计成一个质数,这样即便程序以2^{n}间隔寻址,落到同组可能性大大减小,缓存各组的利用率相对均衡。

一个内存块怎样映射到一个缓存块?

缓存块的内部结构

  • V(valid),这缓存块是否有效或是否正在被使用
  • M(modified),这缓存块是否被写,即“脏”位
  • B,缓存块的bit个数

假设要寻址一个32位地址,缓存块64字节,缓存组织方式4路组相连,缓存8K。

缓存共32组(8 * 1024 / 64 / 4=32)。则对任一32位地址Addr ,它映射到缓存的组号(set index)为 Addr对组数32取模,组号同时也等于Addr的第6~10位( (Addr >> 6) & 0x1F ),Addr低6位很好理解,它是缓存块的内部偏移(262^{6}为64字节)。

确定需要被映射到哪组后,需在该组的路中查询。查询方式也简单,直接将每个缓存块tag的bit位和地址Addr的高21位逐一匹配:

  • 相等,说明该内存块已载入缓存
  • 若无匹配的tag,说明缓存缺失,需将内存块放到该组的一个空闲缓存块上
  • 若所有路的缓存块都正被使用,就需要选择一个缓存块,将其移出缓存,把新的内存块载入

上面这个过程涉及到缓存块状态转换,而状态转换又涉及到有效位V、脏位M、标签tag。

缓存状态转换:

当同组的缓存块都被用完,需选择一个缓存块被换出,那选谁换出呢?