Python函数运行缓慢是开发过程中常见的问题,盲目优化往往效率低下,火焰图作为性能分析的可视化工具,能清晰展示函数调用关系和各部分的耗时占比,帮助开发者快速找到性能瓶颈所在。

火焰图的基本原理
火焰图的横轴代表采样数量,纵轴代表函数调用栈,每个矩形块对应一个函数,块的长度对应该函数的采样耗时占比。颜色仅用于区分不同函数,没有特殊含义。如果某个函数对应的矩形块很长,说明该函数在采样期间占用的时间较多,大概率是性能瓶颈。
生成Python程序火焰图的工具
常用的生成Python火焰图的工具是py-spy,它是一款采样分析器,不需要修改代码就能对运行中的Python程序进行性能分析,支持生成火焰图文件。
安装py-spy
可以通过pip直接安装py-spy:
pip install py-spy
生成火焰图
假设我们有一个运行缓慢的Python脚本slow_func.py,内容如下:
import time
def sub_func1():
# 模拟耗时操作
time.sleep(0.5)
def sub_func2():
total = 0
for i in range(1000000):
total += i
return total
def main_func():
for _ in range(3):
sub_func1()
sub_func2()
if __name__ == "__main__":
main_func()
运行以下命令即可生成火焰图:
py-spy record -o flamegraph.svg -- python slow_func.py
命令执行完成后,会在当前目录生成flamegraph.svg文件,用浏览器打开即可查看火焰图。
通过火焰图定位瓶颈
打开生成的火焰图后,我们可以从下到上查看调用栈,最下方的函数是入口函数,上方的函数是被调用的子函数。观察矩形块的长度,长度越长的函数耗时越多。
在上述示例的火焰图中,可以看到sub_func1对应的矩形块长度明显长于sub_func2,说明sub_func1是主要的耗时点。进一步查看sub_func1的实现,发现是time.sleep(0.5)导致的耗时,这就是需要优化的瓶颈。
常见优化方向
- 如果是IO等待导致的耗时,比如time.sleep、网络请求、文件读写,可以考虑使用异步IO或者多线程优化
- 如果是计算密集型操作,比如大量循环、复杂运算,可以考虑使用numpy等优化库,或者将核心逻辑用C扩展实现
- 如果是重复调用的小函数,可以考虑使用缓存装饰器减少重复计算
注意事项
采样分析会有一定的误差,火焰图展示的是采样期间的耗时占比,对于运行时间很短的函数,可能需要延长采样时间才能得到准确的结果。另外,不要在火焰图中寻找绝对的时间数值,重点关注相对耗时占比即可。