当 15% 的加速只是一场编译器的误会——读《Performance of the Python 3.14 ... 》
当 15% 的加速只是一场编译器的误会——读《Performance of the Python 3.14 tail-call interpreter》
Nelson Elhage 的这篇博客讲了一个令人拍案又令人后怕的故事:CPython 3.14 合入了全新的尾调用(tail-call)解释器,基准测试显示 10–15% 的性能提升,社区一片叫好。然而作者花了三周时间反复编译、反汇编、对比数十个 Python 二进制后,发现这轮"神级优化"的主要功劳并非来自新解释器本身,而是它无意间绕过了 LLVM 19 的一处尾复制(tail-duplication)回归 bug。换一个更公平的基线——GCC、clang-18 或打了修复 flag 的 clang-19——真实增益只有 1–5%。
读完最大的感受是:性能数字从来不是孤立的,它总是相对于某个基线而言,而基线的选取本身就是一种"性能理论"。 文章把这个抽象道理讲得极其具体:clang-19 把 computed goto 的分发逻辑合并成一条间接跳转,等于把程序员精心手写的优化全部抵消,而旧解释器恰好踩中了这个坑,于是"新 vs 旧"的对比里,新解释器看起来赢了 15%,其实只是旧的不该那么慢。作者在 Anthropic 做 ML,他指出论文里最该追问的第一句话往往不是"他们做了什么",而是"他们跟谁比的"——这句话我深以为然,它不只适用于编译器,也适用于每一个"提升了 X%"的技术报告。
第二个感触是软件生态的复杂度已超出任何个体的掌控范围。LLVM 发了一个版本,CPython 慢了 10%,五个月没人发现——单看概率似乎不大,但乘以海量项目之间的交叉依赖,这类"隐形回归"几乎必然时刻发生。我们习惯性地信任编译器"正确性",却很少追问它是否忠实于程序员的性能意图。computed goto 在源码层面和"分发复制"在机器码层面,竟然近乎正交,四象限的每一种组合都在实验中出现了。现有工具只能保证行为等价,无法表达"我写这段代码是为了触发某种优化",这暴露出编译器与程序员之间对话机制的缺失。
文章结尾对 musttail 这类新特性的期待让我看到了一线希望:它不改变可观测行为,而是直接与优化器对话,若优化未发生则编译失败。这种"硬约束"式的性能契约,或许比靠手写 hack 去暗示编译器更可靠、更抗版本漂移。
最后,作者用 nix 管理数十种构建矩阵的经历也让我印象深刻——可复现、可追溯的构建环境,正是做严肃性能比较的前提条件,否则连"哪个二进制用了哪个 flag"都说不清,结论便无从谈起。
这篇博文最可贵的地方在于态度:作者反复声明不想责怪 Python 团队,因为"这种混淆太常见了,我自己也误解过很多基准测试"。它不是一篇"打脸"文,而是一份关于性能工程方法论的自省——提醒每一个写"提升了 X%"的人,先问清楚:跟谁比、为什么、还有没有第三种解释。
- 点赞
- 收藏
- 关注作者
评论(0)