2954 字
15 分钟

Python 性能优化完全指南:从性能分析到内存优化与 JIT 加速实战

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

Python 性能优化

本文内容包括:

  • 性能分析工具(cProfile / line_profiler / memory_profiler / py-spy)
  • CPU 优化技巧
  • 内存优化策略
  • NumPy 向量化计算
  • Cython 编译加速
  • PyPy JIT 加速
  • 算法与数据结构优化
  • 优化决策树与最佳实践

一、性能分析工具#

1.1 cProfile(函数级分析)#

import cProfile
import 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函数自身耗时(不含子调用)
percalltottime / ncalls
cumtime累计耗时(含子调用)
percallcumtime / ncalls

1.2 line_profiler(逐行分析)#

Terminal window
# 安装
pip install line_profiler
script.py
@profile
def process_data(data):
result = []
for item in data:
# 逐行分析每行耗时
transformed = item * 2
result.append(transformed)
return result
data = list(range(100_000))
process_data(data)
Terminal window
# 运行
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 result

1.3 memory_profiler(内存分析)#

Terminal window
pip install memory_profiler
from memory_profiler import profile
@profile
def 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()
Terminal window
# 运行
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 a

1.4 py-spy(采样式分析,无需修改代码)#

Terminal window
# 安装
pip install py-spy
# 实时监控运行中的程序
py-spy top --pid 12345
# 生成火焰图
py-spy record -o profile.svg --pid 12345
# 直接运行
py-spy top python script.py

1.5 snakeviz(可视化)#

Terminal window
pip install snakeviz
# 生成可视化
python -m cProfile -o profile.prof script.py
snakeviz profile.prof
# 自动打开浏览器查看

二、CPU 优化技巧#

2.1 使用内置函数#

import time
data = list(range(1_000_000))
# ❌ 慢:手动循环
start = time.time()
total = 0
for x in data:
total += x
print(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) // 2
print(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)
# ✅ 快:join
result = "".join(str(i) for i in range(10_000))
# ✅ 更快:列表 + join
parts = [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 MB
print(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 array
import sys
# ❌ 列表存储整数
list_data = [0] * 1_000_000
print(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_000
chunk = 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)]
# ❌ 不使用 intern
total_before = sum(sys.getsizeof(s) for s in strings)
# ✅ 使用 intern
strings_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 np
import 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 + 1
print(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)
# ✅ NumPy
total = 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 安装与基础使用#

Terminal window
pip install cython
fast_math.pyx
# Cython 文件,编译后可加速 10-100 倍
def sum_squares(int n):
cdef long total = 0
cdef int i
for i in range(n):
total += i * i
return total
setup.py
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize("fast_math.pyx")
)
Terminal window
# 编译
python setup.py build_ext --inplace
# 使用
python -c "from fast_math import sum_squares; print(sum_squares(1000000))"

5.2 性能对比#

pure_python.py
def sum_squares(n):
total = 0
for i in range(n):
total += i * i
return total
# fast_math.pyx
def 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 类型注解加速#

cython_types.pyx
import numpy as np
cimport 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 安装与使用#

Terminal window
# 安装 PyPy
# macOS
brew install pypy3
# Linux
sudo apt install pypy3
# 使用(直接替换 python 命令)
pypy3 script.py

6.2 性能对比#

benchmark.py
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)
# ✅ defaultdict
groups = 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
# ✅ Counter
counts = Counter(words)

7.2 缓存计算结果#

from functools import lru_cache
import 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)) # 约 3s
print(f"无缓存: {time.time() - start:.2f}s")
start = time.time()
print(fib_fast(35)) # < 0.001s
print(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_cache100x+重复计算
slots省内存40%大量对象
Cython10-100x⭐⭐⭐CPU 密集
PyPy4-10x纯 Python
multiprocessingNx(N=核数)⭐⭐CPU 密集

8.3 优化检查清单#

✅ 先分析,后优化(不要盲目优化)
✅ 使用 cProfile 找出真正的瓶颈
✅ 优先使用内置函数和标准库
✅ 选择正确的数据结构
✅ 数值计算用 NumPy
✅ 重复计算用缓存
✅ 大数据用生成器
✅ CPU 密集用 Cython 或 multiprocessing
✅ 纯 Python 用 PyPy
✅ IO 密集用 asyncio
✅ 优化后重新测试验证效果

九、总结#

Python 性能优化的核心原则

  1. 先分析,后优化:用 cProfile/line_profiler 找到真正的瓶颈,不要猜测
  2. 优先简单方案:内置函数 > 列表推导 > NumPy > Cython
  3. 正确数据结构:集合查找 O(1) > 列表查找 O(n)
  4. 内存意识:生成器 > 列表,__slots__ > __dict__
  5. 缓存重复计算@lru_cache 是最简单的加速手段
  6. 数值计算用 NumPy:向量化比循环快 10-50 倍
  7. 极致性能用 Cython:CPU 密集型可加速 10-100 倍
  8. 整体加速用 PyPy:纯 Python 代码 4-10 倍加速

记住:过早优化是万恶之源。先写正确的代码,再分析瓶颈,最后针对性优化。2026 年,Python 的性能优化生态已经非常成熟,从标准库到第三方工具,总有一款适合你的场景。

Python 性能优化完全指南:从性能分析到内存优化与 JIT 加速实战
https://971918.xyz/posts/python-guide/python-performance-optimization/
作者
九所长
发布于
2026-07-24
许可协议
CC BY-NC-SA 4.0