CPython以指令特化替代通用JIT 借内联缓存消除字典查找开销

【python】Faster CPython的重要力量——Specialized Instruction — 码农高天

码农高天

Faster CPython团队基于真实运行环境动态性极低的特征,采用单指令特化与内联缓存替代通用JIT,以最小代价消除全局变量重复哈希寻址的性能损耗。

AI-assisted summary of 【python】Faster CPython的重要力量——Specialized Instruction — original source. Check the original for full context.

Source published:

Key ideas

  • CPython 3.11 在推进 Faster CPython 项目时未直接引入通用 JIT,而是选择了单字节码粒度的特化指令(Specialized Instruction)作为折中加速方案。 (0:00)
  • 传统 LOAD_GLOBAL 指令每次执行均需经历类型校验、哈希计算与冲突遍历,且在未命中 globals 时还要在 builtins 中完整重查一遍,存在严重的重复计算开销。 (1:45)
  • 特化机制通过内联缓存(Inline Cache)保存字典的槽位索引与版本号,使字节码特化为 LOAD_GLOBAL_MODULE 或 LOAD_GLOBAL_BUILTIN 后可直接按偏移读取变量值。 (5:00)
  • 在 Python 中可通过 dis.dis(..., adaptive=True) 观察指令特化过程,热点函数预热多次后即可见原版指令被就地替换为特化指令。 (5:00)

Detailed notes

## Background 该内容由码农高天于 2024 年 6 月 29 日发布在 YouTube 平台上。视频简介指出,本期主要探讨自 Python 3.11 引入 CPython 的一项全新加速机制——Specialized Instruction(特化指令),解析它是如何让 CPython 实现性能提升的。

## TL;DR Faster CPython 团队并未直接采用冷启动慢且难以精细控制的通用 JIT 编译器,而是选择了细化到单个字节码的务实折中方案——Specialized Instruction。原版的 LOAD_GLOBAL 指令在执行时每次都需重复进行哈希计算、冲突遍历以及在 globals 和 builtins 之间的退避查找,存在严重的算力冗余。针对热点代码,CPython 通过执行计数将指令就地特化为 LOAD_GLOBAL_MODULE 等形态,利用内联缓存(Inline Cache)记录索引下标与版本号,在绝大多数无需重哈希的场景下跳过昂贵查找直接取值,并在结构变动时安全去优化回退。这一套机制实质上是代价更小、更易验证正确性的轻量级 JIT 实践,极大补偿了 Python 过于动态带来的性能损耗。

## Atmosphere & style 视频风格硬核严谨却通俗易懂,展现出一种庖丁解牛般的工程教学美感。高天以从容平缓的语调层层拆解底层机制,从字节码的执行细节直击字典哈希查找的性能死穴,讲授思路极具说服力。整期内容既有底层探秘的深度,又带有生动务实的调侃(如将其戏称为“穷人版 JIT”),带给观众一种拨开技术黑箱、顿悟语言运行本质的清晰与踏实感。

## Bulletin - [00:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=0s) CPython 3.11 在推进 Faster CPython 项目时未直接引入通用 JIT,而是选择了单字节码粒度的特化指令(Specialized Instruction)作为折中加速方案。 - [01:45](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=105s) 传统 LOAD_GLOBAL 指令每次执行均需经历类型校验、哈希计算与冲突遍历,且在未命中 globals 时还要在 builtins 中完整重查一遍,存在严重的重复计算开销。 - [05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) 特化机制通过内联缓存(Inline Cache)保存字典的槽位索引与版本号,使字节码特化为 LOAD_GLOBAL_MODULE 或 LOAD_GLOBAL_BUILTIN 后可直接按偏移读取变量值。 - [05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) 在 Python 中可通过 dis.dis(..., adaptive=True) 观察指令特化过程,热点函数预热多次后即可见原版指令被就地替换为特化指令。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=0s) 传统 JIT 编译器在运行时将热点代码编译为机器码,但在冷启动首次编译慢、黑箱难以控制及极端全局优化受限等方面存在劣势。 - [00:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=0s) Faster CPython 放弃大块代码重编的通用 JIT,转而采用代价更小、更易验证的单字节码特化方案(Specialized Instruction)。 - [01:45](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=105s) 原版 LOAD_GLOBAL 字节码在加载变量时,需要依次确认字典类型、在 globals 中计算哈希及遍历冲突槽位,未找到时再退到 builtins 重复全套流程。 - [01:45](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=105s) 真实代码中变量归属(globals 或 builtins)高度稳定,且字典未扩容时底层槽位索引完全固定,重复求哈希与碰撞比对造成极大算力浪费。 - [05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) 指令执行达到一定次数后被识别为热点,LOAD_GLOBAL 会特化为 LOAD_GLOBAL_MODULE 或 LOAD_GLOBAL_BUILTIN。 - [05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) 内联缓存(Inline Cache)保存了底层槽位 index 与 globals 字典的 version 版本号,实现快速路径下的常数级内存直读。 - [05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) 若运行时环境改变(如 globals 结构变更或不再是 dict),特化指令会去优化(deoptimize)回退到原版指令以确保安全性。 - [05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) 使用 dis.dis 配合 adaptive=True 参数,可以直观检验函数在经过循环预热前后字节码从原版变为特化版本的变化。 - [05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) Specialized Instruction 本质上是一个“穷人版 JIT”,它基于真实热点代码在运行时并未那么动态的核心假设,务实弥补了 Python 的动态性开销。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=0s) Specialized Instruction:CPython 引入的折中 JIT 方案

