Python常用的性能分析(Profiling)方法

本文以一个通过采样求圆周率pi的脚本,介绍一些常用的对Python脚本进行性能分析的方法,针对cpu-bound的问题,找到限制执行速度的瓶颈,为下一步的代码优化做准备

  • 最简单但是无法得到代码细节信息的方法
    • 在IPython中使用%timeit进行计时
      Python常用的性能分析(Profiling)方法_第1张图片
    • 在脚本中使用time模块或者计时功能的装饰器
# 使用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 

得到如下结果
Python常用的性能分析(Profiling)方法_第2张图片
说明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)

Python常用的性能分析(Profiling)方法_第3张图片

  • 也可以在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

可以得到该函数中某一行代码的耗时情况以及执行次数,如下图所示
Python常用的性能分析(Profiling)方法_第4张图片

你可能感兴趣的:(Python)