在 Python 中执行 `a + b` 需要多少行 C 代码
Python 中看似简单的表达式 `a + b` 背后隐藏着大量 C 代码和运行时机制,从动态分发和魔术方法,到任意精度整数以及基于哈希的属性查找。评论者借此讨论为什么 CPython 相比基于 JIT 的运行时(如 V8、LuaJIT 或 PyPy)要慢,认为原因包括对象动态性无处不在、C API 设计,以及出于安全考虑的哈希策略。许多人认为 Python 更适合作为快速 C 或 C++ 库之上的“胶水”,而不是高性能计算引擎;若要获得显著提速,往往需要深层次的架构变更,而不是表面上的小修小补。
原始问题的范围
- 许多人指出,文章其实并没有真正回答在执行
a + b时会运行“多少行 C 代码”。 - 一些人认为这个问题本身就不太成立:一旦允许运算符重载(
__add__/__radd__),a + b就可能执行任意复杂的工作。 - 也有人建议,至少可以用代码覆盖率或性能分析来衡量一次具体运行,但即便如此,也仍然依赖于路径和输入。
动态分发、魔术方法与复杂性
- 讨论强调了即使是简单操作,在 CPython 背后也有很多机制:对象模型、通过类型槽进行的动态分发、魔术
__dunder__方法、描述符,以及用于迭代的异常。 - 与 JS 或 Lua 之类的语言相比,Python 在普通对象上更普遍地暴露“魔法”,这使得激进优化和 JIT 更困难。
- 对内建类型(例如
int)进行子类化,以及自定义描述符,都会进一步增加特化的复杂度。
性能、JIT 与设计权衡
- 一些评论者认为 CPython 本就不是以性能为首要目标;它更优化的是简洁性和与 C 的互操作性,而 Python 最适合作为快速原生库之上的“胶水”。
- 也有人反驳说,其他动态语言(JS、LuaJIT、Smalltalk、Ruby JIT)表明,只要工程投入足够,高性能是可以实现的。
- 讨论还涉及 Python 的语言设计,还是 C API 与生态系统才是阻碍一流 JIT 的主要因素。
- 还提到了近期更快的 CPython 工作以及外部 JIT(PyPy、Pyston);目前的改进被认为是真实的,但与 JS 的 JIT 革命相比仍然比较有限。
哈希、安全与速度
- 另一个分支重新讨论了“更改 Python 的哈希函数可能把性能翻倍”的说法。
- 多位评论者对此表示异议:字符串哈希会被缓存,符号名会被驻留,且那些显示巨大收益的基准测试被批评为不现实。
- Python 使用随机化的 SipHash(而不是旧的 FNV)主要是为了抵御 HashDoS;对真实工作负载的性能影响看起来很小。
- 讨论还提到了整数的身份哈希;它与哈希表实现相互作用,在某些整数分布下可能导致病态情况。
C 与 Python 的易用性,以及“直接用 C 就行”
- 有人建议直接使用 C 可能会“更容易”,但也有人强调 Python 的样板代码少得多,而且内建支持大整数。
- 示例展示了 Python 用很少的代码就能实现任意精度算术,而要在 C 或 C++ 中达到相同行为和安全性则需要更多代码。