拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CPython JIT 关闭期断言修复解读:冷执行器(Cold Executor)的 vm_data 初始化

CPython JIT 关闭期断言修复解读冷执行器Cold Executor的 vm_data 初始化【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython导读本文围绕 CPython 源码仓库中一条针对JIT即时编译器与解释器关闭流程的缺陷修复记录展开剖析解释器关闭期间触发 JIT 断言失败这一问题的根因与修复方式。通过阅读本文你将理解 CPython 优化器/执行器Executor体系中_Py_ExecutorInit()与_PyVMData字段的初始化约定掌握冷执行器Cold Executor为何会绕过标准初始化流程、以及如何通过补齐字段初始化来消除关闭期断言。核心修复位于 Python/optimizer.c 与 Include/internal/pycore_optimizer.h。1. 修复记录原文本文依据的关联文档是仓库内的一条 NEWS 变更记录Fix a JIT assertion during interpreter shutdown by initializingvm_datafields for cold executors that bypass_Py_ExecutorInit().来源Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-16-18-25.gh-issue-154014.CJsjVL.rst这条记录属于Core_and_Builtins类别对应 GitHub Issue #154014主要描述了两件事症状解释器关闭interpreter shutdown期间会触发一条 JIT 断言失败根因与修复某些冷执行器cold executors在创建时绕过了_Py_ExecutorInit()这一标准初始化函数导致其vm_data字段未被完整初始化需要在创建路径中显式补齐。下面结合源码逐层展开说明这条修复背后的技术机理。2. 背景CPython 的优化器与执行器体系CPython 3.13 引入了基于字节码到微操作uopmicro-operation的优化器Optimizer与执行器Executor机制。热点字节码片段会被优化器编译为一条trace封装在_PyExecutorObject中解释器在执行时遇到ENTER_EXECUTOR指令会尝试复用已编译的执行器以提升执行效率这也是 CPython JIT 功能_Py_JIT编译选项的基础载体。执行器的核心数据结构定义在 Include/internal/pycore_optimizer.htypedef struct { uint8_t opcode; uint8_t oparg; uint8_t valid; uint8_t chain_depth; // Must be big enough for MAX_CHAIN_DEPTH - 1. bool cold; uint8_t pending_deletion; int32_t index; // Index of ENTER_EXECUTOR (if code isnt NULL, below). int32_t bloom_array_idx; // Index in interp-executor_blooms/executor_ptrs. _PyExecutorLinkListNode links; // Used by deletion list. PyCodeObject *code; // Weak (NULL if no corresponding ENTER_EXECUTOR). } _PyVMData; typedef struct _PyExecutorObject { PyObject_VAR_HEAD const _PyUOpInstruction *trace; _PyVMData vm_data; /* Used by the VM, but opaque to the optimizer */ uint32_t exit_count; uint32_t code_size; size_t jit_size; void *jit_code; _PyJitCodeRegistration *jit_registration; _PyExitData exits[1]; } _PyExecutorObject;值得注意的关键设计_PyVMData被注释为Used by the VM, but opaque to the optimizer即对优化器不透明、由虚拟机VM负责维护的元数据区其中valid表示执行器是否仍有效失效后会被替换为冷执行器pending_deletion标记是否已进入待删除链表bloom_array_idx记录该执行器在解释器级executor_blooms/executor_ptrs数组中的下标code弱引用关联的代码对象_PyExecutorObject的exits[1]是柔性数组flexible array存放若干个出口_PyExitData每个出口指向下一个执行器用于构建执行链。3. 正常路径_Py_ExecutorInit()的初始化约定所有常规执行器在投入使用之前都必须调用 Python/optimizer.c 中的_Py_ExecutorInit()完成vm_data的初始化/* This must be called by optimizers before using the executor */ int _Py_ExecutorInit(_PyExecutorObject *executor, const _PyBloomFilter *dependency_set) { executor-vm_data.valid true; executor-vm_data.pending_deletion 0; executor-vm_data.code NULL; if (link_executor(executor, dependency_set) 0) { return -1; } return 0; }该函数承担三类职责设置有效性标志vm_data.valid true使执行器可被 VM 正常使用清零删除标记vm_data.pending_deletion 0确保执行器不处于待删除状态登记到解释器级数组通过link_executor()将执行器与依赖集合_PyBloomFilter布隆过滤器挂到PyInterpreterState的executor_blooms/executor_ptrs数组并把数组下标写入executor-vm_data.bloom_array_idx见 Python/optimizer.c。调用点在优化器完成 trace 编译之后Python/optimizer.c若_Py_ExecutorInit()返回负值说明登记失败优化器会放弃该执行器。与之对应的注销逻辑是unlink_executor()Python/optimizer.c它用交换删除swap-remove方式从数组中移除执行器并维护bloom_array_idx的一致性最后将其置为-1。4. 冷执行器绕过_Py_ExecutorInit()的特殊路径当某个执行器因依赖失效、去优化deoptimization等原因被判定不可用时VM 会用一个冷执行器cold executor替换它。冷执行器本身不再执行任何优化逻辑而是触发重新跟踪re-tracing流程。该对象由make_cold_executor()创建Python/optimizer.cstatic _PyExecutorObject * make_cold_executor(uint16_t opcode) { _PyExecutorObject *cold allocate_executor(0, 1); if (cold NULL) { Py_FatalError(Cannot allocate core JIT code); } ((_PyUOpInstruction *)cold-trace)-opcode opcode; // Cold executors bypass _Py_ExecutorInit(). cold-vm_data.valid true; cold-vm_data.pending_deletion 0; cold-vm_data.code NULL; // This is initialized to false so we can prevent the executor // from being immediately detected as cold and invalidated. cold-vm_data.cold false; #ifdef _Py_JIT cold-jit_code NULL; cold-jit_size 0; if (_PyJIT_Compile(cold, cold-trace, 1)) { _PyExecutor_Free(cold); Py_FatalError(Cannot allocate core JIT code); } #endif _Py_SetImmortal((PyObject *)cold); return cold; }源码注释明确说明Cold executors bypass_Py_ExecutorInit().——即冷执行器是这条初始化约定的唯一例外。原因不难理解冷执行器不会被登记进解释器级的executor_blooms/executor_ptrs数组它不需要参与依赖失效追踪因此不必调用link_executor()但它仍然要作为_PyExecutorObject被 VM 读取vm_data字段例如valid、pending_deletion、cold等一旦这些字段残留未初始化如堆上随机值或calloc零值就可能引发错误判断。冷执行器通过两个公开接口按需懒创建并缓存在解释器状态中Python/optimizer.c_PyExecutor_GetColdExecutor()→ 缓存于interp-cold_executor对应_COLD_EXIT_r00_PyExecutor_GetColdDynamicExecutor()→ 缓存于interp-cold_dynamic_executor对应_COLD_DYNAMIC_EXIT_r00。这两个缓存指针定义在 Include/internal/pycore_interp_structs.h。5. 根因关闭期清理为何触发断言问题出在解释器关闭interpreter shutdown / finalization阶段对执行器体系的清理过程。从源码结构看关闭期会遍历并注销仍存活immortal的执行器其中就包括被替换后回收的冷执行器路径unlink_executor()中带有防御性断言assert(idx 0 (size_t)idx interp-executor_count)Python/optimizer.c它依赖vm_data.bloom_array_idx处于已登记的有效下标或-1这两个合法状态而关闭期还会检查vm_data.pending_deletion/vm_data.valid/vm_data.cold等标志位来决定回收策略参考 Python/bytecodes.c 中DEOPT_IF(!current_executor-vm_data.valid)以及vm_data.cold标志的读取逻辑在启用 JIT#ifdef _Py_JIT的构建中冷执行器还会携带jit_code/jit_size关闭期需要正确释放 JIT 代码注册项。若冷执行器的vm_data字段未初始化这些标志位和下标在关闭期被读取时就是不确定值从而触发断言失败assertion failure或错误的回收行为——这正是 NEWS 条目中 JIT assertion during interpreter shutdown 的直接表现。6. 修复方式与验证修复的核心思路是让绕过_Py_ExecutorInit()的冷执行器也在创建时把vm_data的关键字段显式初始化到位。对照 Python/optimizer.c 可以看到make_cold_executor()中现在补齐了// Cold executors bypass _Py_ExecutorInit(). cold-vm_data.valid true; cold-vm_data.pending_deletion 0; cold-vm_data.code NULL; // This is initialized to false so we can prevent the executor // from being immediately detected as cold and invalidated. cold-vm_data.cold false;逐项对照_Py_ExecutorInit()的初始化内容vm_data字段_Py_ExecutorInit()常规路径make_cold_executor()冷路径作用validtruetrue标记执行器有效可被 VM 使用pending_deletion00未进入待删除链表codeNULLNULL冷执行器不关联任何ENTER_EXECUTOR代码位置cold不显式设置false防止冷执行器被立即识别为已冷却而再次失效bloom_array_idx由link_executor()写入不登记保持未用状态冷执行器不参与依赖布隆过滤器追踪其中vm_data.cold false是一个容易被忽略的关键细节_PyExecutorObject中的cold字段若为真VM 会将其视为失效待替换对象从而立刻再次触发去优化形成抖动thrashing。显式初始化为false是为了让这个冷执行器能够稳定存在避免被立即检测为 cold 并再次失效。7. 测试与回归验证建议要验证该修复在本地构建的 CPython 中生效可以按以下思路进行在启用 JIT 的构建配置下--enable-jit对应的_Py_JIT宏重新编译 CPython运行涉及解释器关闭路径的回归测试重点观察 JIT 相关断言是否复现。仓库中的相关测试与调试工具包括Lib/test/test_jit若存在以及 Python/bytecodes.c 中针对执行器失效/冷替换逻辑的微操作定义用 debug 构建启用assert执行大量短生命周期解释器进程的启停验证关闭期不再触发unlink_executor相关的断言。由于冷执行器属于解释器内部实现细节其正确性主要依赖vm_data初始化约定的一致性因此该修复的回归风险点在于未来若为_PyVMData新增字段必须同步更新make_cold_executor()与_Py_ExecutorInit()两处初始化点保持两条路径语义一致。8. 小结这条 NEWS 记录虽然只有两句话却精确刻画了 CPython 优化器/JIT 体系中的一个典型陷阱标准初始化函数之外的特殊对象创建路径必须自行补齐 VM 依赖的元数据。通过本文的源码级拆解可以看到常规执行器经_Py_ExecutorInit()完成vm_data初始化并登记到解释器级数组Python/optimizer.c冷执行器作为失效替换的兜底对象绕过该函数但必须手动初始化valid、pending_deletion、code、cold等字段Python/optimizer.c缺失初始化会导致解释器关闭期的清理逻辑读到不确定的标志位进而触发 JIT 断言失败修复方式即补齐上述字段。理解这一模式对后续阅读 CPython 的_PyVMData、执行器失效机制DEOPT_IF以及cold_executor缓存路径Include/internal/pycore_interp_structs.h都有直接的帮助。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门