python标准库提供的cProfile/profile模块,计时输出信息较多。本节将介绍其他几种精度略低但简单易用的计时工具。根据代码粒度不同,将其分为三类。
1.1整个程序计时
Unix/Linux系统中,可用time命令简单地统计整个程序的耗时。例如:
[wangxiaoyuan_@localhostPyTest]$timepythonBCLineCounter.pybulk
FileLinesCodeLinesCommentLinesEmptyLinesCommentPercent
1545010437326425380.24
real0m2.803s
user0m1.124s
sys0m0.052s
统计值real表示程序运行的实际耗时,user表示程序执行用户态代码(内核外)耗费的CPU时间,sys表示程序在内核态运行所耗费的CPU时间(即调用特定内核函数的耗时)。若user和sys时间之和小于real时间,表明程序为I/O密集型(I/Obound),即程序的性能问题很可能与等待I/O有关。
time命令的详细描述参见《Linux用户态程序计时方式详解》。
1.2代码片段计时
代码片段计时分为函数计时和语句块计时。这两种计时均可使用Python标准库timeit模块,该模块的详细介绍参见官方帮助。
本小节将使用timeit模块的timeit()方法,即timeit(stmt='pass',setup='pass',timer=,number=1000000)。其中,参数stmt为待计时的目标代码;setup为执行代码的准备工作(通常是import之类的语句),不计入时间;timer在Windows系统中为time.clock(),Linux系统中则为time.time(),取默认值即可;number指示stmt重复执行的次数。该方法返回执行stmt代码number遍所用的时间,单位为秒,float类型。
除timeit()方法外,对于特定函数的计时,可使用装饰器(decorator);对于语句块计时,则可使用上下文管理器(contextmanager)。
以装饰器为例:
importfunctools,sys,time
defFuncTimer(repeats=10000):
defdecorator(func):
@functools.wraps(func)
defwrapper(*args,**kwargs):
#Windows系统中clock()粒度为毫秒,time()粒度为1/60秒;
#Unix系统中clock()粒度为1/100秒,time()精度较其更高。
ifsys.platform=="win32":
timerFunc=time.clock
else:
timerFunc=time.time
try:
startTime=timerFunc()
foriinrange(repeats):
ret=func(*args,**kwargs)
finally:#当目标函数发生异常时,仍旧输出计时信息
endTime=timerFunc()
print'%s.%s()=>'%(func.__module__,func.__name__),
print'TimeElasped:%.3fmsec,repeated%dtime(s).'\
%(((endTime-startTime)*1000.0),repeats)
returnret
returnwrapper
returndecorator
运行如下代码,对比自定义装饰器FuncTimer与timeit模块计时效果:
@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
可见,计时效果非常接近。
注意,FuncTimer装饰器内根据系统选用不同的计时器,这是考虑到time.clock()的精度因系统平台而异。在Unix/Linux系统中,该方法返回当前所耗的CPU时间;而在Windows系统中,该方法基于Win32函数QueryPerformanceCounter(),返回从首次调用待计时函数起所经历的挂钟时间(wallclocktime),精度较time.time()更高。相比而言,timeit方法中使用的缺省计时器总是测量挂钟时间,这也意味着关于某函数的计时可能会受到同一计算机上运行的其他进程的影响。
time.clock()计时器的平台差异性参考以下示例(假定所在脚本名为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系统控制台和IDLEShell里的运行结果如下:
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系统中运行结果与之类似。若将timerFunc改为time.clock(),则计时输出为:
[wangxiaoyuan_@localhost~]$timepythonTiming.py
__main__.SqrtTiming()=>TimeElasped:320.000msec,repeated5time(s).
__main__.SleepTiming()=>TimeElasped:330.000msec,repeated1time(s).
real0m2.381s
user0m0.332s
sys0m0.019s
可见,time.sleep(2)并未计入SleepTiming()耗时,导致计时结果与real时间相差很大。
对于代码片段计时,以上下文管理器为例:
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测量代码片段的示例如下:
withBlockTimer('cPickle'):
fromcPickleimportdumps,loads
s=dumps([x*2.4forxinrange(100000)])
loads(s)
withBlockTimer('json'):
fromjsonimportdumps,loads
s=dumps([x*2.4forxinrange(100000)])
loads(s)
运行结果如下:
cPickle=>TimeElasped:237.569msec.
json=>TimeElasped:181.714msec.
可见,对于浮点型对象,json模块执行速度比cPickle模块更快。
当然,借助timeit模块也可对代码片段计时。例如:
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)
本例改为整型对象,且模块导入语句不计入总耗时。运行结果如下:
cPickle:0.100775sec
json:0.064752sec
可见,对于整型对象,json模块执行速度也比cPickle模块快。
以上内容为大家介绍了Python自定义计时函数,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注IT培训机构:千锋教育。http://www.mobiletrain.org/