在讨论 Python 性能提升时,大家最熟悉的概念往往是 JIT(Just-In-Time 编译器)。JIT 的核心理念是在运行时寻找“热点代码”,并根据该热点代码的实际运行情况,动态编译出执行效率更高的本地机器码。以动态语言中的加法操作为例,常规流程需要先检查变量类型,再根据类型找到对应的加法函数进行计算;但如果编译器通过监测发现该位置永远是两个整数相加,就会重新编译这段代码,仅做一个快速类型判断后直接调用原生底层的整数加法,从而带来巨大的速度提升。

然而,在推进 Faster CPython 项目时,官方团队并没有直接引入市面上现有的通用 JIT 编译器。码农高天指出:“为什么不直接用现在市面上已经有的JIP compiler呢他有很多原因比如现在很多JIT compiler它背后是一个黑箱你相对来说不太好控制他的首次编译速度会非常慢你更难进行绝对意义上的优化反正种种原因吧 导致Faster CPython的团队并没有直接去用现在市面上的JIT compiler他们选择的是一个比较折中的方法也就是specialized instruction”。通用的 JIT 通常会对一整块大型代码进行编译优化,而 Faster CPython 的折中选择则是将粒度细化到单个字节码(bytecode instruction),对其执行特化优化。

```mermaid flowchart TD accTitle: CPython 原版 LOAD_GLOBAL 字典查找流程 accDescr: 展示 Python 解释器在执行 LOAD_GLOBAL 时,分别在 globals 和 builtins 字典中进行哈希计算与冲突解决的完整流程。

Start["执行 LOAD_GLOBAL 字节码"] --> GetName["获取变量名(如 'math' 字符串)"] GetName --> CheckDict["确认 globals 和 builtins 均为字典"]

subgraph GlobalsSearch ["步骤 1:查找 globals 字典"] HashGlobal["计算 key 的哈希值"] --> LoopGlobal["遍历解决哈希冲突并比对 key"] LoopGlobal --> FoundGlobal{"在 globals 中找到?"} end

CheckDict --> GlobalsSearch FoundGlobal -- 是 --> FetchVal["取出对应 index 的 value 并返回"]

subgraph BuiltinsSearch ["步骤 2:退避查找 builtins 字典"] HashBuiltin["重新计算 key 的哈希值"] --> LoopBuiltin["遍历查找 builtins 字典"] LoopBuiltin --> FoundBuiltin["命中 builtins 返回值(如 print/min/max)"] end

FoundGlobal -- 否 --> BuiltinsSearch ```

- **JIT 的局限性**:黑箱不易掌控、冷启动首次编译开销巨大、在全局优化上难以兼顾极端情况。 - **Specialized Instruction 的核心思路**:不追求整个代码块的大面积重编,而是针对单个字节码指令的类型假定进行就地特化。

---

[01:45](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=105s) 原版 LOAD_GLOBAL 字节码的性能瓶颈

