Python时间测量功能

Python time measure function

我想创建一个python函数来测试在每个函数中花费的时间,并用它的时间打印它的名称,如何打印函数名,如果还有其他方法,请告诉我

1
2
3
4
5
6
def measureTime(a):
    start = time.clock()
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print"Time spent in (function name) is:", elapsed


首先,我强烈建议使用分析器,或者至少使用timeit。

然而,如果您想严格地编写自己的计时方法来学习,这里是开始使用修饰符的地方。

Python 2:

1
2
3
4
5
6
7
8
def timing(f):
    def wrap(*args):
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0)
        return ret
    return wrap

使用非常简单,只需使用@timing decorator:

1
2
3
@timing
def do_work():
  #code

Python 3:

1
2
3
4
5
6
7
8
9
def timing(f):
    def wrap(*args):
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))

        return ret
    return wrap

注意,我正在调用f.func_name以获取字符串形式的函数名(在python 2中),或者在python 3中调用f.__name__


在使用了timeit模块之后,我不喜欢它的接口,与下面的两种方法相比,它并不那么优雅。

下面的代码在python 3中。

装饰方法

这与@mike的方法几乎相同。在这里,我添加了kwargsfunctools包装以使其更好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def timeit(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
    return newfunc

@timeit
def foobar():
    mike = Person()
    mike.think(30)

上下文管理器方法

1
2
3
4
5
6
7
8
from contextlib import contextmanager

@contextmanager
def timeit_context(name):
    startTime = time.time()
    yield
    elapsedTime = time.time() - startTime
    print('[{}] finished in {} ms'.format(name, int(elapsedTime * 1000)))

例如,您可以这样使用它:

1
2
3
with timeit_context('My profiling code'):
    mike = Person()
    mike.think()

with块内的代码将被计时。

结论

使用第一个方法,您可以对装饰器进行注释,以获得正常的代码。但是,它只能对一个函数计时。如果代码中有一些部分不知道如何使其成为函数,那么可以选择第二个方法。

例如,现在您已经

1
2
3
images = get_images()
bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

现在你想给bigImage = ...行计时。如果您将其更改为函数,它将是:

1
2
3
4
5
6
7
images = get_images()
bitImage = None
@timeit
def foobar():
    nonlocal bigImage
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

看起来不太好……如果您在没有nonlocal关键字的python 2中,该怎么办?

相反,使用第二种方法非常适合这里:

1
2
3
4
images = get_images()
with timeit_context('foobar'):
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)


我不知道timeit模块有什么问题。这可能是最简单的方法。

1
2
import timeit
timeit.timeit(a, number=1)

也可以向函数发送参数。您所需要的只是使用装饰器来包装您的函数。更多解释请访问:http://www.pythoncentral.io/time-a-python-function/

唯一可能有兴趣编写自己的计时语句的情况是,如果您只想运行一次函数,并且还想获得它的返回值。

使用timeit模块的优点是它允许您重复执行的次数。这可能是必要的,因为其他进程可能会干扰您的计时准确性。所以,您应该多次运行它并查看最低值。


TimeIt有两个大缺陷:它不返回函数的返回值,它使用eval,这需要为导入传递额外的设置代码。这简单而优雅地解决了这两个问题:

1
2
3
4
5
6
7
8
def timed(f):
  start = time.time()
  ret = f()
  elapsed = time.time() - start
  return ret, elapsed

timed(lambda: database.foo.execute('select count(*) from source.apachelog'))
(<sqlalchemy.engine.result.ResultProxy object at 0x7fd6c20fc690>, 4.07547402381897)


有一个简单的计时工具。网址:https://github.com/ralphmao/pytimer

它可以像装饰师一样工作:

1
2
3
4
5
from pytimer import Timer
@Timer(average=False)      
def matmul(a,b, times=100):
    for i in range(times):
        np.dot(a,b)

输出:

1
2
matmul:0.368434
matmul:2.839355

它还可以像带有名称空间控件的插件计时器一样工作(如果您要将它插入到一个代码很多并且可以在其他任何地方调用的函数中,这将非常有用)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
timer = Timer()                                          
def any_function():                                      
    timer.start()                                        

    for i in range(10):                                  

        timer.reset()                                    
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block1')                        

        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block2')                        
        np.dot(np.ones((100,1000)), np.zeros((1000,1000)))

    for j in range(20):                                  
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
    timer.summary()                                      

for i in range(2):                                        
    any_function()

输出:

1
2
3
4
5
6
========Timing Summary of Default Timer========
block2:0.065062
block1:0.032529
========Timing Summary of Default Timer========
block2:0.065838
block1:0.032891

希望有帮助


使用decorator python库的decorator方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import decorator

@decorator
def timing(func, *args, **kwargs):
    '''Function timing wrapper
        Example of using:
        ``@timing()``
    '''


    fn = '%s.%s' % (func.__module__, func.__name__)

    timer = Timer()
    with timer:
        ret = func(*args, **kwargs)

    log.info(u'%s - %0.3f sec' % (fn, timer.duration_in_seconds()))
    return ret

查看我的日志:

在mobilepro.pl博客上发布

我在google plus上的帖子


我的做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from time import time

def printTime(start):
    end = time()
    duration = end - start
    if duration < 60:
        return"used:" + str(round(duration, 2)) +"s."
    else:
        mins = int(duration / 60)
        secs = round(duration % 60, 2)
        if mins < 60:
            return"used:" + str(mins) +"m" + str(secs) +"s."
        else:
            hours = int(duration / 3600)
            mins = mins % 60
            return"used:" + str(hours) +"h" + str(mins) +"m" + str(secs) +"s."

在执行函数/循环之前将变量设置为start = time(),在块之后设置为printTime(start)

你得到了答案。