Skip to content

高速缓存

更新: 1/19/2026 字数: 0 字 时长: 0 分钟

缓存机制

缓存结构

在计算机系统中,CPU 高速缓存是处理器内部或紧邻处理器的一小块高速存储区域。它的主要目的是减少处理器访问主内存的平均时间。

  • 层次结构:CPU 缓存位于存储体系的第二层,仅次于 CPU 寄存器。
  • 速度与容量:它的容量远小于主内存,但速度却可以接近处理器的频率。
  • 多级缓存:由于 CPU 速度远超主内存,为了弥补巨大的速度差距,在 CPU 和主内存之间架设了多级缓存,如 L1、L2、L3。这些缓存离 CPU 越近,速度越快,容量越小。它们将处理器频繁操作的数据缓存起来,大大加快了数据访问速度。

从 CPU 到大约需要的时钟周期
寄存器1 cycle (4GHz 的 CPU 约为 0.25ns)
L13~4 cycle
L210~20 cycle
L340~45 cycle
内存120~240 cycle

缓存使用

  • 工作原理:当处理器需要数据时,它首先检查 CPU 缓存中是否有该数据。
    • 命中 (Cache Hit):如果数据在缓存中,处理器直接从缓存获取,速度极快,无需访问主内存。
    • 失效 (Cache Miss):如果数据不在缓存中,处理器必须先从主内存中把相应的数据块载入到缓存中,然后再将其返回给处理器。
  • 局部性原理:缓存之所以有效,是因为程序在运行时对内存的访问通常具有局部性特征:
    • 时间局部性:如果一个数据项被访问,那么它很可能在不久的将来再次被访问。
    • 空间局部性:如果一个数据项被访问,那么它附近的内存地址中的数据项也很可能在不久的将来被访问。

有效利用这些局部性,可以使缓存达到很高的命中率。

伪共享

核心概念:CPU 缓存不是以单个字节为单位加载数据的,而是以固定的缓存行 (Cache Line) 为单位。一个缓存行通常是 64 字节(例如,可以存储 8 个 long 类型数据)。当 CPU 从主内存获取数据时,会以整个缓存行的大小加载到缓存中。因此,内存中相邻的数据会一并被加载。

问题描述:当不同线程操作独立且不相关的变量时,如果这些变量恰好位于同一个缓存行中,就会发生“伪共享”。

  • 数据副本:同一份数据(包括该缓存行中的所有变量)可能会被缓存在多个 CPU 核心的缓存行中。
  • 缓存一致性协议:为了保证数据一致性,当某个 CPU 核心修改了缓存行中的任何一个变量时,其他所有 CPU 核心中对应的整个缓存行都必须被标记为失效 (Invalid)
  • 性能影响:即使线程操作的是不同的变量,但由于它们共享同一个缓存行,一个线程的修改会导致其他线程的缓存行失效,迫使它们重新从主内存加载数据,从而频繁地触发缓存同步,降低性能。

解决方法

  • 填充 (Padding):在变量之间插入无用的填充字段(如 long 类型),使得原本相邻的变量落到不同的缓存行中。
  • @Contended 注解:Java 8 引入的 @sun.misc.Contended 注解(需要配置 JVM 参数 -XX:-RestrictContended 才能生效)可以自动为字段添加填充,将其隔离到独立的缓存行。

缓存一致

定义:当多个处理器(或核心)同时处理任务,并且它们都涉及到同一块主内存区域时,每个处理器可能会有该内存区域的缓存副本。缓存一致性协议就是为了确保在这些副本之间,数据始终保持一致。

MESI 协议:MESI (Modified Exclusive Shared Invalid) 是一种广泛使用的支持写回 (Write-Back) 策略的缓存一致性协议。它通过给每个缓存行标记四种状态来管理一致性:

  • M (Modified - 已修改):该缓存行只存在于当前 CPU 的缓存中,并且已被修改过,与主内存中的数据不一致(是“脏”的)。它最终需要被写回主内存。
    • 当再次修改时,无需通知其他核心。
    • 写回主内存后,状态会变为 E (独享)。
  • E (Exclusive - 独享):该缓存行只存在于当前 CPU 的缓存中,但未被修改过,与主内存数据一致。
    • 当 CPU 修改该缓存行内容时,状态变为 M。
    • 当其他 CPU 读取该内存时,状态变为 S (共享)。
  • S (Shared - 共享):该缓存行可能存在于多个 CPU 的缓存中,并且所有缓存中的数据都与主内存数据一致。
    • 当当前 CPU 修改该缓存行内容时,它会向总线广播一个失效请求 (Invalidate Request),通知其他 CPU 将其对应的缓存行标记为 I (无效),然后当前缓存行状态变为 M。
  • I (Invalid - 无效):该缓存行的数据是无效的,可能因为其他 CPU 修改了它。当 CPU 访问该缓存行时,必须重新从主内存加载。

作用:MESI 协议确保了在多核 CPU 环境下,对共享数据的修改能够被所有核心正确地感知和同步。

处理机制

在多核 CPU 环境下,为了解决缓存数据不一致的问题,处理器提供了两种主要的机制:

  1. 总线锁定 (Bus Locking)

    • 原理:当一个处理器需要对共享变量进行操作时,它会在 CPU 总线上发出一个 LOCK# 信号。这个信号会锁定整个总线,阻止其他处理器访问主内存,从而确保当前处理器对共享变量的操作是独占的。
    • 缺点:总线锁定会导致其他处理器完全停滞,等待总线释放,从而引入大量阻塞,严重增加系统性能开销,降低并发度。
    • 使用场景:当操作的数据跨多个缓存行,或者数据未被缓存到处理器内部时,处理器可能会使用总线锁定。一些旧的处理器不支持缓存锁定(如 Intel 486 和 Pentium)也会使用总线锁定。
  2. 缓存锁定 (Cache Locking)

    • 原理:这是更高效的方式。当处理器对缓存中的共享变量进行操作时,它会利用缓存一致性协议(如 MESI)来保证数据一致性。
      • 嗅探机制 (Snooping):每个处理器都会“嗅探”总线上的数据传播。当一个处理器发现自己缓存中的数据对应的内存地址被其他处理器修改时,它会将自己缓存中的该缓存行标记为无效状态 (Invalid)
      • 重新加载:当处理器再次尝试访问这个被标记为无效的数据时,它会强制从主内存中重新读取最新的数据到自己的缓存中。
    • 优点:相比总线锁定,缓存锁定只影响相关的缓存行,其他不相关的数据访问可以继续进行,大大提高了并发性能。

总线机制的补充说明

  • 总线嗅探 (Bus Snooping):是缓存一致性协议的基础。每个处理器持续监听总线上的所有内存事务。当它发现其他处理器发出了对某个内存地址的写入请求,并且这个地址的数据正好在自己的缓存中时,它就会根据协议(如 MESI)将自己缓存中的对应缓存行标记为无效。
  • 总线风暴 (Bus Storm):当系统中有大量共享变量被频繁修改时,会导致总线上出现大量的缓存同步(如失效广播)和数据传输操作。CPU 需要不断监听总线活动,并且频繁地使缓存行失效并重新加载,这会占用大量的总线带宽,导致总线成为性能瓶颈,这种现象被称为“总线风暴”。
    • 提示:因此,过度使用 volatile 关键字或 synchronized 锁(尤其是在高竞争场景下)都可能导致频繁的缓存同步和总线流量,需要根据实际场景进行权衡和优化。

贡献者

The avatar of contributor named as LI SIR LI SIR

页面历史