CPython 3.11.0a6 变更全解读:内联缓存自适应特化、调用序列重构、字典瘦身与 asyncio TaskGroup CPython 3.11.0a6 变更全解读内联缓存自适应特化、调用序列重构、字典瘦身与 asyncio TaskGroup【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython导读本文基于 CPython 官方仓库中的历史发布记录 Misc/NEWS.d/3.11.0a6.rst对应 2022 年 3 月 7 日发布的3.11.0a6即 Python 3.11 正式版发布前的第 6 个 alpha 快照逐类梳理该里程碑引入的变更包括贯穿解释器执行引擎的内联缓存 自适应特化改造CACHE指令、LOAD_ATTR/BINARY_OP等关键指令特化、PUSH_NULL/PRECALL调用序列重构、字典实现的多项内存与正确性优化、asyncio.TaskGroup结构化并发、typing.Self/Never等新 API以及构建系统对 C11/C99 的硬性要求。读完本文你将理解这些变更对 Python 3.11更快与更稳两个目标的贡献方式并能在当前源码树中找到对应实现如 Python/bytecodes.c、Python/specialize.c、Lib/asyncio/taskgroups.py 等。说明本仓库当前 main 分支版本为 3.16.0a0见 Include/patchlevel.h本文所述 3.11.0a6 为历史里程碑文中标注的源码位置为当前仓库中该特性后裔的落地形态可用于对照研读。一、核心与内建3.11 的执行引擎改造3.11.0a6 中Core and Builtins部分的绝大多数条目都围绕一个共同主题把自适应特化 内联缓存adaptive specialization / inline cache机制铺开到更多字节码指令这是 3.11 性能提升的根基。1.1 引入CACHE指令缓存从旁路走向内联多个条目bpo-46841 系列指出BINARY_OP、LOAD_ATTR、LOAD_METHOD、STORE_ATTR、BINARY_SUBSCR、COMPARE_OP、UNPACK_SEQUENCE、LOAD_GLOBAL等指令均开始使用内联缓存inline cache。在 3.11.0a6 中缓存机制发生了一次关键设计转向特化数据如类型版本号、期望的对象类型、属性偏移量等不再依赖外部旁路数据结构而是直接以一条新的CACHE指令的形式紧跟在被特化指令之后内联于字节码流中。这样做的收益体现在两个方面取缓存值不产生额外间接寻址解释器主循环可以按固定步长跳读缓存槽同一批变更同时实现了 bpo-44337 所要求的减少特化LOAD_ATTR/STORE_ATTR的内存占用。对照当前源码CACHE指令依然以 Tier-1 指令形式存在于 Python/bytecodes.c 中而所有特化决策_Py_Specialize_BinaryOp、_Py_Specialize_LoadAttr、_Py_Specialize_CompareOp、_Py_Specialize_UnpackSequence、_Py_Specialize_LoadGlobal、_Py_Specialize_StoreAttr、_Py_Specialize_Call等集中在 Python/specialize.c 中可对照研读。1.2 一批指令获得首个特化形态0a6 阶段特化范围显著扩大部分条目还给出了具体目标条目要点说明bpo-45107LOAD_METHOD针对带实例字典的对象instance with a dict提供专门特化bpo-46702UNPACK_SEQUENCE针对tuple与list的解包分别特化bpo-46841为BINARY_SUBSCR、COMPARE_OP、UNPACK_SEQUENCE、LOAD_GLOBAL、LOAD_ATTR/LOAD_METHOD/STORE_ATTR补上内联缓存bpo-46823实现组合指令LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE把读取局部变量与取实例属性值合并成一条指令减少循环与指令分派开销作者 Dennis Sweeney值得注意的是 bpo-46841 还附带一处修复当BINARY_OP被特化时其内联缓存条目的处理方式曾出现错误incorrect handling of inline cache entries此版本一并修正——说明内联缓存机制本身在推广过程中也在不断纠错。1.3 调用序列重构PUSH_NULL与统一PRECALLbpo-46329 系列条目把调用这一最热路径重排新增PUSH_NULL指令在求值可调用对象时先在栈上压入一个空位使方法调用与普通函数调用在栈上具有相同形状method 调用会在栈上额外出现 self靠这个空位对齐参数区。原先的PRECALL_FUNCTION与PRECALL_METHOD两条指令合并为单条PRECALL。KW_NAMES的位置移到PRECALL之前CALL的 operand 改为与PRECALL一致便于对整条调用序列做统一特化对CALL的判型逻辑更简单。同时修复了PRECALL指令的特化统计收集bpo-46329。语义上并无变化——该条目明确注明There is no change in semantics这是一次纯粹的内部形态调整。1.4 特化统计体系的完备化特化机制要落地必须能回答哪些指令常失败、失败原因是什么。0a6 为此做了大量统计侧工作bpo-46072为BINARY_OP增加更细粒度的特化失败统计bpo-46841为COMPARE_OP后跟EXTENDED_ARG的形态补详细特化失败统计bpo-45885收集特化统计时不再对COMPARE_OP做反适配un-adapt避免统计行为本身改变代码形态bpo-46072opcode pair指令对统计改由--enable-pystats构建选项收集旧的DYNAMIC_EXECUTION_PROFILE、DXPAIRS宏定义不再生效。1.5 快速启动路径quickenedRESUMEbpo-45923 为RESUME3.11 在函数/生成器入口插入、负责在首次执行时触发后续指令快速化 quickening的指令增加一种quickened 形态当快速化已完成、上下文已就绪时跳过常规的 quickening 检查直接进入已特化代码。该优化与组合指令共同降低了已热函数每轮执行的固定开销。1.6 编译器与代码生成调整bpo-46808删除compile.c中的NEXT_BLOCK宏改为由编译器在需要时自动生成隐式 basic block简化编译器的控制流管理。bpo-46724确保所有向后跳转统一使用JUMP_ABSOLUTE不再以JUMP_FORWARD(2**32)offset的负偏移技巧实现同步修复了 Lib/dis.py标准库dis模块对负跳转偏移的处理。bpo-45828字节码编译器开始尝试在编译期就执行可行的运行时栈操作compile-time stack manipulation把部分可在编译阶段确定形状的栈变换前移减少运行期开销。bpo-46820修复数字字面量紧贴not in关键字的解析如1not in x原先抛语法错误现在解析器只发警告——紧跟数字后出现关键字属于书写歧义但不应直接判死。1.7 修复的崩溃与错误消息改进错误消息质量的提升是 3.11 的另一条主线bpo-46940不再覆盖嵌套属性访问抛出AttributeError时携带的元数据信息——使 traceback 能更准确地指出内层失败点。bpo-46927对非泛型类型做下标操作如int[0]时错误消息中包含类型名便于定位。bpo-46730对没有对应 setter/getter/deleter 的 property执行 get/set/del 时AttributeError消息会点明该属性实为 property并给出所属类。bpo-46903修复 3.11 alpha 引入的回归——str 子类现在可用作拥有虚拟字典virtual dict实例的属性名。bpo-46891修复types.ModuleType子类搭配__slots__时初始化不完整、进而导致解释器崩溃的问题。bpo-46615setobject.c内部对集合迭代时为取出的元素获取强引用避免各类集合运算在运行中集合被修改导致的崩溃。bpo-46732修正object.__bool__的 docstring作者 Jelle Zijlstra。bpo-46762修复 debug 构建中断言失败——当 f-string 缺少右花括号且最后一个字符是//时。bpo-46707避免某些大量括号导致的语法错误在错误报告中出现指数级回溯作者 Pablo Galindo。bpo-46729BaseException.__str__现在会附带子异常sub-exceptions的数量配合 3.11 的异常组机制PEP 654提供更清晰的异常树概览。1.8 数字与内存相关的修补bpo-30496修复PyLong_FromLong的一处可移植性问题并为单位数整数在PyLong_FromLongLong中加入快速路径。bpo-46864弃用PyBytesObject.ob_shash计划 3.13 移除。bpo-46852先把私有的float.__set_format__()改名修正为float.__setformat__()修掉 3.7 引入的拼写错误随后彻底移除该私有方法——其 docstring 自述主要供测试套件使用不影响普通用户。二、字典实现的瘦身与回归修复0a6 中有多条直接命中 CPython 最核心数据结构dict的改动影响每个 Python 程序bpo-46845内存关键优化当哈希表中的全部键都是 Unicode 字符串时不再存储键的哈希值str 本身可缓存其哈希从而缩小字典条目大小。条目给出的实测sys.getsizeof(dict.fromkeys(abcdefg))在 64 位平台上从352 字节降到 272 字节。这类纯字符串键场景在真实代码如属性字典、关键字参数中极为常见。bpo-46675split-table 扩容Python 自 3.3 起用分离表split table让多个实例共享同一组键键值分离存储。此前一个实例的键超过 16 个后表会被合并combined该版本把阈值提高到 254让更多实例共享字典继续保持轻量。bpo-40116回归修复当other是 key-sharing dict 时dict.update(other)可能不遵循other的迭代顺序——0a6 修复了 3.11 alpha 引入的该回归。bpo-46841GET_AWAITABLE改用oparg 承载信息来构造更友好的错误消息例如区分对象不可 await的成因避免运行期重复解析。三、冻结模块deep-freeze字符串驻留与瘦身3.11 引入把标准库关键模块深度冻结deep-freeze进解释器的机制0a6 对此集中打磨bpo-46430修复 deep-frozen 模块中interned 字符串驻留字符串的内存泄漏bpo-46430Build 部分对 deep-frozen 模块中的字符串执行驻留intern使相同字符串跨模块共享同一对象bpo-46712在 deep-frozen 模块之间共享全局字符串标识符减少重复创建bpo-46608Build深冻结时排除 marshalled-frozen 数据节省约300 KB磁盘空间为此给_frozen结构新增is_package字段使运行时无需再依赖被剔除的元数据即可区分模块与包。四、标准库从 asyncio 到 typing 的重磅更新4.1 asyncioTaskGroup与取消语义重构bpo-46752 是 0a6 最引人注目的库变更为 asyncio 引入任务组asyncio.TaskGroup提供结构化并发structured concurrency能力——灵感来自 Trio 的 nursery 模型伴随引入取消语义变更一个已被取消的任务在调用.uncancel()之前不能被再次取消以配合任务组内多个任务同时失败时的异常传播语义。结构化并发意味着任务组内的任务同生共死——任一任务失败组内其余任务会被取消所有异常汇总后再统一抛出彻底规避后台任务悄悄失败/悬挂的经典问题。当前实现位于 Lib/asyncio/taskgroups.pyTaskGroup的__aenter__/create_task即对应新版用法配套文档见 Doc/library/asyncio-task.rst。同版本还包含一批 asyncio 修正bpo-45390把asyncio.CancelledError的消息从内层任务传播到外层等待方避免取消原因在嵌套 await 中丢失bpo-46672修复asyncio.gather初始类型检查失败时的NameErrorbpo-44011重写 asyncio 的 SSL/TLS 支持实现借自 uvloop 库提升安全连接路径性能bpo-44886让 asyncio proactor 事件循环的数据报传输datagram transport继承自asyncio.DatagramTransport统一类型体系bpo-46827为基于 selector 的事件循环在asyncio.loop.sock_connect中支持UDP socket此前仅 proactor 支持。4.2 typing新 API 井喷0a6 在typing模块中落地了一批至今仍在广泛使用的能力bpo-46534实现PEP 673typing.Self让返回自身类型的方法如链式调用、替代构造器在类型检查中表达精确bpo-46475新增typing.Neverbottom type表示不可能返回/不可达分支与配套的typing.assert_never()穷尽性检查的运行时断言当前实现见 Lib/typing.py 中assert_neverbpo-46333两组typing.ForwardRef的__eq__/__hash__/__repr__现在尊重module参数——来自不同模块的字符串前向引用不再被混淆bpo-46643 / bpo-46655typing.get_type_hints开始支持求值字符串化的ParamSpecArgs/ParamSpecKwargs以及裸的字符串化TypeAlias注解bpo-46195get_type_hints不再为默认值为None的参数自动补成Optional[...]与 PEP 484 的后续澄清对齐bpo-46571改进typing.no_type_check——不再修改外部类与函数避免污染第三方对象并正确标记 classmethod 为不参与类型检查bpo-46676让typing.ParamSpec的 args 与 kwargs 各自等于自身消除P.args ! P.args这类反身性异常bpo-46066弃用基于 kwargs 语法定义typing.TypedDict如TypedDict(X, aint)之外通过关键字传值的形式因其在指定 total 时语义含混且基本无人使用。4.3 安全与正确性修复bpo-46756认证绕过漏洞修复修复urllib.request.HTTPPasswordMgr.find_user_password与HTTPPasswordMgrWithPriorAuth.is_authenticated的前缀匹配授权绕过——此前已授权example.org/foo的用户可访问example.org/foobar。bpo-46252把ssl.SSLSocket传入 transport 类 API 时现在抛TypeError此前行为不可预期。bpo-46232ssl模块现在能正确处理 DN 中包含bit string的证书。bpo-46521修复codeop模块把含字符串引号的非法代码误判为合法代码的 bug。4.4 扩展模块与 C 互操作bpo-46323多组ctypes回调函数现在在栈上而非堆上分配内存来传递参数并使用PyObject_Vectorcall调用回调显著降低回调开销作者 Donghee Nabpo-46323ctypes.CFUNCTYPE()与ctypes.WINFUNCTYPE()改为在创建类型时就校验_argtypes_参数数量上限此前要等到实际调用才报错bpo-45898ctypes不再在cfield.c中自行定义ffi_type_*符号libffi 已提供十余年bpo-40479在 Modules/_hashopenssl.c 中补上缺失的va_end()调用bpo-44953operator.itemgetter/operator.attrgetter对象改用vectorcall 调用约定调用更快。4.5 数据与文本相关库bpo-45863tarfile生成 pax 格式归档时除保留 pax 扩展头中的完整精度时间戳外还会在ustar 头写入整型时间戳兼容老式解包工具bpo-44289修复tarfile.is_tarfile(fileobj...)推进了fileobj内部位置、导致其无法再被TarFile.open读取的问题bpo-46848mmap.find/mmap.rfind复用bytes.find/bytes.rfind的优化字符串搜索实现bpo-25707修复xml.etree.ElementTree.iterparse在迭代器未被耗尽时的文件句柄泄漏bpo-46786xml.etree.ElementTree的 HTML 序列化按 HTML5 规范把embed/source/track/wbr输出为空标签bpo-45948修复xml.etree.ElementTree.XMLParser的 C 实现与 Python 实现不一致——传入targetNone时 C 实现现在同样回退到默认TreeBuilderbpo-46400 / bpo-46794expat 升级libexpat 从 2.4.1 升级到 2.4.4并在后续条目中将随附版本提升至 2.4.6同时修复嵌入式场景下 libexpat 符号与用户动态/静态链接版本符号冲突bpo-46784、并让测试套件兼容 Expat ≥ 2.4.5bpo-46811。4.6 其它行为变更bpo-46737random.gauss与random.normalvariate增加默认参数调用更简便bpo-14156argparse.FileType支持以-参数配合二进制模式使用返回sys.stdin/sys.stdout的.bufferx、a在参数为-时与w等价处理bpo-46736http.server.SimpleHTTPRequestHandler输出的目录页改用HTML5 语法bpo-39327shutil.rmtree现在可以正确处理VirtualBox 共享文件夹客户机视角bpo-46659两组calendar.LocaleTextCalendar/LocaleHTMLCalendar改用locale.getlocale同时弃用locale.getdefaultlocale计划 3.13 移除建议改用setlocale/getpreferredencoding(False)/getlocale组合bpo-46626socket模块暴露 Linux 的IP_BIND_ADDRESS_NO_PORT选项bpo-46522让各模块__getattr__抛出的AttributeError更贴近常规AttributeError形态bpo-46556弃用把pathlib.Path对象当上下文管理器使用的未文档化行为bpo-46877在unittest中导出doModuleCleanupsbpo-46246为importlib.metadata.DeprecatedList补上__slots__bpo-31369将re.RegexFlag加入re.__all__并补文档新增RegexFlag.NOFLAG表示无任何旗标bpo-41086configparser.ConfigParser构造时若interpolation参数不是configparser.Interpolation类型则抛TypeErrorbpo-29418实现inspect.ismethodwrapper并修复传入 methodwrapper 时inspect.isroutine的判断。五、构建系统C11/C99 硬门槛与平台适配bpo-46656编译器要求升级构建 Python 现在要求 C11 编译器不强制可选 C11 特性同时要求平台支持浮点 NaN移除Py_NO_NAN宏bpo-46640要求提供带NAN常量的 C99math.h或支持__builtin_nan()bpo-46860在大小写不敏感的文件系统上构建时尊重--with-suffix选项bpo-40280修复 wasm32-emscripten 平台的多处测试失败与平台问题——禁用不支持的 syscallwait/getrusage/prlimit/mkfifo/mknod/setres[gu]id/setgroups、以fd_count统计打开的文件描述符、补充 subprocess 与 fork 检查、绕过缺失的_multiprocessing与失败的socket.accept()、启用 bzip2、禁用大文件支持与signal.alarm。六、C API 与头文件整理bpo-46836把PyFrameObject的类型定义struct _frame从公共头移入内部 C API头文件Include/internal/pycore_frame.h——外部扩展不再能触碰帧结构内部布局当前仓库中struct _frame仍定义于 Include/internal/pycore_frame.hbpo-45459将Include/buffer.h重命名为Include/pybuffer.h避免与项目自带的buffer.h冲突bpo-45412移除HAVE_PY_SET_53BIT_PRECISION宏移入内部 C APIbpo-46748Python 公共头文件不再引入stdbool.h让嵌入/扩展 Python 的代码可自行定义bool/true/falsebpo-46613新增PyType_GetModuleByDef()——当某方法的定义类不可用时如被继承仍可借此访问所属模块状态。七、测试、文档与平台专项Testsbpo-46913 修复 UBSAN 构建下test_faulthandler.test_sigfpe()对faulthandler_sigfpe()关闭 UBSANbpo-46760 从test.test_dis期望值中移除字节码偏移量降低后续改动 VM/编译器的成本bpo-46708 避免test_asyncio运行后修改默认事件循环策略引发告警bpo-46678 让make_legacy_pycLib/test/support/import_helper.py在PYTHONPYCACHEPREFIX指向不同设备的目录时不再失败bpo-46623 在 s390x 上跳过使用硬件加速器 zlib 时失败的test_zlib用例。Documentationbpo-42238 将Doc/tools/rstlint.py迁移到独立仓库并以sphinx-lint发布到 PyPI。Windowsbpo-46744 修正 ARM64 的默认全用户安装目录为原生Program Files而非面向 32 位 ARM 的Program Files (Arm)bpo-46567 为 Windows ARM64 添加Tcl/Tk 与 IDLE支持bpo-46638 统一关闭 3.11 及以后的注册表虚拟化。IDLEbpo-46630 让 Windows 上的查询对话框在输入框内自动聚焦光标bpo-45447 为.pyi文件应用 IDLE 语法高亮。八、结语一条通向更快更稳的 3.11 主线从 Misc/NEWS.d/3.11.0a6.rst 这一份碎片记录可以清晰看到 3.11 的两条主线更快通过CACHE指令把缓存内联进字节码、为高频指令铺开自适应特化与组合指令LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE、以PUSH_NULL/统一PRECALL重塑调用序列、用 quickenedRESUME缩短冷启动路径并对纯字符串键字典去哈希化352→272 字节——这些机制虽在后续版本中继续演进但其骨架CACHE、specialize.c中的_Py_Specialize_*一族至今仍是当前解释器的核心组成可在 Python/bytecodes.c 与 Python/specialize.c 中继续追踪。更稳一方面修复了模块子类崩溃、集合变更崩溃、f-string 断言、认证前缀绕过等一批真实缺陷另一方面把typing.Self/Never/assert_never、asyncio.TaskGroup等开发体验与安全能力落地并同步收紧构建与 C API 边界C11 门槛、struct _frame内移、pybuffer.h改名。对希望深入 3.11 特化与内联缓存细节的读者建议沿三条路径继续阅读Python/specialize.c特化函数的实现与失败原因分类、Python/bytecodes.c含CACHE的 Tier-1 指令定义、以及 3.11 正式的 Whats New 文档 Doc/whatsnew/3.11.rst可获得本文件之外更完整的特性叙述。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考