Python时间测量功能


121

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

def measureTime(a):
    start = time.clock() 
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print "Time spent in (function name) is: ", elapsed

Python分析工具可以向您显示函数名称和每个函数所花费的时间。在这里阅读:docs.python.org/library/profile.html
Roadmaster

更好地timeit用于测量。它不是完美的,但是到目前为止它击败了您,它timeit比使用自己动手做的东西容易得多。

Answers:


240

首先,我强烈建议使用探查器或至少使用timeit

但是,如果您想严格地学习自己的计时方法,可以在这里开始使用装饰器。

Python 2:

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装饰器即可:

@timing
def do_work():
  #code

Python 3:

def timing(f):
    def wrap(*args, **kwargs):
        time1 = time.time()
        ret = f(*args, **kwargs)
        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中)或f.__name__ 在Python 3中。


4
正是我想要的:) ...但是你们说服了我使用python profiler
Wazery 2011年

3
看起来这是假设time.time()报告自纪元以来的时间(以微秒为单位)?该文档说它以秒为单位报告时间docs.python.org/2/library/time.html#time.time
拉胡尔·贾

在func中使用yield后,此操​​作不会生效。我如何仍可以使用此方法并可以使用yield?
佳模2015年

def Timing(f):def wrap(* args,** kwargs):time1 = time.time()ret = f(* args,** kwargs)time2 = time.time()print'%s函数占用%0.3 f ms'%(f.func_name,(time2-time1)* 1000)return ret return wrap
Vivek Bagaria

1
自己编写的缺点是什么?存储经过时间的列表并检查其分布是否不够简单?
3pitt

51

在玩完timeit模块之后,我不喜欢它的界面,与下面两种方法相比,它的界面并不那么优雅。

以下代码在Python 3中。

装饰器方法

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

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)

上下文管理器方法

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)))

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

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

并且该with块内的代码将被计时。

结论

使用第一种方法,您可以轻松地注释掉装饰器以获取常规代码。但是,它只能计时一个功能。如果您有一部分代码不是使它起作用的功能,则可以选择第二种方法。

例如,现在你有

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

现在您要为bigImage = ...生产线计时。如果将其更改为功能,它将是:

images = get_images()
bitImage = None
@timeit
def foobar():
    nonlocal bigImage
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

看起来不太好...如果您使用的是没有nonlocal关键字的Python 2,该怎么办?

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

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

有趣的贡献,但是我发现在您提到的装饰器方法中,您必须更改timeit接口并使用模块的wraps()功能是没有用的functools。我的意思是不需要所有额外的代码。
Billal Begueradj

1
需求import functools
Guillaume Chevalier 2017年

1
请注意,您的装饰器失去了原始函数的返回值
Marc Van Daele

11

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

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

也可以向函数发送参数。您需要做的就是使用装饰器包装功能。此处有更多说明: http //www.pythoncentral.io/time-a-python-function/

您可能对编写自己的时序语句感兴趣的唯一情况是,您只想运行一个函数并且还希望获得其返回值。

使用该timeit模块的优势 在于,它使您可以重复执行次数。这可能是必要的,因为其他过程可能会干扰您的计时精度。因此,您应该多次运行它并查看最小值。


3
使用包装器和装饰器向函数发送参数?为什么不timeit.timeit(lambda: func(a,b,c), number=1)呢?在对终端中的假设解决方案进行测试时,会用到它。
杰克

10

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

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)

谢谢!timeit在Apache Spark中不能很好地工作,因为您必须导入所有Spark依赖项,谁想创建一个大的旧字符串来做到这一点?该解决方案更简单,更灵活。
保罗

4

有一个简单的计时工具。https://github.com/RalphMao/PyTimer

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

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

输出:

matmul:0.368434
matmul:2.839355

它也可以像带有名称空间控制的插件计时器一样工作(如果将其插入具有很多代码并且可以在其他任何地方调用的函数,则将很有帮助)。

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()                                        

输出:

========Timing Summary of Default Timer========
block2:0.065062
block1:0.032529
========Timing Summary of Default Timer========
block2:0.065838
block1:0.032891

希望对你有帮助


3

使用装饰器Python库的Decorator方法:

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

我的做法:

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)在块之后立即设置变量。

然后你得到了答案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.