page contents

AI 项目越来越离不开 Python,但底层性能怎么办?

Python 方便、表达力强,而且开发效率高。但 CPython 解释器并不是为了让每一种 CPU 密集型操作都达到优化后原生代码的执行速度。

attachments-2026-10-gPuo3Yt76ad2cf70c7b5b.png

Python 已经成为人工智能领域最重要的编程语言之一。

从模型训练、数据处理,到推理流程、AI agents、评估框架和研究工具,Python 几乎无处不在。

这就产生了一个有意思的工程问题:

如果 AI 系统越来越依赖 Python,那么当性能变得至关重要时,该怎么办?

Python 方便、表达力强,而且开发效率高。但 CPython 解释器并不是为了让每一种 CPU 密集型操作都达到优化后原生代码的执行速度。

那么,Python 如何继续处于现代 AI 系统的核心位置,同时又让这些系统获得足够高的性能?

答案是,现代 AI 软件很少只依赖 Python。

相反,整个行业已经逐渐形成了一种分层架构:

上层是 Python,中间是原生代码和编译器,底层则是专用硬件。

Python 通常是接口,而不是计算核心

很容易产生一种误解:只要一个程序是 Python 写的,就意味着 CPU 会为每一个耗时操作执行 Python bytecode。

大多数现代 AI 软件并不是这样工作的。

来看一个简单的 PyTorch 程序:

import torch

x = torch.randn(4096, 4096, device="cuda")
y = torch.randn(4096, 4096, device="cuda")

z = x @ y

这段代码是 Python,但矩阵乘法并不是通过 Python 循环实现的。

Python 层负责构造操作,并将其分发给底层数值计算系统。真正耗时的计算最终会通过优化后的原生实现和 GPU kernel 执行。

PyTorch 官方文档也明确描述了这种架构。它的 Python frontend 暴露了由大量 C++ 代码实现的功能,同时还提供 C++ API 和 C++ extensions,用于对性能敏感的应用。

这带来了一个重要的区别:

用于描述一个操作的语言,不一定就是实际执行这个操作的语言。

因此,一个 Python 程序的大部分执行时间完全可能发生在 Python interpreter 之外。

Python AI 背后的真正性能栈

一个现代 AI 应用可以看成由多个层次组成。

最上层是 Python:

AI application
      ↓
Python
      ↓
Python libraries
      ↓
C / C++
      ↓
CUDA / GPU kernels
      ↓
Compiler
      ↓
CPU / GPU / TPU

Python 提供高层编程模型。

C 和 C++ 提供重要底层组件的高效实现。

CUDA 和其他 accelerator programming systems 提供访问专用硬件的能力。

Compiler 针对特定处理器或 accelerator 对这些操作进行优化。

最终,硬件执行生成的 machine code。

这种架构让开发者可以继续使用 Python 的开发效率,同时不需要 Python 自己执行每一个底层计算。

PyTorch 是一个很好的例子

PyTorch 也许是这种设计理念最典型的例子之一。

它的 C++ API 包括 ATen、Autograd、C++ frontend、CUDA support 和 C++ extensions。项目将 C++ extensions 描述为一种使用 C++ 或 CUDA 实现 custom operators,并从 Python 中使用这些 operators 的方式。

例如,一个应用程序可能看起来是这样的:

result = my_custom_operator(x)

而底层实现可能是 C++:

Tensor my_custom_operator(const Tensor& input) {
    // Performance-critical implementation
}

或者,它最终也可能分发到运行在 GPU 上的 CUDA kernel。

对于 Python 开发者来说,这个操作看起来仍然只是一个普通的 Python function。

这种分离非常有用。

应用层可以保持简洁和灵活,而性能敏感的组件则可以使用更底层的技术实现。

Python 不需要和 C++ 竞争

这也解释了为什么简单地说“Python 比 C++ 慢”在 AI 开发中往往过于简单。

PyTorch 文档也说明了类似的情况:Python frontend 会调用 C++ 来执行计算密集型操作,尤其是数值计算操作。在很多 workload 中,这些操作占据了主要的执行时间。

想象两个程序。

第一个程序直接在 Python 中执行一千万次小操作:

for i in range(10_000_000):
    result += data[i]

解释器需要不断执行 Python 层面的操作。

第二个程序将整个计算交给一个优化后的原生实现:

result = optimized_operation(data)

第二个程序可能仍然几乎完全使用 Python 编写。

但真正耗时的工作是在其他地方完成的。

这正是 scientific Python 和 AI computing 背后的核心思想之一。

Python 提供控制层。原生代码执行繁重的计算。

跨越 Python 边界也有成本

不过,这里还有一个重要限制。

