Python 3.15 预览版继续把 JIT 编译器推向更实用的方向:不只是“能打开”,而是围绕热点代码、优化器升级和基准测试形成一套可验证的工作流。对日常写 Python 的开发者来说,关键问题不是“JIT 会不会让所有代码变快”,而是“我的这段代码有没有被 JIT 命中,收益是否大于额外复杂度”。
下面用一个可复制的小实验,把启用、确认、基准测试和判断边界串起来。
JIT 在 Python 3.15 里解决的不是所有慢
JIT,或者即时编译,通常会盯住反复执行的热点路径,把解释执行中的一部分工作转成更直接的机器码执行。Python 3.15 预览版的重点在于升级这条链路里的优化器,让符合条件的代码更容易从热点识别和优化中受益。
但它不是万能按钮:
- I/O 密集型代码,例如网络请求、数据库访问、文件读写,通常瓶颈不在解释器。
- 大量调用 C 扩展的代码,例如 NumPy 里的向量化计算,主要时间可能已经在 C 层。
- 一次性脚本、短生命周期 CLI,可能还没“热”起来就结束了。
- 动态特性极重的代码,例如频繁改变对象结构、猴子补丁、复杂反射,可能让优化器更难稳定工作。
因此,打开 JIT 之前先找热点;打开之后用基准测试说话。
先确认你的解释器真的支持 JIT
预览版和不同构建方式可能暴露不同能力。可以先用下面的命令检查当前 Python 是否带有 JIT 相关接口。把 python3.15 替换成你的实际可执行文件名。
python3.15 -VV
python3.15 -c "import sys; print('has sys._jit:', hasattr(sys, '_jit')); print(dir(sys._jit) if hasattr(sys, '_jit') else 'JIT API not available in this build')"
如果构建支持 JIT,通常还可以检查可用状态和启用状态。下面这段代码做了防御式处理:即使当前构建没有 sys._jit,也不会报错。
python3.15 - <<'PY'
import sys
jit = getattr(sys, "_jit", None)
if jit is None:
print("This Python build does not expose sys._jit")
else:
for name in ("is_available", "is_enabled", "is_active"):
fn = getattr(jit, name, None)
if callable(fn):
print(f"{name}: {fn()}")
else:
print(f"{name}: not exposed")
PY
如果你的预览版支持通过环境变量控制 JIT,可以这样实践:
PYTHON_JIT=0 python3.15 -c "import sys; print(getattr(getattr(sys, '_jit', None), 'is_enabled', lambda: 'n/a')())"
PYTHON_JIT=1 python3.15 -c "import sys; print(getattr(getattr(sys, '_jit', None), 'is_enabled', lambda: 'n/a')())"
注意:具体开关名称和可用性取决于你安装的 3.15 预览构建。遇到不一致时,以本地 python3.15 -h、python3.15 -X help 或构建说明为准。
用 pyperf 测,而不是用感觉测
JIT 的收益往往体现在“重复执行的纯 Python 热路径”。下面准备一个简单的素数计数函数,刻意让它有循环、分支和整数运算,便于观察解释器层面的变化。
新建 bench_jit.py:
# bench_jit.py
from __future__ import annotations
def count_primes(limit: int) -> int:
count = 0
for n in range(2, limit):
is_prime = True
d = 2
while d * d <= n:
if n % d == 0:
is_prime = False
break
d += 1
if is_prime:
count += 1
return count
if __name__ == "__main__":
print(count_primes(20_000))
先确认脚本能跑:
python3.15 bench_jit.py
安装 pyperf 并分别测试关闭与开启 JIT 的结果:
python3.15 -m pip install pyperf
PYTHON_JIT=0 python3.15 -m pyperf timeit \
-s "from bench_jit import count_primes" \
"count_primes(20_000)" \
-o no_jit.json
PYTHON_JIT=1 python3.15 -m pyperf timeit \
-s "from bench_jit import count_primes" \
"count_primes(20_000)" \
-o with_jit.json
python3.15 -m pyperf compare_to no_jit.json with_jit.json
这组命令有几个好处:
pyperf会做多轮运行,减少单次波动。- 结果保存为 JSON,方便在 CI 或不同机器上比较。
compare_to会直接告诉你差异是否明显,而不是只给一堆裸数字。
如果你想看更接近业务的结果,把 count_primes(20_000) 换成自己的热点函数调用。不要拿启动时间、一次性导入、网络请求去代表 JIT 效果,那样很容易测错。
优化器升级意味着什么
Python 3.15 预览版 JIT 的“升级”更值得关注的是优化器,而不是单纯多了一个开关。一个实用的理解方式是:
- 解释器先观察代码运行情况;
- 找到足够热、足够稳定的路径;
- 优化器尝试减少动态语言带来的重复判断和调度成本;
- JIT 对合适路径生成更快的执行形式。
这也是为什么同一项目里,有的函数明显受益,有的函数几乎没变化。优化器通常喜欢稳定的类型、紧凑的循环、可预测的分支;它不喜欢到处变化的对象形状和难以推断的动态行为。
可以用一个简单清单筛选候选代码:
- 这个函数是否在一次进程生命周期内执行很多次?
- CPU profile 里它是否排名靠前?
- 它是否主要由 Python 循环、整数/字符串/容器操作组成?
- 输入类型是否相对稳定?
- 它是否很少被 I/O、锁等待或外部服务调用支配?
满足越多,越值得拿 JIT 试。
落地建议:把 JIT 当成性能实验,而不是默认信仰
对团队来说,Python 3.15 JIT 最合理的引入方式是“可开关、可回滚、可量化”:
- 在本地或性能测试环境安装支持 JIT 的 3.15 预览构建。
- 用 profiler 找出真实热点,而不是凭经验猜。
- 用
PYTHON_JIT=0/1做 A/B 基准测试。 - 记录吞吐、延迟、CPU、内存和尾延迟,而不只看平均耗时。
- 在 CI 或性能流水线里保留基准脚本,避免升级解释器后性能悄悄回退。
如果你的服务以 CPU 密集型 Python 逻辑为主,3.15 预览版 JIT 值得尽早实验;如果你的瓶颈在数据库、网络或 C 扩展,JIT 可能只是锦上添花。正确姿势是让数据决定开关,而不是让新特性决定架构。