profile和line_profiler两个模块都是性能分析工具。有时候需要找到代码中运行速度较慢处或瓶颈,可以通过这两模块实现,而不再使用time计时。
line_profiler模块可以记录每行代码的运行时间和耗时百分比。
memory_profiler模块用来监控进程,记录每行代码的内存使用状况。
profile是python自带性能分析模块。line_profiler和memory_profiler模块都需要自行安装。
profile
常用方法
profile.run(statement, filename=None, sort=-1)
#filename用来设置输出文件,默认直接打印
输出结果的各项含义:
名称 | 含义 |
---|---|
ncalls | 函数被调用次数 |
tottime | 函数(除去子函数)总运行时间 |
percall | 平均时间,等于tottime/ncalls |
cumtime | 函数(包括子函数)总运行时间 |
percall | 平均时间,等于cumtime/ncalls |
filename | 文件:行号(函数) |
基于上述信息可以分析代码性能,通过filename可以知道哪个文件第几行的啥函数运行了多少次,运行时间多少。
示例
import profile
a=[[1,5],[2,3],[6,6],[0,0]]
profile.run('sorted(a,key=lambda x:x[1])')
'''
9 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(exec)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 :0(sorted)
4 0.000 0.000 0.000 0.000 <string>:1(<lambda>)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.000 0.000 profile:0(sorted(a,key=lambda x:x[1]))
'''
#通过上述结果,可以看出string中第1行的lambda函数运行了四次
也可以先生成Profiler实例对象,再run:
p=profile.Profile()
p.run('sum') #<profile.Profile at 0x7f01ca552c18>
p.print_stats() #将性能打印出来
line_profiler
常用方法
该模块常通过装饰器来使用。在安装好该模块后,编写需要测试的python程序,并在需要测试的函数上方加入@profile
,保存为*.py文件。在命令行使用kernprof -l -v *.py
,在输出打印出程序结果之后会打印性能分析。
输出结果的含义:
名称 | 意义 |
---|---|
Timer unit | 计时器单位,微秒 |
Total time | 测试代码总运行时间 |
File | 测试代码文件名 |
Hits | 每行代码运行次数 |
Time | 每行代码运行时间 |
Per Hit | 每行代码运行一次的时间 |
% Time | 每行代码运行时间的百分比 |
Line Contents | 每行代码 |
示例
将下列代码保存为test.py,不需要导入line_profiler模块。
@profile
def compute():
for i in range(100):
print(i**3)
if __name__=='__main__':
compute()
在命令行运行kernprof -l -v test.py
,注意如果test.py不在命令行当前文件夹,需要些写上路径。输出为:
'''
Wrote profile results to test.py.lprof
Timer unit: 1e-06 s
Total time: 0.012405 s
File: test.py
Function: compute at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 @profile
2 def compute():
3 101 74.0 0.7 0.6 for i in range(100):
4 100 12331.0 123.3 99.4 print(i**3)
'''
#可以分析代码性能,比如 print(i**3)共执行了100次,平均每次运行时间123.3微秒。
memory_profiler
常用方法
使用方法同line_profiler模块,借助装饰器调用。在需要分析的函数上方加@profile
。在命令行使用python -m memory_profiler *.py
运行代码。
输出结果中:
Mem usage:内存使用情况
Increment:每行代码运行后内存增减情况
还有一种调用该模块的方式,在测试代码中导入memory_profiler模块的profile方法,如下:
from memory_profiler import profile
其余编写方法同上,装饰器的参数如下:
@profile(func=None, stream=None, precision=1, backend='psutil')
#stream表示将分析结果输出的文件,默认为命令行;precision表示分析结果中数字小数点保留位数。
这种方法在命令行只需运行python *.py
即可,若不需要测试性能,可直接将装饰器注释掉。
示例
将下列代码保存为test.py,不需要导入line_profiler模块。
from memory_profiler import profile
@profile(precision=2)
def compute():
a=[]
b=3.4
for i in range(10):
a.append(i)
del b
if __name__=='__main__':
compute()
在命令行运行python test.py
,注意路径。若代码中没有导入memory_profiler模块的profile方法,则应该运行python -m memory_profiler test.py
。输出为:
'''
Line # Mem usage Increment Line Contents
================================================
2 39.08 MiB 39.08 MiB @profile(precision=2)
3 def compute():
4 39.08 MiB 0.00 MiB a=[10]*100
5 39.08 MiB 0.00 MiB b=3.4
6 39.08 MiB 0.00 MiB for i in range(10):
7 39.08 MiB 0.00 MiB a.append(i)
8 39.08 MiB 0.00 MiB del b
'''
#输出分析结果的数字保留了2位小数