调用原生代码并不是完全没有成本的。

当 Python 调用 C++ function 时,参数可能需要进行转换,对象可能需要进行封装,reference count 可能需要更新,同时控制流也需要跨越 Python/native 边界。

pybind11 这样的项目,就是为了让这种互操作变得更加实用。pybind11 提供了将 C++ functions 和 data structures 暴露给 Python 的机制,同时减少 binding 所需要的样板代码。

但是,当 native operation 本身非常小时,这个边界的成本就可能变得明显。

例如:

for item in data:
    native_function(item)

如果 native_function() 只执行几条 CPU instructions,那么反复进入和离开 native layer 的成本,就可能成为总运行时间中相当大的一部分。

更好的架构可能是:

native_function(data)

然后让 native implementation 对整个 batch 进行处理。

这是一个重要的性能原则:

把耗时的工作以较大的批次完成,而不是为了很小的操作反复跨越语言边界。

GPU 又改变了这个问题

AI workload 让这种架构变得更加有意思,因为大量计算工作可以从 CPU 转移到 GPU。

Python 程序可能负责创建 tensors、构建 model,并启动 operations。

但 GPU 可以并行执行数十亿次 arithmetic operations。

Python interpreter 并不会逐个执行这数十亿次操作。

相反,Python 启动一个计算操作,然后底层 framework 将优化后的 kernels 调度到 accelerator 上。

这也是为什么即使 AI workload 需要巨大的计算吞吐量,Python 仍然可以发挥作用。

Python 层本身可能只执行相对少量的计算。

它的工作是描述、协调和启动计算。

Compiler 可以把更多工作从 Python 中移出去

下一步就是 compilation。

与其独立执行每一个高层操作,compiler 可以观察更大范围的计算,然后将整个计算作为一个整体进行优化。

JAX 就是一个很好的例子。

它的 jax.jit() transformation 会对 Python function 进行 tracing,并将得到的计算发送给 XLA。然后 compiler 可以进行 operation fusion、消除临时数组,并针对特定的 CPU、GPU 或 TPU 对计算进行优化。

例如:

@jax.jit
def compute(x, y):
    return x * y + 1

Python function 仍然是开发者编写的接口。

但经过 compilation 之后,实际执行路径可能与反复解释单个 Python operation 有很大不同。

Compiler 可以看到更多的计算过程。

这让它有机会进行一些优化,而当每一个 operation 都被独立处理时,这些优化往往很难实现。

Python 自身也在变得更快

还有另一个方向值得关注。

Python runtime 本身也在发生变化。

CPython 一直在推进多项性能改进,包括 specializing adaptive interpreter、free-threading,以及新的 experimental JIT compiler。

Python 3.13 引入了对 free-threaded builds 的实验性支持,可以禁用 GIL,让 Python threads 在多个 CPU cores 上并行执行。不过,free-threaded build 会带来可测量的单线程性能成本,这主要与移除 GIL 后增加的同步和内存管理开销有关。这部分开销一直在通过持续优化降低,但具体影响取决于 workload。

Python 3.14 也在官方的 macOS 和 Windows binaries 中加入了 experimental JIT compiler。

这个 JIT 目前仍处于早期阶段。Python 官方文档指出,启用 JIT 的典型性能影响大约从 10% slower 到 20% faster,具体取决于 workload。一些循环密集型数值任务可以获得接近 20% 的提升,例如 n-body benchmark;但也有不少场景几乎没有收益,甚至会出现性能下降。

这一点很重要,因为它展示了 Python 正在尝试改善自身执行性能,但目前的 JIT 还远没有达到“普遍加速”的程度。

目标并不是简单地让每一个 Python 程序都达到优化后 C++ 的速度。

而是逐步降低 Python code 自身的执行成本。

Free-Threading 是另一块拼图

多年来,CPython 的一个主要限制就是 Global Interpreter Lock,也就是 GIL。

传统的 CPython implementation 一次只允许一个 thread 执行 Python bytecode。

这并不意味着 Python 无法进行并行计算。Libraries 可以释放 GIL,应用程序可以使用多个 processes,而 native code 也可以执行并行工作。

但对于那些大量时间都花在执行 Python code 上的 workload,这个限制仍然很重要。

从 Python 3.13 开始,CPython 提供了可以禁用 GIL 的 experimental free-threaded builds。

free-threaded build 改变了 runtime 的内存管理和同步机制,使多个 Python threads 可以并发执行 Python code。不过,这会带来真实的单线程性能成本。现有 benchmark 的结果差异较大,从大约 5%~10% 的下降到部分 workload 下更高的下降都有报告,具体取决于 Python version、build、hardware 和 workload。主要原因包括原子引用计数以及线程安全的内存管理开销,即使程序本身只有一个线程,也需要承担这些额外成本。

