Skip to content

缓存一致性与 NUMA、多核亲和

一、多核 cache 一致性(CC-coherent)

多核共享内存但各有私有 L1/L2 → 硬件用缓存一致性协议(MESI/MOESI 变种)让各核看到一致内存。写入使其它核持有一致的副本失效/更新,靠一致性流量在核间传。

启示(对性能的理解):

  • 不同核频繁读写同一 cache line → 一致性消息风暴、核间反复失效 → 成为瓶颈(这也是假共享本质,见 concurrency/lock-optimize)。
  • 因此尽量让写集中、减少核间“来回弹”同一地址

二、NUMA(非一致性内存访问)

服务器多 socket:每 socket 有自己的内存控制器,访问本地内存快、远端内存慢(跨 QPI/UPI)。lscpu 可见多个 NUMA node。

  • 本地性:线程最好在离其数据所在的 NUMA node 的 core 上跑。跨 node 访问延迟/带宽都差(典型远端访问 1.3~2× 本地)。
  • Linux 默认 first-touch 分配:内存落上首次访问它的核所属 node。所以常见坑:主线程先 memset 一个大数组(全部落到 node0),再把线程 bind 到 node1→那些线程全是远端访问。
  • 对策:first-touch 并行初始化(每核自己写自己那部分才分给它)、绑核到数据所在 node(numactl / sched_setaffinity / OpenMP OMP_PROC_BIND)。
bash
numactl --hardware        # 看 node 布局
numactl --cpunodebind=0 --membind=0 ./a

三、CPU 亲和(pinning)

把线程固定到特定核(sched_setaffinity,线程池可选):

  • 好处:cache 热(进程常驻 L1/L2)、避免跑动、配合 NUMA 本地;
  • 坏处:过度绑定可能浪费空闲核、与系统其它任务抢资源;
  • 线程数/绑核策略通常以实测(吞吐/延迟抖动)为准。

四、命令速记

bash
lscpu            # cores/sockets/threads/NUMA
lstopo           # 拓扑图
numactl --hardware
taskset -c 0-3 ./a   # 绑核执行

perf / vtune 类型工具都会给 NUMA/local miss 计数。

五、应用建议

  • 多线程高吞吐 HPC/AI 服务:用 numactl 建 NUMA 感知池 + first-touch 初始化,多半比默认 10-30% 提升,常被拿去当“你会查多核性能”的证据。
  • 解释“为什么我加了核数却吞吐不涨”:争用/假共享/(cache miss) / NUMA 跨 node / 内存带宽饱和——不该只怪锁。

C++ 面试八股 · VitePress 版