本页目录

性能 II · 缓存优化实战

对标:MIT 6.172 / What Every Programmer Should Know About Memory(Drepper)| 前置:csapp-02(缓存原理)、perf-01(测量、Roofline) perf-01 建立了性能的科学方法,这一页把最大的性能杠杆——缓存——从理论(csapp-02)变成一套可操作的优化手法,并收束到 [大 Project P06] "把一个基线程序优化 100×"的终极挑战。核心信念不变:现代程序的性能主要由内存访问模式决定,谁能把数据喂进缓存,谁就赢。

1. 缓存优化的四把手术刀

把 csapp-02 的原理落成四类可执行的手法:

① 改善空间局部性——让访问连续

② 改善时间局部性——趁热复用

③ 减少缓存未命中的总量

④ 避免多线程的缓存灾难

读法这四把刀覆盖了 90% 的实用缓存优化。遇到性能问题先问:"数据访问连续吗?复用充分吗?结构紧凑吗?线程在抢缓存行吗?"

2. 数据结构的缓存代价:一个反直觉的真相

数组(连续,预取,少 miss) vs 链表(散落,每节点 miss) 的缓存行为。

图 perf-02.2数组(连续,预取,少 miss) vs 链表(散落,每节点 miss) 的缓存行为。

算法课教你链表插入 O(1)、数组插入 O(n)——但在真实硬件上,遍历一个数组常常比遍历等长链表快一个数量级,因为:

推论"渐进复杂度相同(甚至更差)的数据结构,因为缓存行为可能快很多"——std::vector 常胜 std::list、扁平数组常胜指针树。现代高性能数据结构(B+ 树的高扇出 db-01、列存 db-02、ECS 游戏架构)本质都是为缓存重新设计的数据结构这是算法理论与硬件现实的一个重要缝隙,性能工程师必须跨过它

3. 编译器、内存与你的分工

优化里要清楚"谁该做什么":

方法论闭环(perf-01 的科学方法在此实操):perf 找热点 → 看是否 cache miss 高(perf stat 的 cache-misses)→ 判断内存还是算力受限(Roofline)→ 应用对应手术刀 → 再测量确认。每一步都有数字,不靠感觉

4. 超越单机:性能优化的完整层级

优化层级金字塔:算法→数据布局→向量化→多核→GPU→分布式(收益递减、代价递增)。

图 perf-02.1优化层级金字塔:算法→数据布局→向量化→多核→GPU→分布式(收益递减、代价递增)。

优化是有层级的,从高到低收益递减、代价递增——按顺序来:

  1. 算法/数据结构(O(n²)→O(n log n),收益最大,先做)。
  2. 数据布局与缓存(本页,常见 2~10×)。
  3. 向量化与指令级并行(perf-01,2~16×)。
  4. 多核并行(par 线,~核数倍)。
  5. GPU/加速器(gpu 线,特定负载 10~100×)。
  6. 分布式(跨机器,dist 线,用于超大规模)。

先爬对楼层:一个 O(n²) 算法再怎么向量化也输给 O(n log n)——Amdahl 和这个层级表一起,构成"该优化什么、按什么顺序"的完整决策框架。这是把全站性能相关知识(算法/缓存/SIMD/并行/GPU/分布式)串成一条优化路线的总纲。

5. 练习与要点

例 1(数组 vs 链表实测) 遍历求和一个 1000 万元素的数组 vs 等长链表——数组快近十倍,用 perf stat 看 cache-misses 差异。"渐进复杂度不是全部"一次刻进去

例 2(循环融合) 两个分别对数组做 scaleshift 的循环,融合成一个——测量加速(数据只过一遍缓存)。小重构、真收益

例 3(爬对楼层) 给一个"用 O(n²) 算法 + 未向量化"的慢程序,判断先做什么(换算法,别急着向量化 O(n²))——练"优化顺序"的判断力,这比任何单一技巧都值钱。\(\blacksquare\)


📋 大 Project P06 · 性能工程终极题(优化 100×)

教师版作业说明书,不提供完整解。 P06 的重点不是炫技,而是训练“测量 → 假设 → 修改 → 复测”的工程循环。

  • 学习目标:把算法复杂度、缓存局部性、SIMD、多线程、Roofline 分析串成一个完整优化案例。
  • 教师提供:三选一基线程序(N 体模拟 / 图像卷积 / 稀疏矩阵运算)、固定输入集、正确性校验器、benchmark harness、计时脚本、报告模板。
  • 学生任务:① 建立 baseline,记录 wall time、cache miss、IPC、Roofline 位置;② 算法或数据结构替换;③ 数据布局与缓存优化;④ SIMD 或自动向量化辅助;⑤ 多线程并行;⑥ 可选 GPU/Metal/CUDA 版本。
  • 约束:每次提交只能包含一个主要优化点;不得牺牲数值正确性;所有 benchmark 要固定机器、编译器、输入规模并重复取中位数;报告必须保留失败尝试。
  • 验收测试:输出误差在给定阈值内;相对 baseline 达到分档加速(及格 20×,良好 50×,优秀 100×);每一步都有 before/after 数字和解释;隐藏输入上没有退化。
  • 评分重点:测量纪律 25%,优化有效性 35%,正确性与泛化 20%,报告质量 20%。
  • 延伸挑战:用同一 workload 画出单线程优化、多线程优化和 GPU 优化的 Roofline 迁移图,解释瓶颈如何变化。

下一页:并行 I——并行模型与缓存一致性:从单核榨性能到用多核,共享内存并行的原理与陷阱。