Python的性能分析分析十分重要,因为可以给我们优化性能提供可靠的数值参考
Python的性能分析有两个主流工具cProfile和line_profiler,前者是python内置的工具,不过不是分析报告特别详细
下面试一下cProfile:
import profile
class cached:
def __init__(self, fn):
self.fn = fn
self.cache = {}
def __call__(self, *args):
try:
return self.cache[args]
except KeyError:
self.cache[args] = self.fn(*args)
return self.cache[args]
@cached
def fib(n):
if n <= 1:return n
else:return fib(n-1) + fib(n-2)
def fib_seq(n):
seq = []
if n > 0:
seq.extend(fib_seq(n-1))
seq.append(fib(n))
return seq
if __name__ == '__main__':
#print(fib_seq(20))
profile.run('print(fib_seq(200)); print')
运行结果如下:
11521 function calls (8023 primitive calls) in 0.009 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
2505/5 0.005 0.000 0.008 0.002 78797.py:19(fib_seq)
2500 0.001 0.000 0.001 0.000 {method 'extend' of 'list' objects}
3503/2505 0.001 0.000 0.001 0.000 78797.py:7(__call__)
5 0.001 0.000 0.001 0.000 {built-in method builtins.print}
501 0.000 0.000 0.001 0.000 78797.py:15(fib)
2505 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 cProfile.py:50(create_stats)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Ordered by: cumulative time
Function was called by...
ncalls tottime cumtime
78797.py:19(fib_seq) <- 2500/5 0.005 0.008 78797.py:19(fib_seq)
{method 'extend' of 'list' objects} <- 2500 0.001 0.001 78797.py:19(fib_seq)
78797.py:7(__call__) <- 998 0.000 0.000 78797.py:15(fib)
2505 0.001 0.001 78797.py:19(fib_seq)
{built-in method builtins.print} <-
78797.py:15(fib) <- 501 0.000 0.001 78797.py:7(__call__)
{method 'append' of 'list' objects} <- 2505 0.000 0.000 78797.py:19(fib_seq)
cProfile.py:50(create_stats) <-
{method 'disable' of '_lsprof.Profiler' objects} <- 1 0.000 0.000 cProfile.py:50(create_stats)
注:只写了报告内容,print的内容没有贴出来
我们可以从结果中看到函数调用的概要
不过没法像line_profiler可以按行给出分析报告
line_profiler的使用之前需要安装:
pip3 install line_profiler
安装成功就可以使用了,在使用之前值得一提的是,kernprof是line_profiler工具,在pip3 install line_profiler结束后已经安装好了,不信我们可以试一下:
root@root:/opt/pypy3.7-v7.3.2-linux64# kernprof
Usage: kernprof [-s setupfile] [-o output_file_path] scriptfile [arg] ...
接下来使用kernprof进行分析,在这之前需要建立一个名为tt.py的python文件,内容如:
@profile
def run():
a = [1]*100
b = [x**3 for x in a ]
c = [x for x in b]
d = c*2
run()
接下来就是见证奇迹的时候了:
kernprof -l -v tt.py
结果:
Timer unit: 1e-06 s
Total time: 4e-05 s
File: tt.py
Function: run at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 @profile
2 def run():
3 1 3.0 3.0 7.5 a = [1]*100
4 1 28.0 28.0 70.0 b = [x**3 for x in a ]
5 1 8.0 8.0 20.0 c = [x for x in b]
6 1 1.0 1.0 2.5 d = c*2
同时会在当前路径下面 生成一个名为.lprof的文件
这个文件可以理解成刚才运行的报告的持久化的文件,可以读出来的:
root@root:/opt/pypy3.7-v7.3.2-linux64# python3 -m line_profiler tt.py.lprof
Timer unit: 1e-06 s
Total time: 4e-05 s
File: tt.py
Function: run at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 @profile
2 def run():
3 1 3.0 3.0 7.5 a = [1]*100
4 1 28.0 28.0 70.0 b = [x**3 for x in a ]
5 1 8.0 8.0 20.0 c = [x for x in b]
6 1 1.0 1.0 2.5 d = c*2
我们可以看到和运行的时候一样,我们发现装饰器@就能进行性能分析,是不是很方便呢