CPython 空元组常量优化:`()` 改用 LOAD_COMMON_CONSTANT 加载并从 co_consts 中移除

发布时间:2026/10/10 5:31:36
CPython 空元组常量优化:`()` 改用 LOAD_COMMON_CONSTANT 加载并从 co_consts 中移除
编程语言语言运行时解释器标准库【免费下载链接】cpythonThe Python programming language项目地址https://gitcode.com/GitHub_Trending/cp/cpython点击查看免费下载导读本篇技术指南聚焦 CPython 编译与字节码层面的一个常量加载优化变更空元组()不再通过LOAD_CONST从每个 code object 的co_consts中加载而是改用LOAD_COMMON_CONSTANT指令从解释器interpreter级别的公共常量表中直接取出从而彻底从 per-code-object 的co_consts元组中移除。读完本文你将理解LOAD_COMMON_CONSTANT指令的完整设计公共常量表、oparg 约定、初始化与清理、编译器中该指令的生成路径以及这一变更对反汇编输出、.pyc兼容性和 JIT 优化器的影响。变更一览本变更来自 Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-18-20-41.gh-issue-148871.AeCbq7.rst原文如下The empty tuple()is now loaded viaLOAD_COMMON_CONSTANTinstead ofLOAD_CONST, removing it from per-code-objectco_conststuples.翻译过来变更前()作为普通常量被写入每个函数/模块的co_consts通过LOAD_CONST index加载变更后()通过LOAD_COMMON_CONSTANT oparg加载值由解释器进程内共享的公共常量表提供不再出现在co_consts中。从字节码魔数的版本历史看LOAD_COMMON_CONSTANT指令本体在 Python 3.14a1magic 3600引入而CONSTANT_EMPTY_TUPLE这一项是在 Python 3.16a0magic 3701加入的见 Include/internal/pycore_magic_number.h。因此本变更属于 3.16 开发周期的编译器优化工作对应 gh-issue-148871并会同步改变PYC_MAGIC_NUMBER。LOAD_COMMON_CONSTANT解释器级公共常量指令语义LOAD_COMMON_CONSTANT是一个不携带完整常量对象、只携带一个很小的整数 oparg的加载指令其定义位于 Python/bytecodes.cinst(LOAD_COMMON_CONSTANT, ( -- value)) { // Keep in sync with _common_constants in opcode.py assert(oparg NUM_COMMON_CONSTANTS); value PyStackRef_DupImmortal(tstate-interp-common_consts[oparg]); }要点指令把值压入求值栈-- value无输入操作数oparg 必须是小于NUM_COMMON_CONSTANTS的合法索引实际对象从tstate-interp-common_consts[oparg]即每个解释器实例持有的公共常量数组中取出取出的对象以immortal不可变/不参与普通引用计数回收的语义处理PyStackRef_DupImmortal因此加载本身几乎零成本没有引用计数原子操作。该指令在 Doc/library/dis.rst 中有官方说明Pushes a common constant onto the stack. The interpreter contains a hardcoded list of constants supported by this instruction. Used by theassertstatement to loadAssertionError.(versionadded:: 3.14)公共常量表14 个硬编码常量LOAD_COMMON_CONSTANT支持的解释器级常量是一个硬编码、只允许追加append-only的固定列表oparg 与常量的对应关系定义在 Include/internal/pycore_opcode_utils.hoparg 宏oparg 值加载的常量用途说明CONSTANT_ASSERTIONERROR0AssertionErrorassert语句CONSTANT_NOTIMPLEMENTEDERROR1NotImplementedError未实现/抽象方法CONSTANT_BUILTIN_TUPLE2tupletuple(...)生成器优化CONSTANT_BUILTIN_ALL3allall(...)生成器优化CONSTANT_BUILTIN_ANY4anyany(...)生成器优化CONSTANT_BUILTIN_LIST5listlist(...)生成器优化CONSTANT_BUILTIN_SET6setset(...)生成器优化CONSTANT_NONE7None通用返回/缺省值CONSTANT_EMPTY_STR8空字符串通用空串CONSTANT_TRUE9True布尔字面量CONSTANT_FALSE10False布尔字面量CONSTANT_MINUS_ONE11-1常用小整数CONSTANT_BUILTIN_FROZENSET12frozensetfrozenset(...)生成器优化CONSTANT_EMPTY_TUPLE13()空元组本变更新增Python 侧的同源列表定义在 Lib/opcode.py注释明确要求与Include/internal/pycore_opcode_utils.h中的CONSTANT_*保持一致Append-only — must match CONSTANT_* ..._common_constants [builtins.AssertionError, builtins.NotImplementedError, builtins.tuple, builtins.all, builtins.any, builtins.list, builtins.set, # Append-only — must match CONSTANT_* in # Include/internal/pycore_opcode_utils.h. None, , True, False, -1, builtins.frozenset, ()]可以看到()已经被追加为该列表的第 14 项索引 13即CONSTANT_EMPTY_TUPLE。由于是 append-only 表新增项不会打乱既有字节码中已生成的 oparg 编号。运行时初始化与生命周期公共常量数组是解释器状态PyInterpreterState的组成部分定义于 Include/internal/pycore_interp_structs.h_PyStackRef common_consts[NUM_COMMON_CONSTANTS];初始化发生在解释器启动阶段见 Python/pylifecycle.cPyObject *common_objs[NUM_COMMON_CONSTANTS] {NULL}; common_objs[CONSTANT_ASSERTIONERROR] PyExc_AssertionError; common_objs[CONSTANT_NOTIMPLEMENTEDERROR] PyExc_NotImplementedError; common_objs[CONSTANT_BUILTIN_TUPLE] (PyObject *)PyTuple_Type; ... common_objs[CONSTANT_EMPTY_TUPLE] Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_TUPLE); for (int i 0; i NUM_COMMON_CONSTANTS; i) { assert(common_objs[i] ! NULL); _Py_SetImmortal(common_objs[i]); interp-common_consts[i] PyStackRef_FromPyObjectBorrow(common_objs[i]); }实现要点空元组通过Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_TUPLE)取得interpreter 级单例静态 immortal 对象表中每个对象都被_Py_SetImmortal标记为 immortal之后被所有线程、所有 code object 共享引用无需为每次加载做引用计数解释器销毁时由 Python/pystate.c 的common_constants_clear()统一清理对非静态 immortal 对象调用_Py_ClearImmortal手动解除 immortal 标记后再释放引用。编译器如何生成 LOAD_COMMON_CONSTANT统一的常量下发路径常量指令的选择并非只在codegen阶段完成而是在flowgraph阶段统一处理。核心入口是 Python/flowgraph.c 的instr_make_load_const()其决策顺序为maybe_instr_make_load_smallint()小整数0–255 或-1尝试转为LOAD_SMALL_INToparg 即值本身maybe_instr_make_load_common_const()命中公共常量表的字面量转为LOAD_COMMON_CONSTANT兜底add_const()将常量加入co_consts生成LOAD_CONST index。maybe_instr_make_load_common_const()位于 Python/flowgraph.c逐一匹配候选常量if (newconst Py_None) { oparg CONSTANT_NONE; } else if (newconst Py_True) { oparg CONSTANT_TRUE; } else if (newconst Py_False) { oparg CONSTANT_FALSE; } else if (PyUnicode_CheckExact(newconst) PyUnicode_GET_LENGTH(newconst) 0) { oparg CONSTANT_EMPTY_STR; } else if (PyTuple_CheckExact(newconst) PyTuple_GET_SIZE(newconst) 0) { oparg CONSTANT_EMPTY_TUPLE; /* ← 本变更新增的分支 */ } else if (PyLong_CheckExact(newconst)) { ... oparg CONSTANT_MINUS_ONE; } ... INSTR_SET_OP1(instr, LOAD_COMMON_CONSTANT, oparg);因此编译器只要发现某个字面量精确等价于空元组PyTuple_CheckExact且长度为 0就立即改写指令为LOAD_COMMON_CONSTANT 13不再把()放入co_consts。注意判定使用的是PyTuple_CheckExact精确类型检查且转换前会断言_Py_IsImmortal(newconst)——只有 immortal 单例才会走这条路径普通构造出的临时元组不会误命中。同时loads_const()Python/flowgraph.c把LOAD_COMMON_CONSTANT纳入常量加载指令集合后续的常量折叠get_const_value()见 Python/flowgraph.c能够通过common_consts[oparg]重新取得实际常量值保证优化管线如()参与元组解包、展开依旧可感知到该常量。codegen 中的既有用法除字面量优化外编译器在多处显式使用LOAD_COMMON_CONSTANT加载内置对象例如 Python/codegen.c、Python/codegen.c 分别加载NotImplementedError与AssertionError在all/any/tuple/list/set/frozenset(genexp)优化中用LOAD_COMMON_CONSTANT加载对应内置类型后与栈上的函数做Is比较见 Python/codegen.c。这些机制共享同一个公共常量表。变更的实际效果与验证空元组不再出现在 co_consts 中编译测试 Lib/test/test_compile.py 直接锁定了新的字节码形态(*(),): [ (RESUME, 0), (LOAD_COMMON_CONSTANT, ()), # 空元组从公共常量表加载 (RETURN_VALUE, None), ], (*(), *()): [ (RESUME, 0), (LOAD_COMMON_CONSTANT, ()), (RETURN_VALUE, None), ], (*(), 1): [ (RESUME, 0), (LOAD_CONST, (1,)), # 非空常量仍走 LOAD_CONST (RETURN_VALUE, None), ],注意(*(), 1)的例子展开结果(1,)是非空元组仍作为普通常量放进co_consts走LOAD_CONST恰好印证了只有空元组()本身被移除。反汇编展示dis模块能正确解析LOAD_COMMON_CONSTANT并显示常量名或字面量Lib/dis.py 与 Lib/dis.pyoparg 7–11 与 13 属于字面量None、、True、False、-1、()可直接回填真实值0–6 与 12 属于可调用对象内置函数/类型反汇编输出类名。典型输出格式来自 Lib/test/test_dis.pyLOAD_COMMON_CONSTANT 7 (None)对应空元组场景则显示为LOAD_COMMON_CONSTANT 13 (())之类的字面量注解。dis.hasconst相关逻辑Lib/dis.py、Lib/dis.py已把该指令纳入带常量语义的指令集合确保dis.get_instructions()、co_consts分析类工具行为一致。另外Lib/test/test_ast/test_ast.py 的辅助函数get_load_const()会同时收集hasconst指令与LOAD_COMMON_CONSTANT的参数用于断言 AST 常量求值路径正确。异常处理路径中的空元组/NoneLib/test/test_code.py 展示了一个有趣细节解释器在except块合成的人工指令序列中None是通过LOAD_COMMON_CONSTANT注入的。这说明该指令不仅是编译期优化也是运行时栈布局重建的一部分与co_consts解耦让这类合成值无需污染代码对象的常量表。与 JIT/优化器的协同LOAD_COMMON_CONSTANT在 Tier-2微指令与 JIT 层面同样受支持见 Include/internal/pycore_uop_ids.h#define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT及其多个版本变体_r01/_r12/_r23对应不同寄存器分配形态。在优化器模拟执行阶段Python/optimizer_bytecodes.c_LOAD_COMMON_CONSTANT会从common_consts[oparg]取出 immortal 常量并记录到符号表中供后续常量传播使用op(_LOAD_COMMON_CONSTANT, (-- value)) { assert(oparg NUM_COMMON_CONSTANTS); PyObject *val PyStackRef_AsPyObjectBorrow( _PyInterpreterState_GET()-common_consts[oparg]); assert(_Py_IsImmortal(val)); ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); value PyJitRef_Borrow(sym_new_const(ctx, val)); }JIT 测试 Lib/test/test_capi/test_opt.py 则验证优化器最终会消除_LOAD_COMMON_CONSTANT微指令改为直接内联常量的借用引用_LOAD_CONST_INLINE_BORROW使生成代码零开销访问常量同时对None//True/False/-1等新字面量test_load_common_constant_new_literals断言同样的消除行为。空元组的加入同样受益于这一通道进入 JIT 后成为寄存器中的常量不产生访存与引用计数开销。兼容性提示.pyc缓存失效LOAD_COMMON_CONSTANT的常量表变化会改变编译器生成的字节码进而更新PYC_MAGIC_NUMBER本变更对应 magic 3701见 Include/internal/pycore_magic_number.h。旧缓存.pyc会因 magic 不匹配而被自动忽略并重新编译。co_consts内容变化使用code.co_consts做静态分析、序列化或缓存 key 的工具在 3.16 下看到函数/模块的co_consts中不再包含()需要改从LOAD_COMMON_CONSTANT的 oparg 结合_common_constantsLib/opcode.py恢复真实常量。仅限字面量()该优化只针对编译器能精确识别的空元组字面量运行时构造的空元组如tuple()[:0]的求值结果不受影响依然走普通对象创建路径。小结本变更把空元组()从 per-code-object 的co_consts中彻底移除改为通过LOAD_COMMON_CONSTANT 13CONSTANT_EMPTY_TUPLE从解释器级 immortal 公共常量表加载。其收益体现在三方面字节码瘦身每个用到()的函数不再重复携带该常量co_consts更小.pyc文件更紧凑执行更快加载 immortal 常量免去引用计数原子操作且利于 JIT 内联为寄存器常量统一设计与既有 13 项公共常量None、、True、False、-1、内置函数/类型等共享同一条 append-only 常量表与初始化/清理管线保持了编译器 – 反汇编器 – 优化器三方的单一事实来源Include/internal/pycore_opcode_utils.h 与 Lib/opcode.py。赞分享编程语言语言运行时解释器标准库【免费下载链接】cpythonThe Python programming language项目地址https://gitcode.com/GitHub_Trending/cp/cpython点击查看免费下载相关推荐终极FTXUI动态组件加载指南运行时添加/移除UI元素终极FTXUI动态组件加载指南运行时添加/移除UI元素 FTXUI是一个功能强大的C终端用户界面库提供了 动态组件加载 功能让你能够在运行时灵活地添加UI组件vanilla-lazyload 的 loadAll() 性能优化一次加载全部懒加载元素并解除观察的完整指南vanilla lazyload 的 loadAll 性能优化一次加载全部懒加载元素并解除观察的完整指南 本指南围绕 currentFeature.md 与前端es-toolkit 的 pullAt 数组定点删除指南按索引批量移除元素并返回被删值es toolkit 的 pullAt 数组定点删除指南按索引批量移除元素并返回被删值 pullAt 是 es toolkit 数组模块提供的一个按索引定点删前端后端上一篇Facebook iOS SDK 终极数据持久化指南用户会话与配置信息存储方案下一篇突破蛋白质设计瓶颈ByteDance-Seed/PAR零样本基序支架技术原理与应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考