Python 3.15 预览版 JIT:别只看开关,要会测热路径

2026-07-01 20 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

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 -hpython3.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 最合理的引入方式是“可开关、可回滚、可量化”:

  1. 在本地或性能测试环境安装支持 JIT 的 3.15 预览构建。
  2. 用 profiler 找出真实热点,而不是凭经验猜。
  3. PYTHON_JIT=0/1 做 A/B 基准测试。
  4. 记录吞吐、延迟、CPU、内存和尾延迟,而不只看平均耗时。
  5. 在 CI 或性能流水线里保留基准脚本,避免升级解释器后性能悄悄回退。

如果你的服务以 CPU 密集型 Python 逻辑为主,3.15 预览版 JIT 值得尽早实验;如果你的瓶颈在数据库、网络或 C 扩展,JIT 可能只是锦上添花。正确姿势是让数据决定开关,而不是让新特性决定架构。


相关推荐