python標(biāo)準(zhǔn)庫(kù)提供的cProfile/profile模塊,計(jì)時(shí)輸出信息較多。本節(jié)將介紹其他幾種精度略低但簡(jiǎn)單易用的計(jì)時(shí)工具。根據(jù)代碼粒度不同,將其分為三類。
1.1整個(gè)程序計(jì)時(shí)
Unix/Linux系統(tǒng)中,可用time命令簡(jiǎn)單地統(tǒng)計(jì)整個(gè)程序的耗時(shí)。例如:
[wangxiaoyuan_@localhostPyTest]$timepythonBCLineCounter.pybulk
FileLinesCodeLinesCommentLinesEmptyLinesCommentPercent
1545010437326425380.24
real0m2.803s
user0m1.124s
sys0m0.052s
統(tǒng)計(jì)值real表示程序運(yùn)行的實(shí)際耗時(shí),user表示程序執(zhí)行用戶態(tài)代碼(內(nèi)核外)耗費(fèi)的CPU時(shí)間,sys表示程序在內(nèi)核態(tài)運(yùn)行所耗費(fèi)的CPU時(shí)間(即調(diào)用特定內(nèi)核函數(shù)的耗時(shí))。若user和sys時(shí)間之和小于real時(shí)間,表明程序?yàn)镮/O密集型(I/Obound),即程序的性能問(wèn)題很可能與等待I/O有關(guān)。
time命令的詳細(xì)描述參見(jiàn)《Linux用戶態(tài)程序計(jì)時(shí)方式詳解》。
1.2代碼片段計(jì)時(shí)
代碼片段計(jì)時(shí)分為函數(shù)計(jì)時(shí)和語(yǔ)句塊計(jì)時(shí)。這兩種計(jì)時(shí)均可使用Python標(biāo)準(zhǔn)庫(kù)timeit模塊,該模塊的詳細(xì)介紹參見(jiàn)官方幫助。
本小節(jié)將使用timeit模塊的timeit()方法,即timeit(stmt='pass',setup='pass',timer=,number=1000000)。其中,參數(shù)stmt為待計(jì)時(shí)的目標(biāo)代碼;setup為執(zhí)行代碼的準(zhǔn)備工作(通常是import之類的語(yǔ)句),不計(jì)入時(shí)間;timer在Windows系統(tǒng)中為time.clock(),Linux系統(tǒng)中則為time.time(),取默認(rèn)值即可;number指示stmt重復(fù)執(zhí)行的次數(shù)。該方法返回執(zhí)行stmt代碼number遍所用的時(shí)間,單位為秒,float類型。
除timeit()方法外,對(duì)于特定函數(shù)的計(jì)時(shí),可使用裝飾器(decorator);對(duì)于語(yǔ)句塊計(jì)時(shí),則可使用上下文管理器(contextmanager)。
以裝飾器為例:
importfunctools,sys,time
defFuncTimer(repeats=10000):
defdecorator(func):
@functools.wraps(func)
defwrapper(*args,**kwargs):
#Windows系統(tǒng)中clock()粒度為毫秒,time()粒度為1/60秒;
#Unix系統(tǒng)中clock()粒度為1/100秒,time()精度較其更高。
ifsys.platform=="win32":
timerFunc=time.clock
else:
timerFunc=time.time
try:
startTime=timerFunc()
foriinrange(repeats):
ret=func(*args,**kwargs)
finally:#當(dāng)目標(biāo)函數(shù)發(fā)生異常時(shí),仍舊輸出計(jì)時(shí)信息
endTime=timerFunc()
print'%s.%s()=>'%(func.__module__,func.__name__),
print'TimeElasped:%.3fmsec,repeated%dtime(s).'\
%(((endTime-startTime)*1000.0),repeats)
returnret
returnwrapper
returndecorator
運(yùn)行如下代碼,對(duì)比自定義裝飾器FuncTimer與timeit模塊計(jì)時(shí)效果:
@FuncTimer(10)
defDecoratedFunc():
L=[]
foriinrange(100):L.append(i)
defRawFunc():
L=[]
foriinrange(100):L.append(i)
DecoratedFunc()
importtimeit;print'%.6fsec'%timeit.timeit(stmt=RawFunc,number=10)
輸出如下:
__main__.DecoratedFunc()=>TimeElasped:0.164msec,repeated10time(s).
0.000174sec
可見(jiàn),計(jì)時(shí)效果非常接近。
注意,F(xiàn)uncTimer裝飾器內(nèi)根據(jù)系統(tǒng)選用不同的計(jì)時(shí)器,這是考慮到time.clock()的精度因系統(tǒng)平臺(tái)而異。在Unix/Linux系統(tǒng)中,該方法返回當(dāng)前所耗的CPU時(shí)間;而在Windows系統(tǒng)中,該方法基于Win32函數(shù)QueryPerformanceCounter(),返回從首次調(diào)用待計(jì)時(shí)函數(shù)起所經(jīng)歷的掛鐘時(shí)間(wallclocktime),精度較time.time()更高。相比而言,timeit方法中使用的缺省計(jì)時(shí)器總是測(cè)量掛鐘時(shí)間,這也意味著關(guān)于某函數(shù)的計(jì)時(shí)可能會(huì)受到同一計(jì)算機(jī)上運(yùn)行的其他進(jìn)程的影響。
time.clock()計(jì)時(shí)器的平臺(tái)差異性參考以下示例(假定所在腳本名為Timing.py):
@FuncTimer(5)
defSqrtTiming(loops):
importmath
try:
frommathimportfsum#Python2.6+
returnfsum([math.sqrt(x)forxinrange(loops)])
exceptImportError:#Python2.5-
returnsum([math.sqrt(x)forxinrange(loops)])
@FuncTimer(1)
defSleepTiming():
time.sleep(2)
file=open(r'out.txt',"w+")
foriinrange(10000):
file.write('helloworld!')
SqrtTiming(100000)
SleepTiming()
在Windows系統(tǒng)控制臺(tái)和IDLEShell里的運(yùn)行結(jié)果如下:
E:\PyTest>Timing.py
SqrtTiming()=>TimeElasped:150.124msec,repeated5time(s).
SleepTiming()=>TimeElasped:2155.140msec,repeated1time(s).
__main__.SqrtTiming()=>TimeElasped:151.809msec,repeated5time(s).
__main__.SleepTiming()=>TimeElasped:2185.594msec,repeated1time(s).
>>>importTiming
Timing.SqrtTiming()=>TimeElasped:148.892msec,repeated5time(s).
Timing.SleepTiming()=>TimeElasped:2223.157msec,repeated1time(s).
在Linux系統(tǒng)中運(yùn)行結(jié)果與之類似。若將timerFunc改為time.clock(),則計(jì)時(shí)輸出為:
[wangxiaoyuan_@localhost~]$timepythonTiming.py
__main__.SqrtTiming()=>TimeElasped:320.000msec,repeated5time(s).
__main__.SleepTiming()=>TimeElasped:330.000msec,repeated1time(s).
real0m2.381s
user0m0.332s
sys0m0.019s
可見(jiàn),time.sleep(2)并未計(jì)入SleepTiming()耗時(shí),導(dǎo)致計(jì)時(shí)結(jié)果與real時(shí)間相差很大。
對(duì)于代碼片段計(jì)時(shí),以上下文管理器為例:
importcontextlib,sys,time
@contextlib.contextmanager
defBlockTimer(label='Block'):
ifsys.platform=="win32":timerFunc=time.clock
else:timerFunc=time.time
startTime=timerFunc()
try:
yield
finally:
endTime=timerFunc()
print'%s=>'%label,
print'TimeElasped:%.3fmsec.'\
%((endTime-startTime)*1000.0)
基于BlockTimer測(cè)量代碼片段的示例如下:
withBlockTimer('cPickle'):
fromcPickleimportdumps,loads
s=dumps([x*2.4forxinrange(100000)])
loads(s)
withBlockTimer('json'):
fromjsonimportdumps,loads
s=dumps([x*2.4forxinrange(100000)])
loads(s)
運(yùn)行結(jié)果如下:
cPickle=>TimeElasped:237.569msec.
json=>TimeElasped:181.714msec.
可見(jiàn),對(duì)于浮點(diǎn)型對(duì)象,json模塊執(zhí)行速度比cPickle模塊更快。
當(dāng)然,借助timeit模塊也可對(duì)代碼片段計(jì)時(shí)。例如:
fromtimeitimporttimeit
sep='fromcPickleimportdumps,loads'
stp='s=dumps([x*2forxinrange(100000)]);loads(s)'
print'cPickle:%.6fsec'%timeit(stmt=stp,setup=sep,number=1)
sej='fromjsonimportdumps,loads'
stj='s=dumps([x*2forxinrange(100000)]);loads(s)'
print'json:%.6fsec'%timeit(stmt=stj,setup=sej,number=1)
本例改為整型對(duì)象,且模塊導(dǎo)入語(yǔ)句不計(jì)入總耗時(shí)。運(yùn)行結(jié)果如下:
cPickle:0.100775sec
json:0.064752sec
可見(jiàn),對(duì)于整型對(duì)象,json模塊執(zhí)行速度也比cPickle模塊快。
以上內(nèi)容為大家介紹了Python自定義計(jì)時(shí)函數(shù),希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。http://m.2667701.com/