一个月前,CPython 项目为其字节码解释器合并了新的实现策略,初始结果令人印象深刻,多个平台的各种基准测试显示平均性能提升 10 - 15%。但后续发现这些性能提升主要是因为无意中绕过了 LLVM 19 的回归问题,与更好的基线(如 GCC、clang - 18 或带有特定调优标志的 LLVM 19)对比,性能提升降至 1 - 5%左右。
性能测试方面:在 Intel 服务器(Raptor Lake i5 - 13500)和 Apple M1 Macbook Air 上,对不同编译器和配置选项构建的 CPython 解释器进行基准测试,包括 clang18、gcc(仅 Intel)、clang19、clang19.tc、clang19.taildup 等。结果显示尾调用解释器仍比 clang - 18 有提升,但远不及从 clang - 18 到 clang - 19 的性能下降。
LLVM 回归相关:经典字节码解释器由while
循环内的switch
语句组成,现代 C 编译器会将switch
编译为跳表,而 Clang 和 LLVM 内部会将goto
合并为单个indirectbr
指令,代码生成时进行“尾复制”。LLVM 19 对尾复制过程设置了限制,导致 CPython 中 Clang 保留了所有的分发跳转合并,完全违背了基于计算goto
的实现目的。通过反汇编对象代码可直接观察到该问题,且无法完全解释回归的幅度。
关于是否需要计算goto
:clang - 18(或带有适当标志的 clang - 19)在面对“经典”switch
基础的解释器时,会将分发逻辑复制到每个操作码体中,而 clang19.nocg 基准测试声称比 clang19 更快,这表明整个“计算goto
”解释器可能是不必要的复杂性。
修复方法:LLVM 拉取请求 114990 修复了该回归问题,对于之前的版本,可通过设置尾复制的阈值来恢复类似行为。
反思:在基准测试方面,要意识到基准测试嵌入了“性能理论”,需谨慎将特定数据点推广到更广泛的结论;在软件工程方面,软件系统复杂且相互关联,回归问题不可避免,计算goto
解释器的 saga 体现了优化器和优化编译器的紧张关系;在使用nix
方面,nix
在管理多个 Python 解释器版本和构建过程中非常有帮助,但也存在一些“怪异”之处需要注意。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。