2954 字
15 分钟
Python 性能优化完全指南:从性能分析到内存优化与 JIT 加速实战
Python 以开发效率著称,但运行效率常被诟病。然而,通过正确的性能分析工具和优化技巧,Python 程序的性能可以提升数倍甚至数十倍。本文从性能分析到各种优化手段,提供完整的 Python 性能优化指南。

本文内容包括:
- 性能分析工具(cProfile / line_profiler / memory_profiler / py-spy)
- CPU 优化技巧
- 内存优化策略
- NumPy 向量化计算
- Cython 编译加速
- PyPy JIT 加速
- 算法与数据结构优化
- 优化决策树与最佳实践
一、性能分析工具
1.1 cProfile(函数级分析)
import cProfileimport pstats
def slow_function(): total = 0 for i in range(1_000_000): total += i * i return total
def fast_function(): return sum(i * i for i in range(1_000_000))
def main(): slow_function() fast_function()
# 方法1:代码内调用cProfile.run('main()', 'profile.prof')
# 查看结果stats = pstats.Stats('profile.prof')stats.sort_stats('cumulative')stats.print_stats(10)
# 方法2:命令行# python -m cProfile -o profile.prof script.py# python -m pstats profile.prof输出解读:
ncalls tottime percall cumtime percall filename:lineno(function) 1 0.234 0.234 0.234 0.234 script.py:5(slow_function) 1 0.156 0.156 0.156 0.156 script.py:9(fast_function)| 列名 | 含义 |
|---|---|
| ncalls | 调用次数 |
| tottime | 函数自身耗时(不含子调用) |
| percall | tottime / ncalls |
| cumtime | 累计耗时(含子调用) |
| percall | cumtime / ncalls |
1.2 line_profiler(逐行分析)
# 安装pip install line_profiler@profiledef process_data(data): result = [] for item in data: # 逐行分析每行耗时 transformed = item * 2 result.append(transformed) return result
data = list(range(100_000))process_data(data)# 运行kernprof -l -v script.py
# 输出示例:# Line # Hits Time Per Hit % Time Line Contents# 2 @profile# 3 def process_data(data):# 4 1 10 10.0 0.0 result = []# 5 100001 50000 0.5 25.0 for item in data:# 7 100000 90000 0.9 45.0 transformed = item * 2# 8 100000 60000 0.6 30.0 result.append(transformed)# 9 1 5 5.0 0.0 return result1.3 memory_profiler(内存分析)
pip install memory_profilerfrom memory_profiler import profile
@profiledef memory_heavy(): # 逐行内存变化 a = [1] * 1_000_000 # +8 MB b = [2] * 1_000_000 # +8 MB c = a + b # +16 MB del a # -8 MB return c
memory_heavy()# 运行python -m memory_profiler script.py
# 输出示例:# Line # Mem usage Increment Occurrences Line Contents# 3 50.0 MiB 50.0 MiB 1 @profile# 4 58.0 MiB 8.0 MiB 1 a = [1] * 1_000_000# 5 66.0 MiB 8.0 MiB 1 b = [2] * 1_000_000# 6 74.0 MiB 8.0 MiB 1 c = a + b# 7 66.0 MiB -8.0 MiB 1 del a1.4 py-spy(采样式分析,无需修改代码)
# 安装pip install py-spy
# 实时监控运行中的程序py-spy top --pid 12345
# 生成火焰图py-spy record -o profile.svg --pid 12345
# 直接运行py-spy top python script.py1.5 snakeviz(可视化)
pip install snakeviz
# 生成可视化python -m cProfile -o profile.prof script.pysnakeviz profile.prof# 自动打开浏览器查看二、CPU 优化技巧
2.1 使用内置函数
import time
data = list(range(1_000_000))
# ❌ 慢:手动循环start = time.time()total = 0for x in data: total += xprint(f"手动循环: {time.time() - start:.4f}s")
# ✅ 快:内置 sum()start = time.time()total = sum(data)print(f"sum(): {time.time() - start:.4f}s")
# ✅ 更快:数学公式start = time.time()n = len(data)total = n * (n - 1) // 2print(f"数学公式: {time.time() - start:.4f}s")
# 结果:# 手动循环: 0.0234s# sum(): 0.0045s (5倍加速)# 数学公式: 0.0000s (1000倍加速)2.2 列表推导式 vs 循环
# ❌ 慢result = []for i in range(1_000_000): result.append(i * 2)
# ✅ 快(快约 2 倍)result = [i * 2 for i in range(1_000_000)]
# ✅ 更快(生成器表达式,省内存)result = (i * 2 for i in range(1_000_000))
# ✅ 最快(map)result = list(map(lambda x: x * 2, range(1_000_000)))2.3 字符串拼接
# ❌ 慢:字符串拼接result = ""for i in range(10_000): result += str(i)
# ✅ 快:joinresult = "".join(str(i) for i in range(10_000))
# ✅ 更快:列表 + joinparts = [str(i) for i in range(10_000)]result = "".join(parts)2.4 集合查找 vs 列表查找
data_list = list(range(100_000))data_set = set(range(100_000))
# ❌ 慢:列表查找 O(n)if 99_999 in data_list: pass
# ✅ 快:集合查找 O(1)if 99_999 in data_set: pass
# 集合查找比列表快 10000+ 倍(大数据集)2.5 局部变量优化
import math
# ❌ 慢:全局查找def compute_global(n): result = 0 for i in range(n): result += math.sqrt(i) # 每次都查找 math.sqrt return result
# ✅ 快:局部变量def compute_local(n): sqrt = math.sqrt # 局部缓存 result = 0 for i in range(n): result += sqrt(i) return result
# 局部变量查找比全局快约 15-30%2.6 避免不必要的属性访问
class DataProcessor: def __init__(self): self.data = list(range(100_000))
# ❌ 慢:重复属性访问 def process_slow(self): total = 0 for i in range(len(self.data)): total += self.data[i] # 每次都查找 self.data return total
# ✅ 快:缓存属性 def process_fast(self): data = self.data # 一次查找 total = 0 for i in range(len(data)): total += data[i] return total
# ✅ 最快:直接迭代 def process_best(self): return sum(self.data)三、内存优化
3.1 使用生成器
import sys
# ❌ 列表:一次性加载所有数据def get_numbers_list(n): return [i for i in range(n)]
# ✅ 生成器:惰性求值def get_numbers_gen(n): for i in range(n): yield i
# 内存对比list_numbers = get_numbers_list(1_000_000)gen_numbers = get_numbers_gen(1_000_000)
print(f"列表内存: {sys.getsizeof(list_numbers)} bytes") # ~8 MBprint(f"生成器内存: {sys.getsizeof(gen_numbers)} bytes") # ~120 bytes
# 生成器节省了 99.99% 的内存3.2 使用 slots
import sys
# ❌ 普通类:使用 __dict__ 存储属性class PointDict: def __init__(self, x, y, z): self.x = x self.y = y self.z = z
# ✅ slots 类:固定属性,省内存class PointSlots: __slots__ = ('x', 'y', 'z')
def __init__(self, x, y, z): self.x = x self.y = y self.z = z
# 内存对比p1 = PointDict(1, 2, 3)p2 = PointSlots(1, 2, 3)
print(f"普通类: {sys.getsizeof(p1) + sys.getsizeof(p1.__dict__)} bytes")print(f"slots类: {sys.getsizeof(p2)} bytes")
# 创建 100 万个对象points_dict = [PointDict(1, 2, 3) for _ in range(1_000_000)]points_slots = [PointSlots(1, 2, 3) for _ in range(1_000_000)]
# slots 类节省约 40-50% 内存3.3 使用 array 模块
import arrayimport sys
# ❌ 列表存储整数list_data = [0] * 1_000_000print(f"列表: {sys.getsizeof(list_data)} bytes") # ~8 MB
# ✅ array 存储整数array_data = array.array('i', [0] * 1_000_000)print(f"array: {sys.getsizeof(array_data)} bytes") # ~4 MB
# array 比列表节省约 50% 内存3.4 使用 memoryview
# ❌ 切片创建副本data = b'x' * 1_000_000chunk = data[100:200] # 创建新的 bytes 对象
# ✅ memoryview 零拷贝mv = memoryview(data)chunk = mv[100:200] # 不创建副本
# 适合处理大二进制数据(图像、视频)3.5 使用 intern() 复用字符串
import sys
# 大量重复字符串strings = [f"user_{i % 100}" for i in range(1_000_000)]
# ❌ 不使用 interntotal_before = sum(sys.getsizeof(s) for s in strings)
# ✅ 使用 internstrings_interned = [sys.intern(s) for s in strings]total_after = sum(sys.getsizeof(s) for s in strings_interned)
# intern 后相同字符串共享内存四、NumPy 向量化计算
4.1 基础向量化
import numpy as npimport time
# 创建大数据data = list(range(1_000_000))np_data = np.array(data)
# ❌ Python 循环start = time.time()result = [x * 2 + 1 for x in data]print(f"Python 列表推导: {time.time() - start:.4f}s")
# ✅ NumPy 向量化start = time.time()result = np_data * 2 + 1print(f"NumPy 向量化: {time.time() - start:.4f}s")
# NumPy 快 10-50 倍4.2 常用向量化操作
import numpy as np
# 条件筛选data = np.random.randn(1_000_000)
# ❌ 循环筛选result = [x for x in data if x > 0]
# ✅ 布尔索引result = data[data > 0]
# 数学运算# ❌ 循环result = [math.sqrt(abs(x)) for x in data]
# ✅ 向量化result = np.sqrt(np.abs(data))
# 聚合运算# ❌ 循环total = sum(data)mean = sum(data) / len(data)
# ✅ NumPytotal = np.sum(data)mean = np.mean(data)std = np.std(data)4.3 广播(Broadcasting)
import numpy as np
# 矩阵运算matrix = np.random.rand(1000, 1000)vector = np.random.rand(1000)
# ❌ 循环逐行计算result = np.zeros_like(matrix)for i in range(1000): result[i] = matrix[i] + vector
# ✅ 广播自动扩展result = matrix + vector # 自动广播
# 广播比循环快 100+ 倍五、Cython 编译加速
5.1 安装与基础使用
pip install cython# Cython 文件,编译后可加速 10-100 倍
def sum_squares(int n): cdef long total = 0 cdef int i for i in range(n): total += i * i return totalfrom setuptools import setupfrom Cython.Build import cythonize
setup( ext_modules=cythonize("fast_math.pyx"))# 编译python setup.py build_ext --inplace
# 使用python -c "from fast_math import sum_squares; print(sum_squares(1000000))"5.2 性能对比
def sum_squares(n): total = 0 for i in range(n): total += i * i return total
# fast_math.pyxdef sum_squares(int n): cdef long total = 0 cdef int i for i in range(n): total += i * i return total
# 测试结果(n = 10,000,000):# Python: 4.52s# Cython: 0.03s (150 倍加速)# NumPy: 0.05s (90 倍加速)5.3 类型注解加速
import numpy as npcimport numpy as cnp
def process_array(cnp.ndarray[cnp.double_t, ndim=1] data): cdef int n = len(data) cdef cnp.ndarray[cnp.double_t, ndim=1] result = np.zeros(n) cdef int i for i in range(n): result[i] = data[i] * 2.0 + 1.0 return result六、PyPy JIT 加速
6.1 安装与使用
# 安装 PyPy# macOSbrew install pypy3
# Linuxsudo apt install pypy3
# 使用(直接替换 python 命令)pypy3 script.py6.2 性能对比
def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2)
# 运行# python benchmark.py → 约 12.5s# pypy3 benchmark.py → 约 2.1s (6 倍加速)6.3 PyPy 适用场景
| 场景 | 加速效果 | 推荐 |
|---|---|---|
| 纯 Python 循环 | 4-10x | ✅ |
| 字符串处理 | 3-5x | ✅ |
| Web 应用 | 3-5x | ✅ |
| NumPy 计算 | 0.5-1x | ❌ |
| C 扩展密集 | 可能更慢 | ❌ |
| 短脚本 | 有预热开销 | ⚠️ |
七、算法与数据结构优化
7.1 选择正确的数据结构
from collections import deque, defaultdict, Counter
# ❌ 列表头部插入 O(n)data = list(range(100_000))data.insert(0, -1) # 慢
# ✅ deque 头部插入 O(1)data = deque(range(100_000))data.appendleft(-1) # 快
# ❌ 字典分组groups = {}for item in items: key = item.category if key not in groups: groups[key] = [] groups[key].append(item)
# ✅ defaultdictgroups = defaultdict(list)for item in items: groups[item.category].append(item)
# ❌ 手动计数counts = {}for word in words: if word in counts: counts[word] += 1 else: counts[word] = 1
# ✅ Countercounts = Counter(words)7.2 缓存计算结果
from functools import lru_cacheimport time
# ❌ 重复计算def fib_slow(n): if n <= 1: return n return fib_slow(n - 1) + fib_slow(n - 2)
# ✅ 缓存@lru_cache(maxsize=None)def fib_fast(n): if n <= 1: return n return fib_fast(n - 1) + fib_fast(n - 2)
# 测试start = time.time()print(fib_slow(35)) # 约 3sprint(f"无缓存: {time.time() - start:.2f}s")
start = time.time()print(fib_fast(35)) # < 0.001sprint(f"有缓存: {time.time() - start:.2f}s")7.3 提前退出
# ❌ 遍历整个列表def has_negative(numbers): found = False for n in numbers: if n < 0: found = True return found
# ✅ 找到就退出def has_negative(numbers): for n in numbers: if n < 0: return True return False
# ✅ 更简洁def has_negative(numbers): return any(n < 0 for n in numbers) # 生成器,找到即停八、优化决策树
8.1 优化流程
性能问题?│├── 第1步:性能分析│ ├── cProfile 找热点函数│ ├── line_profiler 逐行分析│ └── memory_profiler 内存分析│├── 第2步:低 hanging fruit(快速见效)│ ├── 使用内置函数(sum, max, min)│ ├── 列表推导式替代循环│ ├── 集合替代列表查找│ └── 局部变量缓存│├── 第3步:算法优化│ ├── 选择正确数据结构│ ├── 减少时间复杂度│ ├── 使用缓存│ └── 提前退出│├── 第4步:内存优化│ ├── 生成器替代列表│ ├── __slots__│ ├── array / memoryview│ └── 及时释放大对象│├── 第5步:高级优化│ ├── NumPy 向量化│ ├── Cython 编译│ ├── PyPy JIT│ └── multiprocessing 并行│└── 第6步:架构优化 ├── 异步 IO(asyncio) ├── 批处理 └── 分布式计算8.2 优化效果参考
| 优化手段 | 加速倍数 | 难度 | 适用场景 |
|---|---|---|---|
| 内置函数 | 2-5x | ⭐ | 通用 |
| 列表推导式 | 2x | ⭐ | 通用 |
| 集合查找 | 100x+ | ⭐ | 查找操作 |
| NumPy 向量化 | 10-50x | ⭐⭐ | 数值计算 |
| lru_cache | 100x+ | ⭐ | 重复计算 |
| slots | 省内存40% | ⭐ | 大量对象 |
| Cython | 10-100x | ⭐⭐⭐ | CPU 密集 |
| PyPy | 4-10x | ⭐ | 纯 Python |
| multiprocessing | Nx(N=核数) | ⭐⭐ | CPU 密集 |
8.3 优化检查清单
✅ 先分析,后优化(不要盲目优化)✅ 使用 cProfile 找出真正的瓶颈✅ 优先使用内置函数和标准库✅ 选择正确的数据结构✅ 数值计算用 NumPy✅ 重复计算用缓存✅ 大数据用生成器✅ CPU 密集用 Cython 或 multiprocessing✅ 纯 Python 用 PyPy✅ IO 密集用 asyncio✅ 优化后重新测试验证效果九、总结
Python 性能优化的核心原则:
- 先分析,后优化:用 cProfile/line_profiler 找到真正的瓶颈,不要猜测
- 优先简单方案:内置函数 > 列表推导 > NumPy > Cython
- 正确数据结构:集合查找 O(1) > 列表查找 O(n)
- 内存意识:生成器 > 列表,
__slots__>__dict__ - 缓存重复计算:
@lru_cache是最简单的加速手段 - 数值计算用 NumPy:向量化比循环快 10-50 倍
- 极致性能用 Cython:CPU 密集型可加速 10-100 倍
- 整体加速用 PyPy:纯 Python 代码 4-10 倍加速
记住:过早优化是万恶之源。先写正确的代码,再分析瓶颈,最后针对性优化。2026 年,Python 的性能优化生态已经非常成熟,从标准库到第三方工具,总有一款适合你的场景。
Python 性能优化完全指南:从性能分析到内存优化与 JIT 加速实战
https://971918.xyz/posts/python-guide/python-performance-optimization/