Python自定义计时函数

发布时间:2022-04-22 10:02:41 人气:294 作者:多测师

  Python标准库提供的cProfile/profile模块,计时输出信息较多。本节将介绍其他几种精度略低但简单易用的计时工具。根据代码粒度不同,将其分为三类。

  1.1 整个程序计时

  Unix/Linux系统中,可用time命令简单地统计整个程序的耗时。例如:

  [wangxiaoyuan_@localhost PyTest]$ time python BCLineCounter.py bulk

  FileLines CodeLines CommentLines EmptyLines CommentPercent

  15450 10437 3264 2538 0.24

  real 0m2.803s

  user 0m1.124s

  sys 0m0.052s

  统计值real表示程序运行的实际耗时,user表示程序执行用户态代码(内核外)耗费的CPU时间,sys表示程序在内核态运行所耗费的CPU时间(即调用特定内核函数的耗时)。若user和sys时间之和小于real时间,表明程序为I/O密集型(I/O bound),即程序的性能问题很可能与等待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);对于语句块计时,则可使用上下文管理器(context manager)。

  以装饰器为例:

  import functools, sys, time

  def FuncTimer(repeats=10000):

  def decorator(func):

  @functools.wraps(func)

  def wrapper(*args, **kwargs):

  #Windows系统中clock()粒度为毫秒,time()粒度为1/60秒;

  #Unix系统中clock()粒度为1/100秒,time()精度较其更高。

  if sys.platform == "win32":

  timerFunc = time.clock

  else:

  timerFunc = time.time

  try:

  startTime = timerFunc()

  for i in range(repeats):

  ret = func(*args, **kwargs)

  finally: #当目标函数发生异常时,仍旧输出计时信息

  endTime = timerFunc()

  print '%s.%s() =>' %(func.__module__, func.__name__),

  print 'Time Elasped: %.3f msec, repeated %d time(s).' \

  %(((endTime-startTime)*1000.0), repeats)

  return ret

  return wrapper

  return decorator

Python自定义计时函数

  运行如下代码,对比自定义装饰器FuncTimer与timeit模块计时效果:

  @FuncTimer(10)

  def DecoratedFunc():

  L = []

  for i in range(100): L.append(i)

  def RawFunc():

  L = []

  for i in range(100): L.append(i)

  DecoratedFunc()

  import timeit; print '%.6f sec' %timeit.timeit(stmt=RawFunc, number=10)

  输出如下:

  __main__.DecoratedFunc() => Time Elasped: 0.164 msec, repeated 10 time(s).

  0.000174 sec

  可见,计时效果非常接近。

  注意,FuncTimer装饰器内根据系统选用不同的计时器,这是考虑到time.clock()的精度因系统平台而异。在Unix/Linux系统中,该方法返回当前所耗的CPU时间;而在Windows系统中,该方法基于Win32函数 QueryPerformanceCounter(),返回从首次调用待计时函数起所经历的挂钟时间(wall clock time),精度较time.time()更高。相比而言,timeit方法中使用的缺省计时器总是测量挂钟时间,这也意味着关于某函数的计时可能会受到同一计算机上运行的其他进程的影响。

  time.clock()计时器的平台差异性参考以下示例(假定所在脚本名为Timing.py):

  @FuncTimer(5)

  def SqrtTiming(loops):

  import math

  try:

  from math import fsum #Python2.6+

  return fsum([math.sqrt(x) for x in range(loops)])

  except ImportError: #Python2.5-

  return sum([math.sqrt(x) for x in range(loops)])

  @FuncTimer(1)

  def SleepTiming():

  time.sleep(2)

  file = open(r'out.txt', "w+")

  for i in range(10000):

  file.write('hello world!')

  SqrtTiming(100000)

  SleepTiming()

  在Windows系统控制台和IDLE Shell里的运行结果如下:

  E:\PyTest>Timing.py

  SqrtTiming() => Time Elasped: 150.124 msec, repeated 5 time(s).

  SleepTiming() => Time Elasped: 2155.140 msec, repeated 1 time(s).

  __main__.SqrtTiming() => Time Elasped: 151.809 msec, repeated 5 time(s).

  __main__.SleepTiming() => Time Elasped: 2185.594 msec, repeated 1 time(s).

  >>> import Timing

  Timing.SqrtTiming() => Time Elasped: 148.892 msec, repeated 5 time(s).

  Timing.SleepTiming() => Time Elasped: 2223.157 msec, repeated 1 time(s).

  在Linux系统中运行结果与之类似。若将timerFunc改为time.clock(),则计时输出为:

  [wangxiaoyuan_@localhost ~]$ time python Timing.py

  __main__.SqrtTiming() => Time Elasped: 320.000 msec, repeated 5 time(s).

  __main__.SleepTiming() => Time Elasped: 330.000 msec, repeated 1 time(s).

  real 0m2.381s

  user 0m0.332s

  sys 0m0.019s

  可见,time.sleep(2)并未计入SleepTiming()耗时,导致计时结果与real时间相差很大。

  对于代码片段计时,以上下文管理器为例:

  import contextlib, sys, time

  @contextlib.contextmanager

  def BlockTimer(label='Block'):

  if sys.platform == "win32": timerFunc = time.clock

  else: timerFunc = time.time

  startTime = timerFunc()

  try:

  yield

  finally:

  endTime = timerFunc()

  print '%s =>' %label,

  print 'Time Elasped: %.3f msec.' \

  %((endTime-startTime)*1000.0)

  基于BlockTimer测量代码片段的示例如下:

  with BlockTimer('cPickle'):

  from cPickle import dumps, loads

  s = dumps([x*2.4 for x in range(100000)])

  loads(s)

  with BlockTimer('json'):

  from json import dumps, loads

  s = dumps([x*2.4 for x in range(100000)])

  loads(s)

  运行结果如下:

  cPickle => Time Elasped: 237.569 msec.

  json => Time Elasped: 181.714 msec.

  可见,对于浮点型对象,json模块执行速度比cPickle模块更快。

  当然,借助timeit模块也可对代码片段计时。例如:

  from timeit import timeit

  sep = 'from cPickle import dumps, loads'

  stp = 's=dumps([x*2 for x in range(100000)]); loads(s)'

  print 'cPickle: %.6f sec' %timeit(stmt=stp, setup=sep, number=1)

  sej = 'from json import dumps, loads'

  stj = 's=dumps([x*2 for x in range(100000)]); loads(s)'

  print 'json: %.6f sec' %timeit(stmt=stj, setup=sej, number=1)

  本例改为整型对象,且模块导入语句不计入总耗时。运行结果如下:

  cPickle: 0.100775 sec

  json: 0.064752 sec

  可见,对于整型对象,json模块执行速度也比cPickle模块快。

  以上内容为大家介绍了Python自定义计时函数,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注多测师。https://www.e70w.com/xwzx/


返回列表
在线客服
联系方式

热线电话

17727591462

上班时间

周一到周五

二维码
线