per-CPU variables 与本地数据
这页讲的是:有些状态并不适合让所有 CPU 共用同一份,因为一共用就会带来争用、缓存抖动和同步成本。学这页的重点,是理解 per-CPU data 的核心不是“复制很多份很浪费”,而是“把天然局部的状态留在本 CPU,换来更低争用和更清楚的执行关系”。
这块是什么
per-CPU variables 与本地数据,讲的是 Linux 怎样把某些统计、缓存、中间状态或短期工作上下文按 CPU 分开存放,让每个 CPU 更常访问自己的那一份,而不是频繁争抢全局共享对象。它关注的是“哪些状态天然适合本地化”,而不是简单把所有数据都复制一遍。
可以把它理解成:一家公司不是所有人都去抢同一个便签本,而是每个工位先有自己的便签区。只在必须汇总的时候,才把各工位的信息正式合起来看。
它负责什么
减少全局共享争用
- 如果每次都改同一个全局计数或状态,多核下很容易打架
- 把状态拆到每个 CPU,本地更新通常更便宜
- 减少锁竞争和缓存来回抖动
- 让高频路径更容易扩展到多核
保留 CPU 本地语义
- 有些状态只对当前 CPU 当下执行最有意义
- 本地缓存、本地统计、本地临时上下文都常属于这类
- 让“谁在用、谁在更新”关系更清楚
- 避免过早把局部问题做成全局问题
给后续汇总留出边界
- 本地化不是永远不汇总
- 很多时候只是把高频更新和低频汇总拆开
- 让系统把最贵的同步推迟到真正需要时
- 把“实时共享”变成“按需合并”
为什么它不是“偷懒不做同步”
| 如果想得太简单 | 会怎样 | 真正关键在哪 |
|---|
| 觉得这是为了逃避锁 | 会低估它其实是在利用很多状态本来就具有 CPU 局部性。 | 这不是绕开正确性,而是把共享范围设计得更合理。 |
| 把每 CPU 数据当完全独立真相 | 会漏掉很多场景最后仍要汇总或跨 CPU 协作。 | 本地化和全局视角经常要配合出现。 |
| 只看内存多占了几份 | 会忽略省下的锁竞争和缓存一致性代价往往更关键。 | 多核系统里同步成本常比那点复制更贵。 |
| 觉得当前任务永远留在本 CPU | 会忽略抢占和迁移会改变“当前到底在哪颗核上跑”。 | 访问本地数据时经常要一起考虑执行连续性。 |
关键概念
| 概念 | 现在怎么理解 |
|---|
| per-CPU variable | 按 CPU 各自保存一份的变量或状态,让本地访问更便宜。 |
| 本地更新 | 多数高频修改先只改当前 CPU 的那份,而不是立刻动全局对象。 |
| 汇总 | 需要全局视图时,再把多个 CPU 的本地结果合并起来看。 |
| CPU 局部性 | 数据更常被哪颗 CPU 访问、更新,会直接影响设计是否适合本地化。 |
| 迁移 / 抢占约束 | 如果执行可能中途换到别的 CPU,本地数据访问就得更认真保护上下文假设。 |
为什么重要
- 它解释了为什么很多高频内核路径不想总抱着一个全局锁或全局计数器跑。
- 很多看起来只是“统计一下”“临时记一下”的对象,一旦落到多核高频路径就会变成性能问题。
- 理解这层后,你会更容易把抢占、CPU 迁移、缓存一致性和锁竞争连起来。
- 它让你看到:提高并发扩展性,很多时候不是把同步做得更花,而是先少共享一点。
常见误解
- 误解一:每 CPU 数据就是复制粘贴的内存浪费。实际上很多时候这是用空间换掉更贵的同步成本。
- 误解二:用了 per-CPU 就绝对不需要同步。实际上跨 CPU 汇总和迁移边界仍然要认真处理。
- 误解三:只有性能优化才关心它。实际上很多执行上下文假设本来就天然是 CPU 局部的。
它不负责什么
- 它不替代对象生命周期管理;对象活不活着仍要靠引用和释放规则保证。
- 它不决定任务最终在哪颗 CPU 跑,那是调度和迁移策略的问题。
- 它不自动生成全局一致视图,汇总时机和语义仍要由系统自己设计。
和其他模块的关系
| 相关模块 | 关系 |
|---|
| preemption 与抢占 | 访问本地数据时,经常要确保自己不会中途被换到另一颗 CPU 继续跑。 |
| 调度器与 CPU 迁移 | 任务可能被迁走,所以“当前 CPU 的本地状态”不是永远自然成立的。 |
| 锁与并发控制 | 很多用 per-CPU 的目的就是缩小共享范围,减少全局锁需求。 |
| NUMA 与局部性 | 它们都在强调“资源离谁近、谁更常访问”会改变系统表现,只是粒度不同。 |
读完这页后,你应该能回答
- 为什么有些内核状态宁可每 CPU 各存一份,也不愿总走全局共享对象?
- 为什么 per-CPU data 的难点不只在“怎么存”,还在“访问时能不能保证自己还在这颗 CPU 上”?
- 为什么本地化和最终汇总通常要被拆成两个不同节奏的问题?
后面适合继续问:关抢占和拿本地锁分别在保护什么?哪些统计值适合做成 per-CPU,哪些不适合?per-CPU 和 NUMA 局部性最容易被混在哪?