主流的Python性能分析工具,
包括cProfile性能分析器、
line_profiler+kernprof性能分析工具、
KCacheGrind+pyprof2calltree、
RunSnakeRun可视化性能分析工具,
帮助发现程序的性能瓶颈。
性能分析
性能分析是测量程序所使用的资源的。虽然资源正变得越来越便宜。
一旦用户激增,程序通常会崩溃,或者变得异常缓慢。(这显然是由于糟糕的程序设计和缺乏扩展性的架构所造成的。
毕竟一台服务器有限的内存和CPU资源也可能会成为软件的瓶颈。另外就是没做过压力测试)
性能分析:
1、运行时间
2、运行瓶颈:
沉重的IO操作
内存泄露
未经优化的代码被频繁使用
密集的操作在可以缓存时没有缓存,占用了大量资源。
注:I/O关联的代码(文件读/写,数据库查询等)很难有话,因为优化可能会改变程序执行I/O操作的方式(通常是语言的核心操作I/O)。
相反,优化计算关联的代码(比如程序使用的算法很糟糕),改善性能会比较容易(并一定很简单)。这是因为优化计算关联的代码就是改写程序。
在性能优化接近尾声的时候,剩下的大多数性能瓶颈都是由I/O关联的代码造成的。
没有优化过的程序通常会在某些子程序上消耗大部分的CPU指令周期。性能分析就是分析代码和它正在使用的资源之间有着怎样的关系。
(占用了多少CPU时间,或者整个程序消耗了多少内存)
性能分析可以帮助开发者深刻地了解程序是如何使用计算机资源的(细致到一个函数被调用了多少次)
过早优化的风险:如果过早优化,结果可能会和原来的代码截然不同。他可能只是完整解决方案的一部分,还可能包含因优化驱动的涉及决策而导致的错误。
性能分析软件有两类方法论
基于事件的性能分析
通过手机程序执行过程中的具体时间进行工作的。会产生大量数据。
统计式性能分析
基于事件的性能分析

import profile
import sys
def profiler(frame, event, arg):
print ('PROFILER: %r %r' % (event, arg))
sys.setprofile(profiler)
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
def fib_seq(n):
seq = [ ]
if n > 0:
seq.extend(fib_seq(n-1))
seq.append(fib(n))
return seq
print(fib_seq(2))
fib_seq.py

