分析所写程序性能是写代码的基本功。我们通常会通过设置起始时间与结束时间点来监测代码片段运行耗时。但是这种naive的方法需要修改源代码,会带来额外的工作量。今天主要介绍几个性能分析工具,及其实践技巧。
以我们熟悉的两种排序算法作为讲解例子,来看看几种工具的使用方法。
import numpy as np
#冒泡
def bubble_sort(data):
leng=len(data)
for i in range(leng):
for j in range(leng-i-1):
if data[j]>data[j+1]:
temp=data[j]
data[j]=data[j+1]
data[j+1]=temp
return data
#快排
def quick_sort(data):
if len(data)<=1:
return data
pivot=data[int(len(data)/2)]
left=[x for x in data if x<pivot]
right=[x for x in data if x>pivot]
pivot_=[x for x in data if x==pivot]
return quick_sort(left)+pivot_+quick_sort(right)
time
time模块可以直接调用查看函数运行用时
b=list(np.random.rand(1000))
%time t=quick_sort(b)
CPU times: user 17.1 ms, sys: 97 µs, total: 17.2 ms
Wall time: 16.3 ms
%time t=bubble_sort(b)
CPU times: user 85 ms, sys: 0 ns, total: 85 ms
Wall time: 84.3 ms
time 输出的well time 是实际耗时。
timeit
%timeit -n 100 -r 5 quick_sort(b)
1.28 ms ± 145 µs per loop (mean ± std. dev. of 5 runs, 100 loops each)
%timeit -n 100 -r 5 bubble_sort(b)
29.3 ms ± 291 µs per loop (mean ± std. dev. of 5 runs, 100 loops each)
上述命令重复跑了100遍,每遍跑函数5次,返回每一遍对应平均耗时.
但是为什么使用%time
每一遍对应耗时跟%timeit
对应耗时相差如此巨大?
line_profiler
上述性能分析方法只能显示整个函数的耗时,为了知道函数中具体每行代码耗时情况,我们可以使用line_profiler
一般来说,使用line_profiler
需要先在待测函数上添加@profile
装饰器,但是在jupyterlab或者ipython交互环境中,首先使用
%load_ext line_profiler
引入line_profiler
模块,然后使用命令
%lprun -f your_function your_function()
来输出每行具体运行性能,这样就比加装饰器更加方便了。
%load_ext line_profiler
%lprun -f bubble_sort bubble_sort(b)
Timer unit: 1e-06 s
Total time: 0.258086 s
File: <ipython-input-2-5f5f09499d34>
Function: bubble_sort at line 2
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 def bubble_sort(data):
3 1 5.0 5.0 0.0 leng=len(data)
4 1001 244.0 0.2 0.1 for i in range(leng):
5 500500 109484.0 0.2 42.4 for j in range(leng-i-1):
6 499500 148353.0 0.3 57.5 if data[j]>data[j+1]:
7 temp=data[j]
8 data[j]=data[j+1]
9 data[j+1]=temp
10 1 0.0 0.0 0.0 return data
%lprun -f quick_sort quick_sort(b)
Timer unit: 1e-06 s
Total time: 0.022512 s
File: <ipython-input-3-63a5655acfad>
Function: quick_sort at line 2
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 def quick_sort(data):
3 1023 2376.0 2.3 10.6 if len(data)<=1:
4 512 825.0 1.6 3.7 return data
5 511 1424.0 2.8 6.3 pivot=data[int(len(data)/2)]
6 511 6093.0 11.9 27.1 left=[x for x in data if x<pivot]
7 511 5259.0 10.3 23.4 right=[x for x in data if x>pivot]
8 511 5448.0 10.7 24.2 pivot_=[x for x in data if x==pivot]
9 511 1087.0 2.1 4.8 return quick_sort(left)+pivot_+quick_sort(right)
参数解释:
- Hits:当前行运行次数
- Time:该行总共运行耗时
- Per Hit: 平均每次运行耗时
- % Time: 当前行运行耗时对总耗时占比
工作原理
%lprun
一些关键字
- -f:待测函数,输入格式是
your_func your_func(args)
- -T:save the profile report as a file