跳到主要内容

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?

答案

  1. py-spy:采样式分析,对性能影响极小(<5%),可直接 attach 到生产进程
  2. Prometheus + 自定义指标:监控请求耗时分布
  3. OpenTelemetry:分布式链路追踪,定位慢的服务调用

Q3: timeittime.time() 的区别?

答案

timeit 会多次运行取平均值,自动关闭垃圾回收,结果更稳定。time.time() 单次测量受波动影响大。Benchmark 应使用 timeit

相关链接