1. 性能调优
在代码可以实现功能且健壮不出错的前提下,我们往往会有优化性能的需求
性能调优大约可以在运行时间和运行内存占用两方面来考量,下面介绍的工具定位精度由粗到细,也分为这两个方面
1.1. 测试整体运行时间
Python中的timeit是测试代码执行效率的工具.可以用命令行直接测试脚本,也可以测试代码字符串的效率,当然最简单的还是直接用ipython的内置timeit魔法命令测某段代码的效率
import timeit t = timeit.Timer('map(lambda x: x**2,range(1000))') t.timeit()
0.404256040987093
!python -m timeit -s "map(lambda x: x**2,range(1000))"
100000000 loops, best of 3: 0.00833 usec per loop
1.2. 函数级性能瓶颈定位
python的标准库中有一个可以实现性能瓶颈定位的模块叫cprofile,他是一个开销极小的C扩展.用它可以实现函数级的性能分析,配合pstats
模块还可以输出分析报告
1.2.1. 使用单独模块分析
%%writefile src/C3/profile_test.py def foo(): sum = 0 for i in range(10000): sum += i return sum if __name__=="__main__": foo()
Overwriting src/C3/profile_test.py
%%writefile src/C3/profile_test.py def foo(): sum = 0 for i in range(10000): sum += i return sum if __name__=="__main__": try : import profile except: import cProfile as profile profile.run("foo()")
Overwriting src/C3/profile_test.py
!python src/C3/profile_test.py
5 function 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 :0(exec) 1 0.001 0.001 0.001 0.001 :0(setprofile) 1 0.000 0.000 0.001 0.001 <string>:1(<module>) 1 0.000 0.000 0.002 0.002 profile:0(foo()) 0 0.000 0.000 profile:0(profiler) 1 0.001 0.001 0.001 0.001 profile_test.py:1(foo)
1.2.2. 使用命令行分析
0.404256040987093
0
0.404256040987093
1
0.404256040987093
2
0.404256040987093
3
1.2.3. 统计项说明
统计项 | 说明 |
---|---|
ncalls | 函数被调用次数 |
tottime | 函数总计运行时间,不含调用函数运行时间 |
cumtime | 函数总计运行时间,含调用的函数运行时间 |
percall | 函数运行一次平均时间,等于tottime(cumtime)/ncalls |
filename:lineno | 函数所在文件名,函数的行号,函数名 |
1.2.4. 与pstats结合提供多种形式的报表
0.404256040987093
4
0.404256040987093
5
0.404256040987093
6
0.404256040987093
7
stats有许多函数,可以提供不同的报表
- stats函数说明
函数 | 说明 |
---|---|
strip_dirs() | 除去文件名前名的路径信息 |
add(filename,[...]) | 把profile输出的文件加入stats实例中统计 |
dump_stats(filename) | 把stats统计结果保存到文件 |
sort_stats(key,[...]) | 最重要的,可以给profile统计结果排序 |
reverse_order() | 数据反排序 |
print_stats([restriction,...]) | 把报表输出到stdout |
print_callers([restriction,...]) | 输出调用指定函数的相关信息 |
print_callees([restriction,...]) | 输出指定函数调用过的函数的相关信息 |
- sort_stats可接受的参数
参数 | 说明 |
---|---|
ncalls | 被调次数 |
cumulative | 函数运行总时间 |
file | 文件名 |
module | 模块名 |
pcalls | 简单统计 |
line | 行号 |
name | 函数名 |
nfl | name,file,line |
stdname | 标准函数名 |
time | 函数内部运行时间 |
1.3. 语句级性能瓶颈定位
cprofiler只能追踪到哪个函数是性能瓶颈,而函数中哪条语句是性能瓶颈就追踪不到了,对于语句级性能瓶颈定位,python并没有官方工具,但github上有位大神制作了line_profiler,这个工具可以实现这一功能,它也几乎可以说是python的半标准工具之一了.
因为不是标准库中的内容,所以需要pip安装.
使用方法十分简单,在需要分析的函数上面加上装饰器@profile
即可(注意不用import任何东西,这条装饰器在定位好后应该删除以保证代码可以运行)
0.404256040987093
8
0.404256040987093
9
!python -m timeit -s "map(lambda x: x**2,range(1000))"
0
!python -m timeit -s "map(lambda x: x**2,range(1000))"
1
1.4. 内存分析
memory_profiler是用来分析内存使用情况和追踪内存泄露的工具.它用法比较接近line_profiler
由于不是标准库中的模块,它需要pip安装.
需要注意的是windows下需要在script文件夹下将mprof
文件改名为mprof.py
并在同一目录下创建一个mprof.bat
文件编辑为如下内容
!python -m timeit -s "map(lambda x: x**2,range(1000))"
2
它的使用及其简单:
!python -m timeit -s "map(lambda x: x**2,range(1000))"
3
!python -m timeit -s "map(lambda x: x**2,range(1000))"
4
之后使用
!python -m timeit -s "map(lambda x: x**2,range(1000))"
5
就可以看到详细结果了
指定精度可以在profile装饰器后面加上参数 如: @profile(precision=4)
mprof工具类似kernprof
,用它可以输出更加友好的统计分析页面
!python -m timeit -s "map(lambda x: x**2,range(1000))"
6
!python -m timeit -s "map(lambda x: x**2,range(1000))"
7
!python -m timeit -s "map(lambda x: x**2,range(1000))"
8
!python -m timeit -s "map(lambda x: x**2,range(1000))"
9
100000000 loops, best of 3: 0.00833 usec per loop
0
100000000 loops, best of 3: 0.00833 usec per loop
1
1.5. 对象分析及追踪(windows下不能用)
Objgraph可以实现对象分析和追踪,它也是用pip安装,不过它依赖xdot(pip 安装) 和graphviz(brew安装)
它可以实现的功能有:
- 统计
- 定义过滤对象
- 遍历和显示对象图
100000000 loops, best of 3: 0.00833 usec per loop
2
100000000 loops, best of 3: 0.00833 usec per loop
3
100000000 loops, best of 3: 0.00833 usec per loop
4
100000000 loops, best of 3: 0.00833 usec per loop
5
于是你可以看到图了
还没有评论,来说两句吧...