伪共享
← 缓存一致性与MESI | ← 存储器导航 | ← 计算机组成原理知识地图 | ← 主页
什么是伪共享
伪共享不是多个线程在写同一个变量,而是不同线程写不同变量,但这些变量刚好落在同一条 Cache Line 里。
因为 CPU 维护一致性的单位是 Cache Line,不是单个变量,所以只要其中一个变量被某个核心修改,这整条 Cache Line 的状态都会变化,其他核心里对应的副本也会受到影响。
为什么会慢
假设两个核心分别运行两个线程:
- 线程 A 只改变量
a - 线程 B 只改变量
b a和b在物理内存上连续,落在同一条 Cache Line
这样虽然 A 和 B 逻辑上互不相关,但只要一方写入,另一方那边这条 Cache Line 就可能失效。两个核心持续交替修改时,这条 Cache Line 会在核心之间反复失效、回写、重新加载。
所以伪共享慢,不是因为业务数据冲突,而是因为一致性协议把同一条 Cache Line 当成了一个整体。
和 MESI 的关系
把它放到 MESI 里看就很直观:
- 核心 1 读取
a,把包含a和b的整条 Cache Line 读进来 - 核心 2 读取
b,也把同一条 Cache Line 读进来 - 两边都读过后,这条 Cache Line 可能处于
S状态 - 核心 1 写
a时,要让核心 2 里的同一条 Cache Line 失效 - 核心 2 之后写
b,又要重新取回这条 Cache Line,再让核心 1 那边失效
于是两个核心虽然改的是不同变量,却会因为落在同一条 Cache Line 而互相拖慢。
怎么避免
核心思路只有一句话:让高频写入的不同变量不要落在同一条 Cache Line。
常见做法:
- Cache Line 对齐:让关键变量按 Cache Line 边界对齐
- 填充 padding:在变量前后补无用字节或字段,把热点变量隔开
- 拆分结构:不要把多个会被不同线程频繁写的字段紧挨着放在同一个结构体里
Linux 内核里常见做法是用 __cacheline_aligned_in_smp 这类宏,让变量按 Cache Line 对齐。应用层常见做法是 padding,本质上都是用空间换时间。
什么时候该怀疑是伪共享
如果满足下面这些条件,就该想到它:
- 多线程或多核场景
- 每个线程主要写自己的变量
- 业务上没有真正共享同一个变量
- 性能却随着线程并行反而变差
这时就要怀疑:问题不是锁,也不是算法,而是这些变量可能挤在了同一条 Cache Line。
继续看
- 缓存一致性与MESI
- [CPU Cache](CPU Cache.md)
参考主线:小林coding《2.5 CPU 是如何执行任务的?》