为了说明特化的必要性,高天以全局变量加载指令 `LOAD_GLOBAL`(例如调用 `math.sqrt` 时加载全局模块 `math`)为例剖析底层的开销。表面上看只是读取一个全局变量,但解释器每次执行原版的 `LOAD_GLOBAL` 指令时,底层都有一长串复杂的步骤:

1. **获取变量名**:从数组中取出目标字符串(如 `"math"`),此步骤为数组访问,若 CPU 缓存命中(cache hit)耗时并不算慢。 2. **确认字典类型**:检查当前执行环境的 `globals` 与 `builtins` 确实是 `dict` 类型。 3. **在 `globals` 字典中查找**:先计算 key 的 hash 值;若为 Unicode 字符串,接着进入字典底层的核心查找函数。该函数内部包含一个处理哈希冲突(hash conflict)的 `for` 循环,需逐一比对哈希值和键值内容,最终确定该 key 在字典底层存储中的具体索引下标(index),再取出对应的 value。 4. **回退查找 `builtins` 字典**:如果在 `globals` 中未找到(例如 `print`、`max`、`min` 等内置函数),则必须退到 `builtins` 字典中将上述计算流程完整重做一遍。

高天强调,分析到这里就会发现传统机制存在严重的冗余算力浪费:第一,对于某一条特定的字节码指令,它所要读取的目标变量要么几乎永远存在于 `globals` 中,要么永远在 `builtins` 中,归属位置是高度稳定的;第二,在字典结构没有新增 key 导致扩容或重新哈希的情况下,目标 key 对应的内部存储 index 是完全固定的。即便变量绑定的 value 发生了改变,其槽位 index 依旧不变。这意味着原版字节码在每一次执行时,都在反复进行昂贵的求 hash、比对碰撞和计算 index 的操作。

---