C:\Python35\python.exe "H:/性能分析/B02088_01_01.py"
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'return' 0
PROFILER: 'c_call' <built-in method append of list object at 0x00000000005D7E08>
PROFILER: 'c_return' <built-in method append of list object at 0x00000000005D7E08>
PROFILER: 'return' [0]
PROFILER: 'c_call' <built-in method extend of list object at 0x00000000005D7F08>
PROFILER: 'c_return' <built-in method extend of list object at 0x00000000005D7F08>
PROFILER: 'call' None
PROFILER: 'return' 1
PROFILER: 'c_call' <built-in method append of list object at 0x00000000005D7F08>
PROFILER: 'c_return' <built-in method append of list object at 0x00000000005D7F08>
PROFILER: 'return' [0, 1]
PROFILER: 'c_call' <built-in method extend of list object at 0x00000000005E48C8>
PROFILER: 'c_return' <built-in method extend of list object at 0x00000000005E48C8>
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'return' 1
PROFILER: 'call' None
PROFILER: 'return' 0
PROFILER: 'return' 1
PROFILER: 'c_call' <built-in method append of list object at 0x00000000005E48C8>
PROFILER: 'c_return' <built-in method append of list object at 0x00000000005E48C8>
PROFILER: 'return' [0, 1, 1]
PROFILER: 'c_call' <built-in function print>
[0, 1, 1]
PROFILER: 'c_return' <built-in function print>
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method locked of _thread.lock object at 0x000000000114C170>
PROFILER: 'c_return' <built-in method locked of _thread.lock object at 0x000000000114C170>
PROFILER: 'c_call' <built-in method release of _thread.lock object at 0x000000000114C170>
PROFILER: 'c_return' <built-in method release of _thread.lock object at 0x000000000114C170>
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method locked of _thread.lock object at 0x000000000114C170>
PROFILER: 'c_return' <built-in method locked of _thread.lock object at 0x000000000114C170>
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method values of dict object at 0x00000000011345C8>
PROFILER: 'c_return' <built-in method values of dict object at 0x00000000011345C8>
PROFILER: 'c_call' <built-in method values of dict object at 0x0000000001138188>
PROFILER: 'c_return' <built-in method values of dict object at 0x0000000001138188>
PROFILER: 'return' [<_MainThread(MainThread, stopped 12524)>]
PROFILER: 'call' None
PROFILER: 'return' False
PROFILER: 'call' None
PROFILER: 'return' False
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in function get_ident>
PROFILER: 'c_return' <built-in function get_ident>
PROFILER: 'return' None
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method acquire of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'c_return' <built-in method acquire of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method acquire of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'c_return' <built-in method acquire of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'return' <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
PROFILER: 'call' None
PROFILER: 'return' <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
PROFILER: 'c_call' <built-in function hasattr>
PROFILER: 'c_return' <built-in function hasattr>
PROFILER: 'call' None
PROFILER: 'return' <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
PROFILER: 'c_call' <built-in method flush of _io.TextIOWrapper object at 0x000000000054E708>
PROFILER: 'c_return' <built-in method flush of _io.TextIOWrapper object at 0x000000000054E708>
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method release of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'c_return' <built-in method release of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'return' None
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method acquire of _thread.RLock object at 0x000000000111F378>
PROFILER: 'c_return' <built-in method acquire of _thread.RLock object at 0x000000000111F378>
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method release of _thread.RLock object at 0x000000000111F378>
PROFILER: 'c_return' <built-in method release of _thread.RLock object at 0x000000000111F378>
PROFILER: 'return' None
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method release of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'c_return' <built-in method release of _thread.RLock object at 0x000000000111F3A0>
PROFILER: 'return' None
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method discard of set object at 0x0000000000D2E588>
PROFILER: 'c_return' <built-in method discard of set object at 0x0000000000D2E588>
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method discard of set object at 0x0000000000D27BA8>
PROFILER: 'c_return' <built-in method discard of set object at 0x0000000000D27BA8>
PROFILER: 'return' None
PROFILER: 'call' None
PROFILER: 'c_call' <built-in method discard of set object at 0x00000000010DA9E8>
PROFILER: 'c_return' <built-in method discard of set object at 0x00000000010DA9E8>
PROFILER: 'return' None
输出结果
统计式性能分析
优点:
1、分析的数据更少:由于我们只对程序执行过程进行抽样,而不用保留每一条数据,因此需要分析的信息量会显著减少。
2、对性能造成的影响更小,使用抽样的方式(系统中断),目标的程序的性能遭受的干扰更小;比上一种方式更小。
运行时间复杂度:
O(1)
判断一个数是奇数还是偶数
用标准输出方式打印信息
O(n)
查找无序表中的最小元素
比较两个字符串
删除链表中的最后一项
O(logn)
二分查找
计算斐波那契数列(用矩阵乘法)
O(nlogn)
归并排序
堆排序
快速排序
O(n2)
冒泡排序
遍历二维数组
插入排序
O(n!)
暴力破解搜索方法
效率:
对数>线性>线性对数>平方>阶乘
性能分析实践
1、建立回归测试套件
尤其是面对大型项目时,确保代码具有足够的覆盖率。可以保证你在代码中尝试优化时,不用担心代码结构被破坏。
2、思考代码结构
3、耐心
4、尽可能多的收集数据
系统日志,自定义日志,系统资源快照
5、数据预处理
当拥有了性能分析器的信息、日志和其他资源之后,在分析之前可能需要对数据进行预处理。不要因为性能分析器不能理解就会比非结构化数据。
数据分析会往往从其他数据中受益。
6、数据可视化
如果在错误发生之前,你不清楚自己要找的问题,只想知道优化代码的方式,那么洞察你已经预处理过的数据的最好方式就是数据可视化。
计算机很酣畅处理数据,但是人类更善于通过图形来发现模式和理解现有信息中的某种特征。
例子:
通过可视化图形,显示出客户集中访问的时间,我们可以在高峰时间为基础设施增加更多资源。