Python慢是公认的,但通过C扩展、JIT、Rust绑定,可以达到接近C的性能。本文讲解Cython、Numba、PyO3三种加速方案,并对比实战。
一、性能瓶颈分析
python
复制
下载
import timeit # 纯Python def fib_py(n): if n < 2: return n return fib_py(n-1) + fib_py(n-2) # 测试 print(timeit.timeit(lambda: fib_py(30), number=1))
先用cProfile定位热点,再选择加速方案。
二、Cython
Cython将Python代码编译为C扩展。
bash
复制
下载
pip install cython
2.1 基础用法
fib.pyx:
cython
复制
下载
def fib(int n): cdef int i cdef long long a = 0, b = 1 for i in range(n): a, b = b, a + b return a
setup.py:
python
复制
下载
from setuptools import setup from Cython.Build import cythonize setup( ext_modules=cythonize("fib.pyx"), )编译:
bash
复制
下载
python setup.py build_ext --inplace
使用:
python
复制
下载
from fib import fib print(fib(100))
2.2 类型声明
cython
复制
下载
# 声明变量类型 cdef int x = 10 cdef double y = 3.14 cdef list lst = [1, 2, 3] # 函数类型 cdef int add(int a, int b): return a + b # 结构体 cdef struct Point: double x double y # 内存视图 def sum_array(double[:] arr): cdef int i cdef double total = 0 for i in range(arr.shape[0]): total += arr[i] return total
2.3 性能对比
python
复制
下载
# 纯Python:约2秒 # Cython:约0.01秒 # 提升200倍
三、Numba JIT
Numba用LLVM在运行时编译,只需一个装饰器。
bash
复制
下载
pip install numba
python
复制
下载
from numba import jit, njit, prange import numpy as np @njit def sum_squares(arr): total = 0.0 for i in range(len(arr)): total += arr[i] ** 2 return total arr = np.random.rand(1000000) print(sum_squares(arr)) # 首次慢,后续快
3.1 并行加速
python
复制
下载
@njit(parallel=True) def parallel_sum(arr): total = 0.0 for i in prange(len(arr)): total += arr[i] ** 2 return total
3.2 向量化
python
复制
下载
from numba import vectorize @vectorize(['float64(float64, float64)'], target='cuda') def add_cuda(a, b): return a + b
3.3 适用场景
数值计算
循环密集
NumPy数组操作
不适用:字符串处理、复杂对象、I/O。
四、PyO3:Rust绑定
Rust性能接近C,内存安全,适合写扩展。
bash
复制
下载
pip install maturin maturin init --bindings pyo3
src/lib.rs:
rust
复制
下载
use pyo3::prelude::*; #[pyfunction] fn fib(n: u64) -> u64 { if n < 2 { return n; } let mut a = 0u64; let mut b = 1u64; for _ in 0..n { let temp = a + b; a = b; b = temp; } a } #[pyfunction] fn sum_squares(arr: Vec<f64>) -> f64 { arr.iter().map(|x| x * x).sum() } #[pymodule] fn mylib(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(fib, m)?)?; m.add_function(wrap_pyfunction!(sum_squares, m)?)?; Ok(()) }pyproject.toml:
toml
复制
下载
[build-system] requires = ["maturin>=0.14"] build-backend = "maturin" [project] name = "mylib" requires-python = ">=3.8"
编译:
bash
复制
下载
maturin develop --release
使用:
python
复制
下载
import mylib print(mylib.fib(100))
五、ctypes与cffi
5.1 ctypes调用C库
python
复制
下载
import ctypes lib = ctypes.CDLL("./libmath.so") lib.add.argtypes = [ctypes.c_int, ctypes.c_int] lib.add.restype = ctypes.c_int print(lib.add(1, 2))5.2 cffi
python
复制
下载
from cffi import FFI ffi = FFI() ffi.cdef("int add(int a, int b);") lib = ffi.dlopen("./libmath.so") print(lib.add(1, 2))六、四种方案对比
| 方案 | 性能 | 学习曲线 | 适用 |
|---|---|---|---|
| Cython | 高 | 中 | 通用加速 |
| Numba | 高 | 低 | 数值计算 |
| PyO3 | 极高 | 高 | 系统编程 |
| ctypes | 中 | 低 | 调用现有C库 |
七、实战:加速图像处理
python
复制
下载
# 纯Python:约10秒 def apply_filter_py(img, kernel): h, w = img.shape result = np.zeros_like(img) for i in range(1, h-1): for j in range(1, w-1): total = 0.0 for ki in range(3): for kj in range(3): total += img[i-1+ki, j-1+kj] * kernel[ki][kj] result[i, j] = total return result # Numba:约0.1秒 @njit(parallel=True) def apply_filter_numba(img, kernel): h, w = img.shape result = np.zeros_like(img) for i in prange(1, h-1): for j in range(1, w-1): total = 0.0 for ki in range(3): for kj in range(3): total += img[i-1+ki, j-1+kj] * kernel[ki][kj] result[i, j] = total return result
八、选择建议
数值计算:Numba
通用加速:Cython
系统级、安全:Rust + PyO3
已有C库:ctypes/cffi
简单场景:先优化算法和数据结构
九、总结
Python性能优化优先级:算法 > 数据结构 > 向量化 > JIT/C扩展。Numba最简单,Cython最通用,Rust最强大。根据场景选择,避免过早优化。