每周性能技巧 #74:不要把路灯扫到地毯下面
本节阅读量:本文翻译自 Abseil 官网的 Performance Tip of the Week #74: Sweeping the streetlight under the rug。
原文发布于 2024 年 1 月 16 日,作者 Chris Kennelly,更新于 2025 年 8 月 23 日。
“路灯效应”描述的是:我们倾向于在容易观察的地方找答案,而不是在真正可能藏有答案的地方。本篇讨论性能分析中类似陷阱:可见成本不一定是真实成本,隐藏成本不一定可以忽略。
可见不等于重要
Profile 中显眼的函数很容易吸引注意。优化这些函数有时是正确选择,但也可能只是因为工具更容易把成本归因到它们身上。
例如,某个库函数可能承担了等待成本,真正的问题却来自之前的内存访问、锁竞争或 I/O。简单地让这个函数看起来更便宜,不一定改善端到端结果。
相反,有些优化会让某个组件自己的 profile 看起来更差,却改善系统整体。例如,预取指令可能把成本记到发出 prefetch 的库上,但它减少了后续等待,让调用方变快。
隐藏成本
隐藏成本常见于共享资源:
- 内存带宽和缓存压力。
- 分配器碎片。
- 后端服务请求量。
- 调度器和负载均衡行为。
- 代码大小带来的指令缓存和 iTLB 压力。
这些成本可能不会出现在被修改函数的局部 profile 中,但会影响整体生产效率。只看局部指标,可能会把成本转移误判为优化。
使用代理指标
代理指标可以帮助我们看到不易直接测量的成本。例如 PMU counter 可以显示 cache miss、TLB miss 或 stalled cycles。内存分配 profile 可以显示分配频率和大小分布。端到端 A/B 测试可以显示跨组件影响。
代理指标不是目标本身。我们使用它们,是为了解释为什么主指标变化,或为什么主指标没有变化。代理指标与主结果冲突时,应该重新检查假设。
避免只优化工具能看到的东西
一个实用策略是同时看多种视角:
- 函数级 CPU profile。
- 分配和 heap profile。
- 硬件计数器。
- 业务或系统级吞吐量指标。
- 代表性负载测试和生产实验。
不同工具的盲点不同。多个工具交叉验证,可以减少我们只在路灯下找钥匙的风险。
结语
性能优化不是让 profile 中某个数字变好,而是让系统以更少资源完成更多有用工作。可见成本是调查起点,不是结论。