Python性能分析
python标准库提供两个代码性能分析相关的模块,即timeit和cProfile/profile。前者更适合测试简短的代码片段,后者则可分析代码片段乃至整体模块中各个函数的调用次数、运行耗时等信息。
cProfile是profile的C版本,开销更小。基于cProfile模块,可方便地评估程序性能瓶颈(bottleneck),借以发现程序中值得优化的短板。
根据粒度不同,可将cProfile使用场景分为三类。
1.1分析单条语句
importcProfile,pstats,re,cStringIO
cProfile.run('re.compile("foo|bar")','prfRes')#将cProfile的结果写入prfRes文件
p=pstats.Stats('prfRes')#pstats读取cProfile输出结果
#strip_dirs()剥除模块名的无关路径(如C:\Python27\lib\)
#sort_stats('cumtime')或sort_stats('cumulative')按照cumtime对打印项排序
#print_stats(n)打印输出前10行统计项(不指定n则打印所有项)
p.strip_dirs().sort_stats('cumtime').print_stats(5)
pstats模块可用多种方式对cProfile性能分析结果进行排序并输出。运行结果如下:
TueMay2413:56:072016prfRes
195functioncalls(190primitivecalls)in0.001seconds
Orderedby:cumulativetime
Listreducedfrom33to5duetorestriction
ncallstottimepercallcumtimepercallfilename:lineno(function)
10.0000.0000.0010.001:1()
10.0000.0000.0010.001re.py:192(compile)
10.0000.0000.0010.001re.py:230(_compile)
10.0000.0000.0010.001sre_compile.py:567(compile)
10.0000.0000.0000.000sre_compile.py:552(_code)
其中,tottime表示某函数的总计运行时间(不含该函数内调用的子函数运行时间),cumtime表示某函数及其调用的子函数的累积运行时间。
1.2分析代码片段
pr=cProfile.Profile()
pr.enable()#以下为待分析代码段
regMatch=re.match('^([^/]*)/(/|\*)+(.*)$','//*suspicious')
printregMatch.groups()
pr.disable()#以上为待分析代码段
s=cStringIO.StringIO()
pstats.Stats(pr,stream=s).sort_stats('cumulative').print_stats(10)
prints.getvalue()
运行结果如下:
('','*','suspicious')
536functioncalls(512primitivecalls)in0.011seconds
Orderedby:cumulativetime
Listreducedfrom78to10duetorestriction
ncallstottimepercallcumtimepercallfilename:lineno(function)
20.0000.0000.0090.005C:\Python27\lib\idlelib\PyShell.py:1343(write)
20.0000.0000.0090.005C:\Python27\lib\idlelib\rpc.py:591(__call__)
20.0000.0000.0090.005C:\Python27\lib\idlelib\rpc.py:208(remotecall)
20.0000.0000.0090.004C:\Python27\lib\idlelib\rpc.py:238(asyncreturn)
20.0000.0000.0090.004C:\Python27\lib\idlelib\rpc.py:279(getresponse)
20.0000.0000.0090.004C:\Python27\lib\idlelib\rpc.py:295(_getresponse)
20.0000.0000.0090.004C:\Python27\lib\threading.py:309(wait)
80.0090.0010.0090.001{method'acquire'of'thread.lock'objects}
10.0000.0000.0020.002C:\Python27\lib\re.py:138(match)
10.0000.0000.0020.002C:\Python27\lib\re.py:230(_compile)
1.3分析整个模块
使用命令行,调用cProfile脚本分析其他脚本文件。命令格式为:
python-mcProfile[-ooutput_file][-ssort_order]myscript.py
注意,-o和-s选项不可同时使用。
以C代码统计工具为例,运行如下命令:
E:\PyTest>python-mcProfile-stottimeCLineCounter.pysource-d-b>out.txt
截取out.txt文件部分内容如下:
250316245433620.25xtm_mgr.c
1408729374932093169380.26
762068functioncalls(762004primitivecalls)in2.967seconds
Orderedby:internaltime
ncallstottimepercallcumtimepercallfilename:lineno(function)
820.9850.0122.8690.035CLineCounter.py:11(CalcLines)
1176400.6120.0001.3150.000re.py:138(match)
1176500.3810.0000.3810.000{method'match'of'_sre.SRE_Pattern'objects}
1176550.3190.0000.3240.000re.py:230(_compile)
1380500.1980.0000.1980.000{method'isspace'of'str'objects}
1058230.1650.0000.1650.000{method'strip'of'str'objects}
123156/1231410.1540.0000.1540.000{len}
378870.0550.0000.0550.000{method'group'of'_sre.SRE_Match'objects}
820.0410.0000.0410.000{method'readlines'of'file'objects}
820.0160.0000.0160.000{open}
10.0040.0042.9502.950CLineCounter.py:154(CountDir)
由tottime可见,此处的性能瓶颈在于CalcLines()函数和其中的正则表达式处理。而isspace()和strip()方法及len()函数因调用次数较多,总的耗时也颇为可观。
作为对比,以下给出一种未使用正则表达式的统计实现:
defCalcLines(line,isBlockComment):
lineType,lineLen=0,len(line)
line=line+'\n'#添加一个字符防止iChar+1时越界
iChar,isLineComment=0,False
whileiChar
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!