当性能分析工具告诉你某个 Python 函数是 CPU 瓶颈时,直接用 C/C++ 重写这部分逻辑是最彻底的加速方式之一。Python 生态里主要有两条路:传统 C 扩展和Cython。前者更底层、更灵活,后者更接近 Python 语法、开发效率高。
传统 C 扩展模块
传统方式用 C 语言直接编写模块,通过 Python/C API 与解释器交互。官网文档的 Extending Python with C or C++ 就是标准教程。
- 基本流程:
- 用 C 实现函数,接收
PyObject args,解析参数,执行计算,返回PyObject。 - 定义方法表,把函数名和 C 函数指针绑定。
- 定义模块结构,填充模块名和方法表。
- 编写
setup.py脚本,使用distutils编译成.so(Linux/macOS)或.pyd(Windows)文件。
- 适合场景:对性能要求极致、需要精细内存控制、直接调用已有 C/C++ 库。但开发调试成本较高,容易写出内存泄漏或段错误。
Cython:更友好的混合代码方案
Cython 是一个编程语言和编译器,让你用接近 Python 的语法编写扩展,最终编译成 C 扩展模块。它极大地降低了 C 扩展开发的门槛,是实际生产中最常用的选择。
- 核心用法:
- 把
.py文件重命名为.pyx(Cython 源文件)。 - 在
.pyx中逐步添加类型声明,让编译器生成高效 C 代码:
def add(int a, int b):
return a + b
- 编写
setup.py,用Cython.Build.cythonize编译.pyx文件:
from setuptools import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize("math_utils.pyx"))
- 运行
python setup.py build_ext --inplace生成.pyd/.so,然后import math_utils就像普通 Python 模块一样使用。
- 类型声明加速原理:Python 每次做
a + b都需要检查类型、查找add方法。当你声明int a, int b,Cython 直接生成 C 的整数相加代码,省去所有动态派发开销。 - 渐进式优化:可以先把 Python 代码改个后缀,跑通编译流程;然后逐步给热点变量和函数加类型声明,每次编译后测试性能提升。不必一上来就全部类型化。
实际建议与避坑
- 从 Cython 起步:绝大多数时候,Cython 已经足够快,且开发效率远高于手写 C 扩展。只有当你需要调用复杂 C++ 模板库、或者 Cython 生成的代码还不够快时,再考虑传统 C 扩展或 pybind11 等方案。
- 编译产物管理:生成的
.so/.pyd文件与 Python 版本和平台绑定,部署时需要确保编译环境与目标环境一致(通常用 manylinux 或 Docker 构建)。 - 配合 NumPy:Cython 对 NumPy 有直接支持,声明
cimport numpy as np和内存视图可实现零拷贝数据传递,科学计算性能提升明显。 - 调试:用
cython -a your_file.pyx生成带颜色标注的 HTML 报告,黄色越深代表 Python 交互越多,优化目标越明确。
总之,写 Python 代码,得 C 语言速度——这就是 Cython 的定位。当遇到性能瓶颈时,先用 Cython 做加速尝试,往往能以最小改动换来显著收益。