每周性能技巧 #95:远距离的诡异作用
本节阅读量:本文翻译自 Abseil 官网的 Performance Tip of the Week #95: Spooky action at a distance。
原文发布于 2025 年 7 月 1 日,作者 Chris Kennelly,更新于 2025 年 7 月 14 日。
共享资源会让看似未改变的软件出现令人意外的性能影响。本篇讨论如何预判这些影响和外部性。
归一化技术
工作负载变化会混淆纵向分析:如果你优化了 protocol buffers 这样的库,代码中花费的时间增加,究竟意味着优化没生效,还是应用现在服务了更多负载?
A/B 测试可以控制独立变量。不过,负载均衡也可能把事情搅乱。客户端侧负载均衡算法,例如 Weighted Round Robin,可能观察到某些任务性能更好,于是向它们发送更多请求。
- 从绝对值看,两个实验分组的总 CPU 用量可能没有变化。
- 从相对值看,相对时间上可能出现小的二阶变化,例如 Protobuf 的时间占比。这可能隐藏优化的真实意义,更糟时还会让我们误以为改动造成了回退。
对于单个 job,按完成的有效工作归一化,可以比较每 CPU 的吞吐量或每 RAM 的吞吐量,从而控制工作负载迁移。
选择正确的分区方案
当进程、机器甚至数据中心层面存在共享资源时,我们希望设计一种实验方案,把这些共享资源也纳入实验组和控制组。
缓存局部性与内存带宽
缓存未命中会带来两种性能成本:代码要等待 miss 处理完成;每次 miss 都会对内存子系统施加压力,影响我们没有仔细研究的其他代码。
在前一篇文章中,我们讨论过把热路径上的 absl::node_hash_set 移到 absl::flat_hash_set 的优化。这个改动只影响流经服务器的一类特定查询。虽然这些查询类型获得了 5% 到 7% 的改善,其他完全未修改代码路径上的查询类型也出现了较小改善。
测量后一种效果需要进程级分区:一些进程始终使用 absl::node_hash_set,另一些进程始终使用 absl::flat_hash_set。没有这种技术时,服务器处理的所有请求都会和 50:50 混合的已修改和未修改代码路径共存。已修改代码路径会因为查找时缓存 miss 更少而显示性能改善。
对于未受改动影响的查询类型,它们的数据结构会看到来自相邻的已修改和未修改请求的同样混合缓存压力,而不是看到 100% 已修改或 100% 未修改的邻居。按请求随机化会阻止我们测量改动的“影响半径”。任何使用共享资源的改动都会经常遇到这个问题,也就是大多数改动。
这种实验设计挑战在负载测试中特别明显。如果没有同时研究不同请求类型的正确查询分布,就无法看到改动的完整影响,包括正向和负向影响。
更好的 hugepage 覆盖让所有船都涨起来
在 Temeraire 这个 TCMalloc 的 hugepage 感知分配器之前,许多团队选择不定期从 TCMalloc page heap 释放内存,以最大化 hugepage 可用性和 CPU 性能。其他 CPU 密集度较低的工作负载则会定期释放内存,因为 TCMalloc heap 上的空闲页体现了 CPU 使用和 RAM 使用之间的另一种权衡。
之后又有若干额外优化修改了行为,但最初 rollout 的目标是保持旧 page heap 的“按需释放”特征,以最小化权衡。确保几乎所有应用都不会比之前使用更多内存,让我们可以把注意力集中在 CPU 性能和少数分配模式边界情况上。
在 fleetwide rollout Temeraire 之前运行 A/B 实验时,我们看到了 hugepage 覆盖改善,甚至那些并不会定期释放内存的应用也受益。
在所有地方以 hugepage 感知方式管理内存,即使在我们没有预期会有显著收益的地方,也改善了性能。新的分配器允许整块 hugepage 被归还,完全避免物理内存碎片,并允许我们迭代地优先使用已经碎片化的页面来满足请求。
这些额外收益只有通过机器级 A/B 测试才能识别。虽然我们借助许多积极早期采用者启用了 Temeraire,但在那些实验中,我们只能看到一阶效果,也就是对修改应用的直接影响,而看不到二阶效果,也就是更规矩的邻居。
分布式系统
在更大的分布式服务栈中,我们可能需要进一步划分资源,避免结果被混杂因素污染。
- 例如,如果我们在 A/B 实验期间向服务后端发送更多请求,更高负载的影响会被涂抹到所有后端上,同时影响实验组和控制组的延迟。几个团队为此使用了“stripes”设置,将服务栈分区,使请求一旦路由到某个特定分区,就不会再跨回其他分区。
- 作业调度的改动可能影响数据中心中工作如何分布到各台机器。如果我们改善了一些机器上的性能,实际降低了它们的利用率,控制平面可能会调整机器分配以重新平衡。
即使 job 实际仍然有相同数量的 CPU 时间,更忙的机器也可能因为内存带宽、缓存竞争和频率缩放效果而转化为更低的单核性能,给实验组带来逆风。
结语
管理并减少共享资源争用,是性能机会的重要来源。不过,要完整识别这些机会,需要仔细考虑实验设计,确保混杂因素不会遮蔽真实收益。