代码之家  ›  专栏  ›  技术社区  ›  unutbu

timeit与计时装饰器

  •  51
  • unutbu  · 技术社区  · 16 年前

    我在试着计时一些代码。首先,我使用了定时装饰器:

    #!/usr/bin/env python
    
    import time
    from itertools import izip
    from random import shuffle
    
    def timing_val(func):
        def wrapper(*arg, **kw):
            '''source: http://www.daniweb.com/code/snippet368.html'''
            t1 = time.time()
            res = func(*arg, **kw)
            t2 = time.time()
            return (t2 - t1), res, func.__name__
        return wrapper
    
    @timing_val
    def time_izip(alist, n):
        i = iter(alist)
        return [x for x in izip(*[i] * n)]
    
    @timing_val
    def time_indexing(alist, n):
        return [alist[i:i + n] for i in range(0, len(alist), n)]
    
    func_list = [locals()[key] for key in locals().keys()
                 if callable(locals()[key]) and key.startswith('time')]
    shuffle(func_list)  # Shuffle, just in case the order matters
    
    alist = range(1000000)
    times = []
    for f in func_list:
        times.append(f(alist, 31))
    
    times.sort(key=lambda x: x[0])
    for (time, result, func_name) in times:
        print '%s took %0.3fms.' % (func_name, time * 1000.)
    

    产量

    % test.py
    time_indexing took 73.230ms.
    time_izip took 122.057ms.
    

    这里我使用timeit:

    %  python - m timeit - s '' 'alist=range(1000000);[alist[i:i+31] for i in range(0, len(alist), 31)]'
    10 loops, best of 3:
        64 msec per loop
    % python - m timeit - s 'from itertools import izip' 'alist=range(1000000);i=iter(alist);[x for x in izip(*[i]*31)]'
    10 loops, best of 3:
        66.5 msec per loop
    

    使用timeit,结果几乎相同,但使用计时装饰器,结果似乎相同 time_indexing 快于 time_izip .

    这两种方法都应该被相信吗?

    如果有,哪一个?

    6 回复  |  直到 12 年前
        1
  •  106
  •   wrhall    10 年前

    使用包装材料 functools 改进Matt Alcock的答案。

    from functools import wraps
    from time import time
    
    def timing(f):
        @wraps(f)
        def wrap(*args, **kw):
            ts = time()
            result = f(*args, **kw)
            te = time()
            print 'func:%r args:[%r, %r] took: %2.4f sec' % \
              (f.__name__, args, kw, te-ts)
            return result
        return wrap
    

    例如:

    @timing
    def f(a):
        for _ in range(a):
            i = 0
        return -1
    

    调用方法 f 裹着 @timing :

    func:'f' args:[(100000000,), {}] took: 14.2240 sec
    f(100000000)
    

        2
  •  42
  •   Asclepius    12 年前

    我会使用计时装饰器,因为您可以使用注释在代码周围散布计时,而不必使用计时逻辑将代码弄得一团糟。

    import time
    
    def timeit(f):
    
        def timed(*args, **kw):
    
            ts = time.time()
            result = f(*args, **kw)
            te = time.time()
    
            print 'func:%r args:[%r, %r] took: %2.4f sec' % \
              (f.__name__, args, kw, te-ts)
            return result
    
        return timed
    

    @timeit
    def compute_magic(n):
         #function definition
         #....
    

    compute_magic = timeit(compute_magic)
    
        3
  •  23
  •   Jochen Ritzel    16 年前

    func_list=[locals()[key] for key in locals().keys() 
               if callable(locals()[key]) and key.startswith('time')]
    
    alist=range(1000000)
    times=[]
    for f in func_list:
        n = 10
        times.append( min(  t for t,_,_ in (f(alist,31) for i in range(n)))) 
    
    for (time,func_name) in zip(times, func_list):
        print '%s took %0.3fms.' % (func_name, time*1000.)
    

    <function wrapper at 0x01FCB5F0> took 39.000ms.
    <function wrapper at 0x01FCB670> took 41.000ms.
    
        4
  •  7
  •   Community Mohan Dere    9 年前

    我厌倦了 from __main__ import foo ,现在使用它--对于简单参数,使用%r, 而不是在伊皮顿。
    (为什么 timeit

    
    import timeit
    
    def timef( funcname, *args, **kwargs ):
        """ timeit a func with args, e.g.
                for window in ( 3, 31, 63, 127, 255 ):
                    timef( "filter", window, 0 )
        This doesn't work in ipython;
        see Martelli, "ipython plays weird tricks with __main__" in Stackoverflow        
        """
        argstr = ", ".join([ "%r" % a for a in args]) if args  else ""
        kwargstr = ", ".join([ "%s=%r" % (k,v) for k,v in kwargs.items()]) \
            if kwargs  else ""
        comma = ", " if (argstr and kwargstr)  else ""
        fargs = "%s(%s%s%s)" % (funcname, argstr, comma, kwargstr)
            # print "test timef:", fargs
        t = timeit.Timer( fargs, "from __main__ import %s" % funcname )
        ntime = 3
        print "%.0f usec %s" % (t.timeit( ntime ) * 1e6 / ntime, fargs)
    
    #...............................................................................
    if __name__ == "__main__":
        def f( *args, **kwargs ):
            pass
    
        try:
            from __main__ import f
        except:
            print "ipython plays weird tricks with __main__, timef won't work"
        timef( "f")
        timef( "f", 1 )
        timef( "f", """ a b """ )
        timef( "f", 1, 2 )
        timef( "f", x=3 )
        timef( "f", x=3 )
        timef( "f", 1, 2, x=3, y=4 )
    

    补充:另见“ipython与 “,马尔泰利 running-doctests-through-ipython

        5
  •  3
  •   Micah Smith Matt    6 年前

    这就是你希望图书馆能提供一个便携解决方案的需求类型——干货!幸运地 funcy.log_durations 答案来了。

    从文档中复制的示例:

    @log_durations(logging.info)
    def do_hard_work(n):
        samples = range(n)
        # ...
    
    # 121 ms in do_hard_work(10)
    # 143 ms in do_hard_work(11)
    # ...
    

    @log_iter_durations .

        6
  •  3
  •   newbee    6 年前

    受Micah Smith回答的启发,我改为直接打印funcy(而不是使用日志模块)。

    # pip install funcy
    from funcy import print_durations
    
    @print_durations()
    def myfunc(n=0):
      for i in range(n):
        pass
    
    myfunc(123)
    myfunc(123456789)
    
    # 5.48 mks in myfunc(123)
    # 3.37 s in myfunc(123456789)
    
        7
  •  2
  •   mpontillo    16 年前

    只是一个猜测,但差异可能是range()值差异的数量级吗?

    从您的原始来源:

    alist=range(1000000)
    

    timeit 例子:

    alist=range(100000)
    

    $ python -V
    Python 2.6.4rc2
    
    $ python -m timeit -s 'from itertools import izip' 'alist=range(1000000);i=iter(alist);[x for x in izip(*[i]*31)]'
    10 loops, best of 3: 69.6 msec per loop
    
    $ python -m timeit -s '' 'alist=range(1000000);[alist[i:i+31] for i in range(0, len(alist), 31)]'
    10 loops, best of 3: 67.6 msec per loop
    

    我无法运行您的其他代码,因为我无法在系统上导入“decorator”模块。


    使现代化

    $ ./test.py
    time_indexing took 84.846ms.
    time_izip took 132.574ms.
    

    谢谢你发布这个问题;我今天学到了一些东西

        8
  •  0
  •   SilentGhost    16 年前

    timeit 是更安全可靠的选择。它也是跨平台的,与您的解决方案不同。