【读书笔记】 如何进行Python性能分析

文章目录

  • Python解释器
  • 性能分析方法
    • 1、time计时
    • 2、标准库内建分析工具
    • 3、逐行分析
    • 4、 诊断内存
    • 其他建议
  • 参考目录

Python解释器

python 解释器有很多:CPython、IPython、Jython、PyPy,这些解释器由编译器和虚拟机组成。

虚拟机可以让python的编程者无需关注底层实现(比如要如何为数组分配内存、如何组织内存以及用什么样的顺序将内存传入 CPU),好处是可以直接快速设计出更高层的业务逻辑和算法,缺陷则是要付出性能损失的代价。但python指令层本身就存在优化,所以更好的利用这一层优化(即使用正确的指令顺序),也就可以提高你写的python性能。

其次,python的GIL(全局解释器锁)会影响程序在并行方面的性能。GIL是CPython中的一个概念,它通过计数的方式进行内存管理,实现了一个互斥锁(防止多线程并发执行机器码)。那么在多核CPU上运行时python时,实际用到的可能就是一个单核,其他沦为摆设。如下图所示,三个线程都只有等到前一个释放资源后才能继续运行。因此需要使用标准库的 multiprocessing,numexpr,或分布式计算模型等方法来解决。

GIL 确保 Python 进程一次只能执行一条指令,无论当前有多少个核心。 这意味着即使某些 Python代码可以使用多个核心,在任意时间点仅有一个核心在执行 Python 的指令。

【读书笔记】 如何进行Python性能分析_第1张图片

另一方面,Python 使用了动态类型,且 Python 也并不是一门编译性的
语言。由于代码在运行过程中会发生改变,那么也没办法在编译器层面对代码进行优化。但使用Just-In-Time(JIT)技术可以改善这一问题,实现加速。例如CPython中将python代码注释为C语言类型,还有微软的Pyston等。

性能分析方法

1、time计时

time计时

import time

start_time = time.time()
for i in range(1,1000000):
    x = i*i
end_time = time.time()

print("Spend:{:.4f}".format(end_time-start_time) )

结果:

Spend:0.0618	

装饰器

可以定义一个装饰器来自动测量时间:

from functools import wraps 
def timefn(fn): 
 @wraps(fn) 
 def measure_time(*args, **kwargs): 
 	t1 = time.time() 

使用装饰器:

@timefn 
def calculate_z_serial_purepython(maxiter, zs, cs): 

timeit模块
该模块会禁用垃圾回收机制。 命令行中使用-m timeit的方式就可以调用
指定-n 循环次数和-r 重复次数,如果不指定则默认为n=10,r=5。

python -m timeit -n 5 -r 5 -s "import julia1" "julia1.calc_pure_python(desired_width=1000, 
 max_iterations=300)"

UNIX 的 time
调用python脚本时,命令行前加上 /usr/bin/time -p,使用系统的time。但只能在类UNIX系统下使用。

python脚本:test.py

	import time
	start_time = time.time()
	for i in range(1,1000000):
	    x = i*i
	end_time = time.time()
	print("Finish test.")

运行:/usr/bin/time --verbose python test.py
【读书笔记】 如何进行Python性能分析_第2张图片

打开--verbose 开关可以获得更多输出信息

2、标准库内建分析工具

总共有三个:

  • hotshot
  • cProfile 模块
  • profile 模块

后两者接口是一致的,实现方法不同。profile是纯python实现,而cProfile用C语言钩入 CPython 的虚拟机来测量其每一个函数运行所花费的时间(代价巨大但信息更丰富)。

例子:

import cProfile
import re
cProfile.run('re.compile("foo|bar")')

打印信息:

197 function calls (192 primitive calls) in 0.002 seconds
Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.001    0.001 :1()
     1    0.000    0.000    0.001    0.001 re.py:212(compile)
     1    0.000    0.000    0.001    0.001 re.py:268(_compile)
     1    0.000    0.000    0.000    0.000 sre_compile.py:172(_compile_charset)
     1    0.000    0.000    0.000    0.000 sre_compile.py:201(_optimize_charset)
     4    0.000    0.000    0.000    0.000 sre_compile.py:25(_identityfunction)
   3/1    0.000    0.000    0.000    0.000 sre_compile.py:33(_compile)

分析工具
为了更好的分析cProfile得到的结果,可以使用这些模块。

  1. runsnakerun可视化工具:
    【读书笔记】 如何进行Python性能分析_第3张图片

  2. ‎‎pstats分析工具:

import pstats
# 创建Stats对象
p = pstats.Stats("result.out")

# strip_dirs(): 去掉无关的路径信息
# sort_stats(): 排序,支持的方式和上述的一致
# print_stats(): 打印分析结果,可以指定打印前几行

# 和直接运行cProfile.run("test()")的结果是一样的
p.strip_dirs().sort_stats(-1).print_stats()

# 按照函数名排序,只打印前3行函数的信息, 参数还可为小数,表示前百分之几的函数信息 
p.strip_dirs().sort_stats("name").print_stats(3)

# 按照运行时间和函数名进行排序
p.strip_dirs().sort_stats("cumulative", "name").print_stats(0.5)

# 如果想知道有哪些函数调用了sum_num
p.print_callers(0.5, "sum_num")

# 查看test()函数中调用了哪些函数
p.print_callees("test")

3、逐行分析

line_profiler可以进行逐行分析
pip或者conda下载line_profiler包以后,用@profile装饰器的方式使用。

用修饰器(@profile)标记选中的函数。用 kernprof.py 脚本运行你的代码,被选函数每一行花费的 CPU 时间以及其他信息就会被记录下来。

命令行中运行 kernprof 逐行分析被修饰函数的 CPU 开销:
kernprof -l -v test.py

运行时参数-l 代表逐行分析而不是逐函数分析,-v 用于显示输出。没有-v,你会
得到一个.lprof 的输出文件,回头你可以用 line_profiler 模块对其进行分
析。例 2-6 中,我们会完整运行一遍我们的 CPU 密集型函数

4、 诊断内存

memory_profiler可以诊断内存的用量,操作与上一个包类似,也要先添加@profile在你需要诊断的函数上方,然后运行:
python -m memory_profiler test.py
再通过mprof功能,将生成的统计文件制作成图。

其他建议

1、如果觉得更改代码,每次都要去添加@profile很麻烦,可以考虑使用no-op 修饰器,避免出现Import Error之类的引用错误。
例如:

# memory_profiler 
if 'profile' not in dir(): 
	 def profile(func): 
		 def inner(*args, **kwargs): 
			 return func(*args, **kwargs) 
	return inner

2、要保证测试机器的稳定,例如在 BIOS 上禁用了 TurboBoost,禁用操作系统改写 SpeedStep,不要用笔记本电池而是使用主电源。
3、多次测试,备份数据。

参考目录

[1] Python高性能编程
[2] Realpython
[3] 一份让Python疯狂加速的工具合集!
[4] python性能分析之cProfile模块
[5]The Python Profilers

你可能感兴趣的:(linux,python,开发语言)