[05:00](https://www.youtube.com/watch?v=SNXZPZA8PY8&t=300s) 内联缓存与特化加速机制

针对上述痛点,Specialized Instruction 引入了执行计数机制与内联缓存(Inline Cache)。当某条 `LOAD_GLOBAL` 指令被连续执行了若干次之后,解释器便会认定其为热点并触发特化(specialize)。对于 `LOAD_GLOBAL`,解释器提供了两种特化形态:针对全局变量/模块的 `LOAD_GLOBAL_MODULE`,以及针对内置函数的 `LOAD_GLOBAL_BUILTIN`。

```mermaid flowchart TD accTitle: LOAD_GLOBAL_MODULE 特化指令执行与去优化回退机制 accDescr: 展示特化后的字节码如何利用内联缓存中的 index 和 version 实现快速取值,以及在版本失效时的去优化回退。

RunSpec["执行 LOAD_GLOBAL_MODULE"] --> ReadCache["读取 Inline Cache:获取 (index, version)"] ReadCache --> Validate{"校验有效性:<br>globals 仍是 dict 且 version 未改变?"}

Validate -- 校验成功(极大概率命中) --> FastPath["快速路径:直接按 index 偏移量读取 value"] Validate -- 校验失效(字典结构改变) --> Deopt["去优化(Deoptimize):<br>退回原版 LOAD_GLOBAL 重新完整查找"] ```

在指令特化为 `LOAD_GLOBAL_MODULE` 后,后续执行时不再重复计算哈希与遍历槽位,而是直接从该指令自带的 Inline Cache 中取出先前计算并缓存的 `index` 以及一个 `version`(版本号): - **`index`**:保存先前哈希计算所得的字典底层槽位位置,以此直接快速定位内存并拿到 `value`。 - **`version`**:用于快速比对 `globals` 字典的 keys 是否发生过结构变化。

如果运行时发现 `globals` 不再是字典或者字典的版本号改变,解释器会去优化(deoptimize)回退到原版的 `LOAD_GLOBAL` 进行保守查找;若版本未变,则直接依靠 `index` 取值。因为程序热点路径中全局命名空间通常极少动态增删键,所以内联缓存的命中率极高,使得运行速度大幅超越原版查找。

要在实际代码中观察特化效果,可以使用 Python 的 `dis` 模块。通过调用 `dis.dis(..., adaptive=True)` 参数,解释器就会打印出特化后的实际指令。例如针对一个包含 `math.sqrt` 调用的函数 `f`,在初始编译状态下反编译查看,字节码显示为原版的 `LOAD_GLOBAL`;但在循环执行该函数 10 次使其充分“预热”后,再次反编译即可看到该指令已经被就地替换为了 `LOAD_GLOBAL_MODULE`。

高天总结道,Faster CPython 团队的工作本质上是在对 Python 过于动态的语言特性进行一种务实的补偿。它基于一个核心假设:绝大部分真实世界的热点代码,在运行阶段其实并没有开发者想象的那么动态。“这一套内容呢,基本上就是一个穷人版的JIT或者说是一个更好理解的代价更小的更容易证明是正确的GIT”,理解了这套基于字节码特化的机制,对于后续进一步理解更为复杂的 JIT 编译原理也是极佳的铺垫。

## Mentioned 码农高天, Python, CPython, Faster CPython, JIT, Specialized Instruction, LOAD_GLOBAL, LOAD_GLOBAL_MODULE, LOAD_GLOBAL_BUILTIN, Inline Cache, dis, math

## Video description 这期视频我们聊一下从3.11引入CPython的一个新机制——specialized instruction。它是如何加速CPython的呢?

[Source](https://www.youtube.com/watch?v=SNXZPZA8PY8)

中文简报

CPython以指令特化替代通用JIT 借内联缓存消除字典查找开销 Faster CPython团队基于真实运行环境动态性极低的特征,采用单指令特化与内联缓存替代通用JIT,以最小代价消除全局变量重复哈希寻址的性能损耗。 Faster CPython 团队并未直接采用冷启动慢且难以精细控制的通用 JIT 编译器,而是选择了细化到单个字节码的务实折中方案——特化指令(Specialized Instruction)。原版的 LOAD_GLOBAL 指令在执行时每次都需重复进行哈希计算、冲突遍历以及在 globals 和 builtins 之间的退避查找,存在严重的算力冗余。针对热点代码,CPython 通过执行计数将指令就地特化为 LOAD_GLOBAL_MODULE 等形态,利用内联缓存(Inline Cache)记录索引下标与版本号,在绝大多数无需重哈希的场景下跳过昂贵查找直接取值,并在结构变动时安全去优化回退。这一套机制实质上是代价更小、更易验证正确性的轻量级 JIT 实践,极大补偿了 Python 过于动态带来的性能损耗。 CPython 3.11 在推进 Faster CPython 项目时未直接引入通用 JIT,而是选择了单字节码粒度的特化指令(Specialized Instruction)作为折中加速方案。 (0:00) 传统 LOAD_GLOBAL 指令每次执行均需经历类型校验、哈希计算与冲突遍历,且在未命中 globals 时还要在 builtins 中完整重查一遍,存在严重的重复计算开销。 (1:45) 特化机制通过内联缓存(Inline Cache)保存字典的槽位索引与版本号,使字节码特化为 LOAD_GLOBAL_MODULE 或 LOAD_GLOBAL_BUILTIN 后可直接按偏移读取变量值。 (5:00) 在 Python 中可通过 dis.dis(..., adaptive=True) 观察指令特化过程,热点函数预热多次后即可见原版指令被就地替换为特化指令。 (5:00)

Reading summary

Faster CPython 团队在推进性能优化时并未直接引入通用的 JIT 编译器,而是选择了细化到单个字节码的指令特化(Specialized Instruction)方案。码农高天指出,该机制通过在运行时利用内联缓存跳过昂贵的冗余动态查找,以更可控、更低代价的折中手段实现了对 Python 动态特性的性能补偿。 绕开通用 JIT 的折中技术选型 (0:00) 在动态语言性能优化中,JIT 编译器的常规思路是在运行时监测热点代码,并根据实际变量类型动态编译生成高效的底层机器码。但码农高天指出,通用 JIT 编译器存在黑箱难以掌控、首次冷启动编译开销极大以及难以进行绝对意义优化等缺陷。通用 JIT 往往需要对大块代码进行编译重构,而 Faster CPython 团队选择了一条折中路线,将优化粒度收缩到单个字节码指令,根据运行时假定对其进行就地特化。 原版 LOAD_GLOBAL 的底层性能瓶颈 (1:45) 以读取全局模块(如 math.sqrt 中的 math)的 LOAD_GLOBAL 指令为例,原版解释器每次执行该指令时都面临冗长的查找开销:首先从数组取出变量名字符串,确认 globals 与 builtins 为字典,接着计算 key 的哈希值并进入解决哈希冲突的循环以确定内部索引,若 globals 中未找到还需退避到 builtins 字典重做整套哈希比对流程。 高天强调,这类传统机制存在严重的算力浪费。在实际运行中,特定指令的目标变量归属(globals 或 builtins)高度稳定;只要字典没有因新增 key 导致扩容重哈希,目标槽位的内部存储 index 是完全固定的。即便绑定的 value 发生改变,index 也保持不变,原版指令每次重复求 hash 和处理冲突属于无谓消耗。 内联缓存与去优化回退逻辑 (5:00) 为消除冗余开销,Specialized Instruction 引入了执行计数机制与内联缓存(Inline Cache)。当一条指令被连续执行若干次判定为热点后,解释器会将其就地特化为 LOAD_GLOBAL_MODULE 或 LOAD_GLOBAL_BUILTIN。特化指令会携带包含 index 和 version 的内联缓存,后续执行时直接校验 globals 的类型与字典版本号,校验通过即可按 index 偏移直接取值,无需再算哈希和遍历。 若运行时字典结构发生变动导致 version 失效,解释器会执行去优化(Deoptimize)回退到原版指令重新保守查找。开发者可以通过 dis 模块并传入 adaptive=True 参数来观察这一现象:例如包含 math.sqrt 调用的函数在初始时显示为原版 LOAD_GLOBAL,在循环执行 10 次充分预热后,反编译结果便会显示为特化后的 LOAD_GLOBAL_MODULE。 高天总结认为,指令特化本质上是针对 Python 过于动态的语言特性的一种务实补偿,其核心假设在于绝大多数真实热点代码在运行阶段并不如预期那般动态。这一机制可以被视为一种代价更小、更易理解且更容易证明正确性的折中方案,同时也为深入理解更复杂的 JIT 编译器原理提供了基础铺垫。

Original transcript

[0:00] 今天我们来聊一下Python解释器的specializing这个Specializing是Faster CPython这个project里面非常重要的一部分它的目的就是提升Cpython的性能那在specializing这个领域大家更熟悉的概念可能是jit啊JIT就是just in time这些的核心理念就是找到热点代码然后根据这段热点代码的运行情况编译出一个运行效率更高的代码比如说我们举一个特别简单的例子 有一个加法可能经常被执行在动态类型的语言里在进行加法之前我们首先要检查这个变量的类型然后根据类型可能要找他如何进行加法再根据这个做加法的函数就把这两个数加起来但是这个这个编辑器可能会发现哎你这个加法呀永远是两个整数相加那他可能就会把这个加法重新编译首先判断一下是不是整数如果是整数直接用他新编译的native的加法方法这样的话你在做整数加法的时候就快了非常多这里举的只是一个非常非常简单的例子啊实际情况比这个复杂的多那Python这次在解释器里面加入的这个specializing是一个盖版的JIT为什么不直接用现在市面上已经有的JIP compiler呢他有很多原因比如现在很多JIT compiler它背后是一个黑箱你相对来说不太好控制他的首次编译速度会非常慢你更难进行绝对意义上的优化反正种种原因吧 导致Faster CPython的团队并没有直接去用现在市面上的JIT compiler他们选择的是一个比较折中的方法也就是specialized instruction什么叫specialized instruction呢就是它不对大型的代码块进行直接的GIT优化我们刚才说是一个加法对吧但是更多的情况下呢是一块代码它整个进行编译上的优化那Faster C Python团队选择的是对单个的byte code也就是instruction进行优化我们依然是给大家介绍一个简单的例子比如说这里有一个load globalload global 就是读取全局变量这里呢他就是读取Math这个全局变量它对应的是第4行这个代码啊我们在做math del square root的时候读取的这个全局变量那我们不难想象在我们每一次进到这个函数的时候实际上这个load global的结果都是一样的或者说起码他读取的永远是global这个diction里面math这个key对应的值就这个我们高亮的instruction实际上他每一次读的key都是一样的那我们就来看一下load global这个instruction每次运行的时候要干什么中间这个部分就是specialization的代码啊我们先不管它它是因为我们要做specialization才引入的首先他要拿到这个name 也就是math这个string这个操作就是一个array access如果cache hit了应该不算慢接下来确认一下global跟builtins都是dict 这件事也不慢然后要做一下load global 我们看看load global需要干什么首先对这个key取一下hash如果这个key是一个unicode也就是一个string的话这个步骤也不算太慢接下来在global这个字典里查找这个key这个key如果是unicode的话就运行这一行代码大家看这个函数就是最重要运行的代码他先拿了很多东西做了一些计算然后这里有一个for loop这个loop呢是处理hash conflict的也就是如果你有两个key是同一个hash的话他需要在这个hash里面一个一个去找每次找到一个可能的key他就要比较一下他们的hash然后比较一下这两个值是否相等从而找到这个key对应的真正的index如果没了着 接着往下找这里就涉及了dictionary具体的实现了我就不多说了大家大概知道是这么回事就行了拿到这个key之后找到这个key对应的value存起来这个就是自点查找的步骤如果在globals里面没有发现那就要去built-ins里面找比如说我们经常用到的print,max,min这些函数都是在built-ins这个字典里面的也就是说如果我们最终要找的这个全局变量不在global这个dictionary里面的话这个查找的过程我们需要进行两遍到这里我们意识到了什么事呢第一对于某一个instruction来说他读取的这个值是在global里面还是在builtins里面几乎永远会是一样的就我同一个instruction读一个全区变量它基本上要么永远这个全球变量在globals里面要么永远这个全球变量在built-ins里面这是第一点非常明显的第二点在进行自点查找的时候如果我的字典没有变或者说我字典的Kiss [5:00] 同时我要查找的这个key也没有变那实际上我对应的这个index是固定的就跟上次查找相比我这个字典没有加入新的key所以你同一个key对应的位置永远是一致的它的value也就是它的值有可能会产生变化但是这个值永远是在刚才的那个位置的这个字典不会随便变也就是说我们进行了大量的重复的根据一个key去找他index这个操作就刚才的什么求hash啊然后处理hashconflict呀这些事情在我们每一次做load global的时候,几乎都是重复的于是我们就可以想到用specialized instruction去解决这个问题我们来看回这段优化的代码这代码大概的意思就是如果这个 instruction被运行了若干次我就要对他进行specialize那现在对于load global 来说它有两个specialized的形式一个就是这个load global module还有一个这个Load Global Built-in那这两个形式都是非常非常常见的比如我们刚才在函数里的math实际上就是做了一个load global module我们看如果把这个load global变成了这个specialized的形式load global module之后再运行这个instruction会发生什么呢他会从这个 instruction 自带的 inline cache 里面读取到这个 index 和一个 version这个index就是我们刚刚提到的反复计算的那个index也就是说我把刚才那个最重要最费时的计算结果给保存下来了而version是什么呢?version就是我们来判断global是个dictionary的key有没有变化的这么一个版本我们可以看如果globals不是一个dictionary的或者globals的版本变化了就退回到原来的load global否则直接根据我们刚才缓存的那个index拿到这个value通过这个比较大家不能看出specialize之后的这个load global module是显然要比刚才那个load global快非常多的同时我们也不难想见这个case的hit率是非常高的就是满足我们这个假设的条件的比例我们刚才在这里提到过在我们load global math的时候永远是做一个load module的操作那怎么来观察一下我们这个Lode Global是不是被specialize了呢我们依然可以使用 this module 里面这个 this 函数只不过我们需要把这个adaptive给成true这样的话它就会显示specialize之后的instructions我们这里先看一下F字节码,然后运行十次F,再看一下F字节码。可以看到在运行之前这个F字节码是原版的load global而在运行完十字之后它就变成了load global module当然如果你观察一下这段代码里还有很多其他的字节码也被specialize了那这个呢就是现在FasterCpython团队在努力做的一个方向主要是对Python过于动态的特性进行一个补偿它假设你的热点代码里的大部分操作其实没有那么动态这一套内容呢,基本上就是一个穷人版的JIT或者说是一个更好理解的代价更小的更容易证明是正确的GIT可能你理解了Python的优化之后再去理解JIT会变得更轻松一些那它到底能给Python带来多大速度上提升我们就拭目以待吧好 那这视频就到这里 喜欢大家的点赞关注 别忘了一键三连哟优优独播剧场——YoYo Television Series Exclusive