Python 性能优化路径
更新时间:2026-08-29。本文回答:Python 慢在哪?怎么一步步把热点找出来并加速?什么时候该下沉到 C?
一、先测再优化:定位热点
不要凭直觉优化。先用 cProfile 看函数级耗时:
bash
python -m cProfile -s cumulative my_script.py或者用 py-spy(无需改代码、可 attach 到运行中的进程,生产友好)生成火焰图:
bash
pip install py-spy
py-spy top --pid 12345 # 实时看哪个函数占 CPU
py-spy record -o flame.svg --pid 12345 # 录一段火焰图| 工具 | 特点 | 场景 |
|---|---|---|
cProfile | 函数级统计,零侵入 | 开发期离线分析 |
py-spy | 采样、可 attach 生产进程 | 线上定位、火焰图 |
perf | 系统级、含 C 扩展 | 下沉后查底层热点(perf) |
核心结论:优化的第一性原则是"先量化、再动手"——和本站性能剖析方法论完全一致。
二、优化层级:从便宜到贵
- 算法与数据结构:把
list成员判断换成set(O(n)→O(1)),避免嵌套循环。 - 用内置与标准库:
sum()、map()、推导式通常比手写的for循环快(CPython 在 C 层实现)。 - numpy 向量化:把 Python 级逐元素循环换成 numpy 的 C 实现批量运算。
- 多进程并行:CPU 密集用
multiprocessing绕 GIL(见 GIL 与 asyncio)。 - C 扩展下沉:把真正的计算热点用 Cython / cffi / Rust 重写。
三、numpy 向量化示例
python
import numpy as np
# 慢:Python 级循环
def slow(a, b):
return [x + y for x, y in zip(a, b)]
# 快:numpy 在 C 层批量计算,且内存连续、缓存友好
def fast(a, b):
return np.add(a, b)numpy 之所以快,不只是"少了解释开销",更因为它操作的是连续内存块,对 CPU 缓存(L3 缓存组织)友好,还能走 SIMD 指令。
四、下沉到编译型代码
当 Python 本身成为瓶颈,把热点函数写成 C/类 C:
cython
# fast_sum.pyx
def fast_sum(double[:] arr):
cdef double s = 0.0
cdef int i
for i in range(arr.shape[0]):
s += arr[i]
return s- Cython:Python 超集,类型标注后编译成 C 扩展,热点提速 10~100x。
- cffi / ctypes:直接调用现成的 C 库。
- Rust + PyO3:用 Rust 写扩展,兼顾安全与零成本(见 Rust 快速上手)。
五、与本站主线的衔接
| 优化手段 | 本站对应 | 衔接文档 |
|---|---|---|
| 量化热点 | perf 观测方法论 | 先测后优化原则通用 |
| numpy 连续内存 | 缓存与程序性能 | 内存布局决定缓存命中 |
| 多进程并行 | 进程模型 | fork/CoW/IPC |
| C 扩展下沉 | ELF/ABI | 扩展是 .so,遵循 ABI |
六、常见坑
- 过早优化:没量化就重写,常常优化错地方。先
cProfile。 - 向量化陷阱:numpy 数组太小反而慢(启动开销 > 计算),要批量足够大才划算。
- 多进程序列化:跨进程传大对象有 pickle 开销,考虑
shared_memory。
一句话总结
Python 性能优化 = 先 cProfile/py-spy 量化热点 → 算法/向量化/并行层层递进 → 真瓶颈用 Cython/C/Rust 下沉;底层仍遵循本站的"内存布局 + 缓存 + perf"铁律。
回到总纲:Python 进阶。