Python的性能分析器
Python中最常用的性能分析工具主要有:cProfiler, line_profiler以及memory_profiler等。他们以不同的方式帮助我们分析Python代码的性能。我们这里主要关注Python内置的cProfiler,并使用它帮助我们分析并优化程序。
cProfiler
快速使用
这里我先拿上官方文档的一个简单例子来对cProfiler的简单使用进行简单介绍。
Python
1
2
3
importcProfile
importre
cProfile.run('re.compile("foo|bar")')
分析结果:
Shell
1
2
3
4
5
6
7
8
9
10
197functioncalls(192primitive calls)in0.002seconds
Ordered by:standard name
ncalls tottime percall cumtime percall filename:lineno(function)
10.0000.0000.0010.001:1()
10.0000.0000.0010.001re.py:212(compile)
10.0000.0000.0010.001re.py:268(_compile)
10.0000.0000.0000.000sre_compile.py:172(_compile_charset)
10.0000.0000.0000.000sre_compile.py:201(_optimize_charset)
40.0000.0000.0000.000sre_compile.py:25(_identityfunction)
3/10.0000.0000.0000.000sre_compile.py:33(_compile)
从分析报告结果中我们可以得到很多信息:
整个过程一共有197个函数调用被监控,其中192个是原生调用(即不涉及递归调用)
总共执行的时间为0.002秒
结果列表中是按照标准名称进行排序,也就是按照字符串的打印方式(数字也当作字符串)
在列表中:
ncalls表示函数调用的次数(有两个数值表示有递归调用,总调用次数/原生调用次数)
tottime是函数内部调用时间(不包括他自己调用的其他函数的时间)
percall等于 tottime/ncalls
cumtime累积调用时间,与tottime相反,它包含了自己内部调用函数的时间
最后一列,文件名,行号,函数名
优雅的使用
Python给我们提供了很多接口方便我们能够灵活的进行性能分析,其中主要包含两个类cProfile模块的Profile类和pstat模块的Stats类。
我们可以通过这两个类来将代码分析的功能进行封装以便在项目的其他地方能够灵活重复的使用进行分析。
这里还是需要对Profile以及Stats的几个常用接口进行简单总结:
Profile类:
enable(): 开始收集性能分析数据
disable(): 停止收集性能分析数据
create_stats(): 停止收集分析数据,并为已收集的数据创建stats对象
print_stats(): 创建stats对象并打印分析结果
dump_stats(filename): 把当前性能分析的结果写入文件(二进制格式)
runcall(func, *args, **kwargs): 收集被调用函数func的性能分析数据Stats类
pstats模块提供的Stats类可以帮助我们读取和操作stats文件(二进制格式)
Python
1
2
importpstats
p=pstats.Stats('stats.prof')
Stats类可以接受stats文件名,也可以直接接受cProfile.Profile对象作为数据源。
strip_dirs(): 删除报告中所有函数文件名的路径信息
dump_stats(filename): 把stats中的分析数据写入文件(效果同cProfile.Profile.dump_stats())
sort_stats(*keys): 对报告列表进行排序,函数会依次按照传入的参数排序,关键词包括calls, cumtime等,具体参数参见https://docs.python.org/2/library/profile.html#pstats.Stats.sort_stats
reverse_order(): 逆反当前的排序
print_stats(*restrictions): 把信息打印到标准输出。*restrictions用于控制打印结果的形式, 例如(10, 1.0, ".*.py.*")表示打印所有py文件的信息的前10行结果。
分析数据可视化
gprof2dot
Gprof2Dot可将多种Profiler的数据转成Graphviz可处理的图像表述。配合dot命令,即可得到不同函数所消耗的时间分析图。具体使用方法详见: https://github.com/jrfonseca/gprof2dot
因此我们可以利用它来为我们的程序生成分析图:
Shell
1
gprof2dot-fpstats mkm_run.prof|dot-Tpng-omkm_run.png
于是我们路径下面就生成了mkm_run.png
我倒是蛮喜欢这个时间分析图,顺着浅色方格的看下去很容易发现程序的瓶颈部分,
每个node的信息如下:
Python
1
2
3
4
5
+------------------------------+
|function name|
|total time%(selftime%)|
|total calls|
+------------------------------+
每个edge的信息如下:
Python
1
2
3
total time%
calls
parent-------------------->children