优先选cprofile快速定位瓶颈函数,line_profiler用于深入分析已知慢函数的行级耗时;注意cprofile不统计c扩展、line_profiler需装饰器且不支持交互式代码。

怎么选 cProfile 还是 line_profiler
看你想定位到哪一层:cProfile 告诉你哪个函数耗时最多,适合快速锁定瓶颈模块;line_profiler 能看到函数内部每一行的执行时间,适合优化已知慢函数里的具体逻辑。
常见错误是直接上 line_profiler——它得先用 @profile 装饰目标函数,且不能 profile 交互式代码(比如 Jupyter 单元格里直接跑),否则报 LineProfiler object has no attribute 'add_function'。
-
cProfile开箱即用,python -m cProfile -s cumulative myscript.py就能出排序结果 -
line_profiler需要 pip install 后手动注册装饰器,且只对 .py 文件里的函数生效 - 如果函数调用链深、C 扩展多(比如 pandas/Numpy 操作),
cProfile会低估实际耗时,因为 C 层不被统计
cProfile 输出看不懂?关键字段怎么读
运行后默认输出类似:ncalls 是调用次数,tottime 是函数自身耗时(不含子调用),cumtime 是累计耗时(含所有子调用)。真正该盯的是 cumtime 排序后的顶部几项。
容易踩的坑是忽略 tottime 和 cumtime 的区别。比如一个函数 cumtime 很高但 tottime 很低,说明它只是个“中转站”,真耗子在它调用的下游函数里。
立即学习“Python免费学习笔记(深入)”;
本课程在设计上本着懂方法,重应用的总体思路,突出体现职业教育的技能型、应用性特色,着重培养学生的实践应用技能,力求达到理论方法够用,技术技能过硬的目的。 通过本课程的学习,使学生具备Android平台应用开发相关知识、良好的编程习惯和手机应用软件开发的能力,能胜任基于Android平台的手机软件研发等工作任务。感兴趣的朋友可以过来看看
- 加
-s cumulative按累计时间降序,比默认的ncalls排序有用得多 - 用
pstats.Stats加载结果后,可以stats.sort_stats('cumulative').print_stats(10)只看前 10 行 - 别信
built-in method这类条目——它们是 C 层调用,cProfile不深入,数值仅供参考
为什么 timeit 和 profiling 结果对不上
timeit 测的是单次小片段的纯 CPU 时间,而 profiling(尤其 cProfile)包含解释器开销、函数调用栈维护、甚至 I/O 等待——两者测量维度根本不同。
典型场景:你用 timeit 发现某个列表推导很快,但整个脚本跑起来却慢。很可能是因为这个推导被调用了几千次,或者它前面有大量磁盘读取没被 timeit 包进去。
-
timeit适合验证“这段逻辑本身快不快” - profiling 适合回答“整个流程里,时间到底花在哪了”
- 如果 profiling 显示某函数
cumtime高但tottime低,再用timeit测它的子调用,往往能找到真实瓶颈
线上服务能直接跑 profiling 吗
不能随便跑。cProfile 默认开销约 5–10%,可能拖慢响应;line_profiler 更重,还可能因装饰器引入线程安全问题。
真实生产环境更常用采样式方案:比如用 py-spy(Rust 写的,无需修改代码,支持 attach 到进程),或导出火焰图(flameprof + pip install py-spy)。
- 本地调试用
cProfile完全够用,但别在压测时开着它——数据失真,还影响并发表现 - 想看内存分配?
cProfile不管这事,换memory_profiler,但它需要逐行插桩,开销更大 - 异步代码(async/await)用
cProfile会漏掉协程切换开销,此时优先考虑py-spy record -o profile.svg --pid $PID
profiling 不是万能开关,它只反映“这一轮运行”的行为。IO 波动、缓存命中率、GIL 争抢这些动态因素,一次采样说不清。









