人人都会AI编程

26.1 性能分析工具

更新时间:2026-07-12

性能优化的第一步永远是测量,而不是猜测。Python 提供了多种成熟的性能分析工具,覆盖从整体耗时到逐行细节、从 CPU 到内存的全维度诊断。这一节介绍最常用、最实用的几种,帮你快速定位瓶颈。

cProfile:整体 CPU 耗时分析

  • 做什么:统计程序中每个函数的调用次数、总耗时和平均耗时,帮你快速找到“哪个函数花了最多时间”。
  • 怎么用
  • 命令行方式:python -m cProfile -s cumulative your_script.py-s cumulative 按累计耗时排序,结果直接打印。
  • 代码内嵌方式:
    import cProfile
    cProfile.run('your_function()', sort='cumulative')
    
  • 生成可探索的 .prof 文件并可视化:
    python -m cProfile -o output.prof your_script.py
    pip install snakeviz
    snakeviz output.prof
    

SnakeViz 会在浏览器里给出火焰图和排序表格,非常直观。

  • 实战建议:cProfile 是优化起手式。先跑一次整体分析,锁定耗时占比最高的几个函数,而不是上来就逐行抠细节。注意 cProfile 本身也有少量性能开销,但在定位瓶颈时完全可以接受。

line_profiler:逐行耗时分析

  • 做什么:当你用 cProfile 确定某个函数是热点后,用 line_profiler 可以一行一行地告诉你在该函数内部每行代码的执行时间和次数
  • 怎么用
  • 安装:pip install line_profiler
  • 在希望分析的函数上添加装饰器 @profile,然后运行:
    kernprof -l -v your_script.py
    

输出会列出每一行代码的 Hits(执行次数)、Time(总耗时)、Per Hit(每次耗时)、% Time(占比),热点一目了然。

  • 典型场景:你发现某个数据处理函数整体很慢,用 line_profiler 后可能发现是某行循环里不必要的属性访问、重复计算或低效的字符串操作。几秒钟就能找到精确的优化点。

memory_profiler:内存占用分析

  • 做什么:逐行测量函数的内存使用量(单位为 MiB),定位内存泄漏、大对象创建、不必要的内存拷贝
  • 怎么用
  • 安装:pip install memory_profiler
  • 在函数上添加 @profile 装饰器,运行:
    python -m memory_profiler your_script.py
    

输出类似 Line # Mem usage Increment,能清晰看到哪一行内存暴涨。

  • 实用提示:结合 gc.collect() 强制触发垃圾回收后再测量,可以排除临时对象的干扰。对于长时间运行的服务,可以周期性地采样内存快照,观察是否持续上涨。

tracemalloc:内存追踪与比较

  • 做什么:这是 Python 内置的内存追踪工具(3.4+),可以记录每一次内存分配及其调用栈,特别适合找出哪些代码块导致了内存增长,并对两个时间点的内存快照进行对比
  • 怎么用
  import tracemalloc

  tracemalloc.start()
  # 运行你的代码
  snapshot1 = tracemalloc.take_snapshot()
  # ... 继续运行
  snapshot2 = tracemalloc.take_snapshot()

  stats = snapshot2.compare_to(snapshot1, 'lineno')
  for stat in stats[:10]:
      print(stat)
  

输出会显示内存新增最多的前10个文件和行号,以及新分配的内存大小。

  • 优势:比 memory_profiler 更底层,无须装饰器,可用于生产环境临时诊断。能精确告诉你是哪段代码制造了大量小对象或大字符串。

timeit:微基准测试

  • 做什么:精确测量小代码片段的执行时间(自动多次运行取最佳值,避免随机波动)。
  • 怎么用
  • 命令行:python -m timeit "sum(range(100))"
  • 代码中使用:timeit.timeit(stmt="[i**2 for i in range(1000)]", number=10000)
  • 在 Jupyter 中直接用 %timeit 魔法命令:
    %timeit [i**2 for i in range(1000)]
    
  • 使用原则
  • 只用于比较“哪种写法更快”,不要用来测量整个模块的性能(那是 cProfile 的活)。
  • 测试前确保被测代码没有任何副作用,且数据量足够小,否则 number 参数需要调大。
  • 切记:timeit 默认关闭了垃圾回收(模拟最佳情况),如果要真实场景,可设置 gc.enable()

实战分析流程

一个典型的性能排查路径如下:

  1. 宏观定位:用 cProfile 跑一遍程序,找出耗时最多的几个函数。
  2. 微观剖析:对可疑函数用 line_profiler 逐行分析,找出具体耗时的代码行。
  3. 内存检查:如果怀疑内存问题,用 memory_profilertracemalloc 定位内存增长点。
  4. 小优化验证:针对单个语句或小逻辑用 timeit 快速对比优化前后的速度差异。
  5. 反复迭代:改一处,测一次,不要凭感觉,要用数据说话。

记住:没有测量,就没有优化。这些工具就是你的诊断仪器,让性能瓶颈无处隐藏。