计算机的存储层次结构(下)——CPU高速缓存

744 阅读5分钟

前言

这是存储层次结构的第二篇,学习一下高速缓存结构。

高速缓存

CPU 和主存的发展速度是相当不平衡的,以致于 CPU 速度已经大大领先主存,主存成为了计算机系统运作的瓶颈。为了弥补这个速度差距,工程师为 CPU 和主存之间增设了高速缓存:L1,L2,L3,假如速度差异持续变大,不排除层次会继续增加。现代 CPU 在读取数据的时候,已经做到95%都可以命中高速缓存。

图片引用自:极客时间《深入浅出计算机组成原理》 P37

如上图所示,CPU 的高速缓存和应用程序的缓存是异曲同工的,虽然高速缓存会很小,但一定需要做到,可以定位并缓存主存里任何地址w的数据。明白这个的原理,就基本清楚高速缓存的结构了。这个部分我觉得可以分为两个环节去理解:

  • 高速缓存的结构
  • 高速缓存的寻址过程

高速缓存的结构

图片引用自:极客时间《深入浅出计算机组成原理》 P37

高速缓存的寻址

假如我们有一个内存地址w,CPU 需要读取这个地址的数据。简单来说,整个寻址过程包括:1、确定哪一组;2、确定哪一行;3、确定哪个偏移值。

一个内存地址,假设是w(32位),假定如下设置

标记位 索引位 偏移量
10bit 10bit 22bit

具体的步骤如下:

  1. 确定哪一组。一般的做法是取w地址的索引位(10bit)做组索引,这也是为什么高速缓存的组数一般是2的幂次方,这样只需要做与运算就可以获得索引位置了。
  2. 判断缓存行的有效位,确定缓存的行数据是有效的。
  3. 使用w地址的高位标记位(10bit)去判断该组是否是w地址目标行。
  4. 假如3符合,通过偏移量的数据,读取目标字长数据。字长数据一般是硬件规定好的,如32Byte。

解析这个步骤会比较难,关键是不断反问自己:一个w地址如何在有限空间的高速缓存中有效地寻址

高速缓存行的加载

上一小节中,简单地描述了一个w地址的数据(一个字长)加载情况。但一个缓存行的数据区域一般远比一个字长要大。所以事实上加载一个缓存行的数据一定不仅仅是加载一个字长,而是根据w地址的索引位和标记位,加载一个组的数据。由于局部性原理,应用了w地址的程序也会比较大概率地引用w+1地址的数据。

Java volatile 的使用优化

小节内容选自 《Java 并发编程之美》 P10页

正如上一小节的高速缓存行的加载所说,一个地址是会让缓存行自动加载多行数据导致缓存行数据区写满的。例如:一个i7处理器的高速缓存行是64个字节宽,考虑以下程序。

LinkedTransferQueue.java

private transient final PaddedAtomicReference<QNode> head;
private transient final PaddedAtomicReference<QNode> tail;

static final class PaddedAtomicReference<T> extends AtomicReference<T> {
    Object p0,p1,p2,p3,....p9,pa,pb,pc,pd,pe;   // 填充字宽
    PaddedAtomicReference(T r) {
        super(r);
    }
}

class AtomicReference {
    private volatile V value;
}

PaddedAtomicReference 对象的主要作用是把对象大小强行占满 64 字节。为什么要这样做呢?上一小节我们谈到,高速缓存的加载是直接对 head 进行加载时,会连着 tail 一起进行加载。

有效位 组标记 实际数据
1 001 head tail

当 CPU 对 head 进行修改时,会对进行缓存锁定,但是缓存锁定会把tail一起锁起来。在多 CPU 的环境下,队列的出入数据很多的时候,这样的锁定会让锁的粒度变大,而导致性能变慢。不过这个填充的问题,JDK7以后已经会自己填充了。

高速缓存的更新

高速缓存的更新大概有两种方法:

  • 直写(write-through) 这是最简单的做法。即上层缓存写入的时候,直接把数据写到下一层。缺点就是会每次写都会造成总线流量。
  • 写回(write-back) 尽可能地推迟更新,只有当替换算法要驱逐某个块时,才会把块的数据写到下一层中。由于局部性,这样的设计会显著地减少总线流量。但随之而来的就是比较复杂的设计实现。

后记

CPU 高速缓存其实和程序中使用的缓存原理上是比较相似的。因此理解原理应该不会很难。指导我们写程序的主要是局部性原理,还有防止高速缓存抖动,失效等。

此外高速缓存依然还有很多知识是没涉及的。如:多核CPU的缓存一致性实现。

参考