人人都会AI编程

cProfile /line_profiler:CPU 耗时分析

更新时间:2026-07-12

性能优化的第一步永远是找到瓶颈在哪里,而不是凭感觉去改。Python 提供了两种常用的 CPU 耗时分析工具:cProfile 适合整体概览,line_profiler 适合逐行细查。


cProfile:整体性能画像

cProfile 是标准库自带的性能分析器,统计每个函数的调用次数、总耗时、平均耗时等。开销较低,适合快速定位“哪个函数最慢”。

基本用法

import cProfile
import re

def slow_function():
    return re.compile(r"a*b*c*").match("a" * 1000)

cProfile.run("slow_function()")

运行后会输出类似这样的表格:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
        1    0.001    0.001    0.001    0.001 script.py:4(slow_function)
  • ncalls:调用次数
  • tottime:函数自身耗时(不含内部调用的子函数)
  • cumtime:函数总耗时(含内部子函数)
  • 排序后很快能看出哪个函数占用了大部分 CPU 时间。

更实用的方式

  • -o 保存分析结果到文件,再用 pstats 模块交互式查看。
python -m cProfile -o output.prof my_script.py

然后在代码里或命令行中用 pstats 排序、过滤。

适用场景:首次对程序进行性能体检,快速定位高耗时的函数或模块。


line_profiler:逐行性能剖析

cProfile 只能告诉你哪个函数慢,但进到函数内部,到底是循环太多、还是某条语句特别耗时,它就鞭长莫及了。这时需要 line_profiler

line_profiler 是第三方库(需 pip install line_profiler),可以给指定函数加上 @profile 装饰器,执行后显示每一行的运行次数、每次耗时、总时间及占比

示例

# script.py
from line_profiler import LineProfiler

@profile
def add_numbers():
    total = 0
    for i in range(100000):
        total += i
    return total

if __name__ == '__main__':
    add_numbers()

然后用 kernprof -lv script.py 运行(kernprof 是 line_profiler 自带的运行器),输出:

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     1                                           @profile
     2                                           def add_numbers():
     3         1          2.0      2.0      0.0      total = 0
     4     100001      25000.0      0.2     99.9      for i in range(100000):
     5     100000      55000.0      0.5      0.1          total += i
     6         1          2.0      2.0      0.0      return total

一眼看出循环行和累加行耗时占比。

实用技巧

  • 不需要修改代码的另一种方式:实例化 LineProfiler,手动添加要分析的函数。
lp = LineProfiler()
lp.add_function(slow_func)
lp.run('slow_func()')
lp.print_stats()
  • 结合 IPython 使用更便捷:%load_ext line_profiler,然后 %lprun -f func_name func_name()

两者的选择策略

| 场景 | 推荐工具 |
|------|----------|
| 程序刚变慢,不知哪里出问题 | cProfile 快速扫描 |
| 已经确定某个函数慢,但需要定位具体哪一行 | line_profiler |
| 生产环境临时诊断 | cProfile(标准库,零依赖) |

重要提示:分析工具本身有一定开销,line_profiler 的开销比 cProfile 大,不要在线上一直开着。通常在开发环境重现问题后,找到瓶颈即可。


实际开发中,这两个工具配合使用效率极高:先用 cProfile 把“嫌疑犯”限制到某几个函数,再用 line_profiler 揪出具体行,然后有针对性地优化算法或数据结构。这种“测量驱动优化”的方式,比靠直觉瞎改可靠得多。