生态系统仍然需要继续解决兼容性问题。

C extensions 也需要针对 free-threaded execution 进行适配。在 free-threaded build 中,如果一个 extension 没有明确标记为支持 free threading,那么在导入它时,GIL 可能会被自动重新启用,同时打印 warning。这意味着应用程序导入了不兼容的 extension 后,可能失去预期的 free-threaded 行为。

因此,free-threading 并不是一个立即解决所有问题的方案。

它是让 Python 更好地适应现代 multi-core hardware 的长期工作中的一部分。

Python、C++、Rust 和 CUDA 并不一定相互竞争

随着 AI systems 越来越大,一个很容易出现的问题是:

Python 最终会不会因为性能问题被 C++ 或 Rust 取代?

现代 AI systems 的架构给出了另一种答案。

不同的语言可以处于不同的层次。

Python 适合:

 

Model development

 

 

Experimentation

 

 

Data pipelines

 

 

AI agents

 

 

Application logic

 

 

Orchestration

 

 

Research workflows

 

C++ 适合:

 

Runtime systems

 

 

High-performance numerical libraries

 

 

Low-latency inference

 

 

Memory-intensive components

 

 

Existing native codebases

 

CUDA 和 GPU programming systems 适合:

 

Custom GPU kernels

 

 

High-throughput numerical computation

 

 

Hardware-specific optimization

 

Rust 也越来越多地被用于一些 systems components,在这些场景中,memory safety 和 predictable native performance 比较重要。

因此,关键问题并不是:

哪种语言最快?

一个更有用的工程问题是:

这部分工作应该由哪一层负责?

层与层之间的边界,比语言名称更重要

性能问题往往来自架构,而不只是来自编程语言本身的选择。

例如:

Python
  ↓
small native call
  ↓
Python
  ↓
small native call
  ↓
Python
  ↓
small native call

这种方式可能性能较差,因为程序不断跨越不同的边界。

更好的设计可能是:

Python
  ↓
large native operation
  ↓
optimized CPU/GPU computation
  ↓
Python

AI compilers 中也存在相同的思想。

与其独立启动大量小的 operations,compiler 可以将它们组合成一个更大的 optimized computation。

Python 层进行不必要的协调越少,底层就有越多机会对 workload 进行优化。

Triton 展示了另一个方向

GPU programming 提供了一个更加有意思的例子。

Triton 提供一套嵌入 Python 中的编程模型来编写 GPU kernels,同时依赖自己的 compiler stack 生成高效的 GPU code。

不过,Triton kernel code 不能被理解成普通 Python code。它本质上是一套类 Python syntax 的 domain-specific language(DSL)。tl.load 和 tl.store 这样的操作由 Triton compiler 处理,不会交给 CPython interpreter 执行。

这形成了一种很特别的组合:

Python-like programming model
          ↓
Triton compiler
          ↓
GPU kernel
          ↓
GPU hardware

开发者不需要编写传统低级 GPU code 中的每一个细节。

与此同时,这些代码也并不是简单地作为普通 Python 执行。

这里使用的是类似 Python 的 syntax,用来描述最终会针对专用硬件进行编译的计算。

这也说明了现代 AI software 正在发展的一个方向:

“高层语言”和“底层性能”之间的边界正在变得更加灵活。

那么,AI 中的 Python 性能到底会怎样?

答案比简单地说“Python 需要变得更快”更加有意思。

AI software 越来越多地采用分层结构。

最上层:

Python

提供开发效率和方便的 programming model。

中间:

C / C++ / Rust

提供高效的 systems 和 runtime implementations。

对于 accelerators:

CUDA / Triton / GPU kernels

提供针对硬件的计算能力。

再往下:

Compilers

针对 CPU、GPU 和其他 accelerators 对整个计算过程进行优化。

与此同时,CPython 本身也在通过 interpreter optimizations、JIT compilation 和 free-threading 持续改进。

最终形成了一个有些反直觉的情况。

AI 可以继续大量使用 Python,同时又不要求 Python 自己完成所有繁重的计算。

这也是 Python 能够持续在 AI 领域保持重要地位的原因之一。

它的价值并不在于 Python 执行每一条 instruction 都尽可能快。

它的价值在于,Python 为开发者提供了一个高效的接口,用来使用下面庞大的 high-performance computing stack。

而随着 AI systems 变得越来越复杂,这种职责划分可能会变得更加重要。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2496 篇文章

作家榜 »

  1. Pack 2496 文章
  2. 轩辕小不懂 2403 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 216 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章