伪共享

← 缓存一致性与MESI | ← 存储器导航 | ← 计算机组成原理知识地图 | ← 主页

什么是伪共享

伪共享不是多个线程在写同一个变量,而是不同线程写不同变量,但这些变量刚好落在同一条 Cache Line 里。

因为 CPU 维护一致性的单位是 Cache Line,不是单个变量,所以只要其中一个变量被某个核心修改,这整条 Cache Line 的状态都会变化,其他核心里对应的副本也会受到影响。

为什么会慢

假设两个核心分别运行两个线程:

  • 线程 A 只改变量 a
  • 线程 B 只改变量 b
  • ab 在物理内存上连续,落在同一条 Cache Line

这样虽然 A 和 B 逻辑上互不相关,但只要一方写入,另一方那边这条 Cache Line 就可能失效。两个核心持续交替修改时,这条 Cache Line 会在核心之间反复失效、回写、重新加载。

所以伪共享慢,不是因为业务数据冲突,而是因为一致性协议把同一条 Cache Line 当成了一个整体

和 MESI 的关系

把它放到 MESI 里看就很直观:

  1. 核心 1 读取 a,把包含 ab 的整条 Cache Line 读进来
  2. 核心 2 读取 b,也把同一条 Cache Line 读进来
  3. 两边都读过后,这条 Cache Line 可能处于 S 状态
  4. 核心 1 写 a 时,要让核心 2 里的同一条 Cache Line 失效
  5. 核心 2 之后写 b,又要重新取回这条 Cache Line,再让核心 1 那边失效

于是两个核心虽然改的是不同变量,却会因为落在同一条 Cache Line 而互相拖慢。

怎么避免

核心思路只有一句话:让高频写入的不同变量不要落在同一条 Cache Line。

常见做法:

  • Cache Line 对齐:让关键变量按 Cache Line 边界对齐
  • 填充 padding:在变量前后补无用字节或字段,把热点变量隔开
  • 拆分结构:不要把多个会被不同线程频繁写的字段紧挨着放在同一个结构体里

Linux 内核里常见做法是用 __cacheline_aligned_in_smp 这类宏,让变量按 Cache Line 对齐。应用层常见做法是 padding,本质上都是用空间换时间

什么时候该怀疑是伪共享

如果满足下面这些条件,就该想到它:

  • 多线程或多核场景
  • 每个线程主要写自己的变量
  • 业务上没有真正共享同一个变量
  • 性能却随着线程并行反而变差

这时就要怀疑:问题不是锁,也不是算法,而是这些变量可能挤在了同一条 Cache Line。

继续看


参考主线:小林coding《2.5 CPU 是如何执行任务的?