本文以一个通过采样求圆周率pi的脚本,介绍一些常用的对Python脚本进行性能分析的方法,针对cpu-bound的问题,找到限制执行速度的瓶颈,为下一步的代码优化做准备
- 最简单但是无法得到代码细节信息的方法
- 在IPython中使用%timeit进行计时
- 在脚本中使用time模块或者计时功能的装饰器
- 在IPython中使用%timeit进行计时
# 使用time模块
import numpy as np
import time
def estimate_pi(n_estimator):
count = 0
for i in range(n_estimator):
x = np.random.rand()
y = np.random.rand()
if x**2 + y**2 <= 1:
count += 1
return count / n_estimator
start_t = time.time()
pi = estimate_pi(1000000)
end_t = time.time()
print('time cost : %f'%(end_t - start_t))
# 使用装饰器
import numpy as np
import time
def time_wrapper(fn):
def inner(*args, **kwargs):
start_t = time.time()
result = fn(*args, **kwargs)
end_t = time.time()
print('time cost : %f'%(end_t - start_t))
return result
return inner
@time_wrapper
def estimate_pi(n_estimator):
count = 0
for i in range(n_estimator):
x = np.random.rand()
y = np.random.rand()
if x**2 + y**2 <= 1:
count += 1
return count / n_estimator
pi = estimate_pi(1000000)
- 使用IPython %run -p 的命令得到运行过程中每一个函数的耗时
# 对pi值进行估计
# test.py
import numpy as np
def estimate_pi(n_estimator):
count = 0
for i in range(n_estimator):
x = np.random.rand()
y = np.random.rand()
if x**2 + y**2 <= 1:
count += 1
return count / n_estimator
if __name__ == '__main__':
pi = estimate_pi(1000000)
# IPython
%run -p test.py
得到如下结果
说明estimate_pi这个函数的调用一共用了3.07s,其中1.265s花费在numpy的rand函数的调用上,剩下1.805s是花费在其他一些类似循环中的数值计算上面
- 使用IPython %prun 的命令也可以得到运行过程中每一个函数的耗时,但是和%run -p 不同的是,%prun后面直接接命令,%run -p后面接脚本
# IPython
# -l 3 表示只显示前3行的
# 也可以加上 -s 的选项对结果按照指定的选项进行排序输出
%prun -l 3 estimate_pi(1000000)
- 也可以在cmd中使用如下命令得到与%prun和%run相似的结果
# cmd
# 也可以加上一些 -s 的选项对输出结果进行定制
python -m cProfile test.py
上面的方法是对每个函数的耗时进行分析,下面介绍一种对函数中每行代码的耗时进行分析的方法,所以建议先进行每个函数的耗时分析,然后针对性的逐行分析
- 使用line_profiler工具,windows下安装建议下载.whl进行安装,在想要分析的函数的定义处,加上@profile装饰器
# 对pi值进行估计
import numpy as np
@profile
def estimate_pi(n_estimator):
count = 0
for i in range(n_estimator):
x = np.random.rand()
y = np.random.rand()
if x**2 + y**2 <= 1:
count += 1
return count / n_estimator
if __name__ == '__main__':
pi = estimate_pi(1000000)
# cmd
kernprof -l -v test.py
可以得到该函数中某一行代码的耗时情况以及执行次数,如下图所示