Profiling 性能分析
问题
Python 中如何定位性能瓶颈?有哪些 profiling 工具?
答案
cProfile(内置)
import cProfile
import pstats
# 方式一:命令行
# python -m cProfile -s cumulative myapp.py
# 方式二:代码中使用
profiler = cProfile.Profile()
profiler.enable()
# 你的代码
result = expensive_function()
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats(20) # 打印最耗时的 20 个函数
输出示例:
ncalls tottime percall cumtime percall filename:lineno(function)
1000 2.340 0.002 5.670 0.006 service.py:45(process_data)
50000 1.230 0.000 1.230 0.000 utils.py:12(validate)
line_profiler(逐行分析)
# pip install line_profiler
@profile # 标记需要分析的函数
def process_data(items: list[dict]) -> list:
result = []
for item in items: # Line 1: 循环
validated = validate(item) # Line 2: 验证
transformed = transform(validated) # Line 3: 转换
result.append(transformed) # Line 4: 追加
return result
# 运行: kernprof -l -v script.py
py-spy(生产环境火焰图)
# 不需要修改代码,直接附加到运行中的进程
pip install py-spy
# 生成火焰图
py-spy record -o flamegraph.svg --pid 12345
# 实时 top 视图
py-spy top --pid 12345
Benchmark
import timeit
# 对比两种实现
setup = "data = list(range(10000))"
# 方式一
t1 = timeit.timeit("sum(data)", setup=setup, number=10000)
# 方式二
t2 = timeit.timeit("reduce(lambda a, b: a + b, data)",
setup=setup + "; from functools import reduce",
number=10000)
print(f"sum(): {t1:.3f}s, reduce(): {t2:.3f}s")
常见面试问题
Q1: cProfile 和 line_profiler 怎么选?
答案:
- cProfile:先用它定位哪个函数慢(函数级别)
- line_profiler:再用它分析慢函数内哪一行慢(行级别)
Q2: 生产环境怎么做 profiling?
答案:
- py-spy:采样式分析,对性能影响极小(
<5%),可直接 attach 到生产进程 - Prometheus + 自定义指标:监控请求耗时分布
- OpenTelemetry:分布式链路追踪,定位慢的服务调用
Q3: timeit 和 time.time() 的区别?
答案:
timeit 会多次运行取平均值,自动关闭垃圾回收,结果更稳定。time.time() 单次测量受波动影响大。Benchmark 应使用 timeit。