Python时间测量功能

时间:2011-03-29 20:12:40

标签: python time callback

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

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

9 个答案:

答案 0 :(得分:225)

首先,我强烈建议您使用profiler或至少使用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):
        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__

答案 1 :(得分:44)

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

看起来不太好......如果您使用的是Python 2,它没有nonlocal个关键字。

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

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

答案 2 :(得分:11)

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

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

它也可以向函数发送参数。您所需要的只是使用装饰器包装您的功能。这里有更多解释:http://www.pythoncentral.io/time-a-python-function/

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

使用timeit模块的好处是它可以让你repeat执行的次数。这可能是必要的,因为其他过程可能会影响您的计时准确性。因此,您应该多次运行它并查看最低值。

答案 3 :(得分:9)

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)

答案 4 :(得分:3)

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

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

请参阅我的博客上的帖子:

post on mobilepro.pl Blog

my post on Google Plus

答案 5 :(得分:3)

有一个简单的计时工具。 Plunker Example

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

    window.onload = function() {
    var ctx = document.getElementById("canvas").getContext("2d");
    window.myLine = new Chart(ctx, config);
};

输出:

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

希望它会有所帮助

答案 6 :(得分:2)

我的做法:

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)

你得到了答案。

答案 7 :(得分:0)

详细介绍@Jonathan Ray,我认为这可以更好地解决问题

import time
import inspect

def timed(f:callable):
    start = time.time()
    ret = f()
    elapsed = 1000*(time.time() - start)
    source_code=inspect.getsource(f).strip('\n') 
    logger.info(source_code+":  "+str(elapsed)+" seconds")
    return ret

它允许采用常规的代码行,例如a = np.sin(np.pi)并将其转换为简单的

a = timed(lambda: np.sin(np.pi))

以便将计时打印在记录器上,并且您可以将结果分配给变量,以进行进一步的工作。

我想在python 3.8中可以使用:=,但我还没有3.8

答案 8 :(得分:0)

下面是一个 Timer 类:

  • 易于使用:直接使用或作为装饰器函数使用,<100 行
  • 测量很多:总调用次数、总时间、平均时间和标准。偏差。
  • 打印美好时光
  • 线程安全

这是你如何使用它:

# Create the timer
timer1 = Timer("a name", log_every=2)

# Use "with"
with timer1:
   print("timer1")

# Reuse as a decorator
@timer1
def my_func():
  print("my_func")

# Instantiate as a decorator
@Timer("another timer", log_every=1)
def my_func2():
  print("my_func2")

my_func()
my_func2()
my_func()

下面是课程

from datetime import datetime
import time, logging, math, threading
class Timer(object):
    '''A general timer class. Does not really belong in a judicata file here.'''
    def __init__(self, name, log_every = 1):
        self.name = name
        self.log_every = 1
        self.calls = 0
        self.total_time = 0
        self.total_squared_time = 0
        self.min, self.max = None, 0
        # Make timer thread-safe by storing the times in thread-local storage.
        self._local = threading.local()
        self._lock = threading.Lock()

    def __enter__(self):
        """Start a new timer"""
        self._local.start = datetime.utcnow()

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Stop the timer, and report the elapsed time"""
        elapsed_time = (datetime.utcnow() - self._local.start).total_seconds()
        with self._lock:
            self.calls += 1
            self.total_time += elapsed_time
            if self.min == None or elapsed_time < self.min:
                self.min = elapsed_time
            if elapsed_time > self.max:
                self.max = elapsed_time
            self.total_squared_time += elapsed_time * elapsed_time
            if self.log_every and (self.calls % self.log_every) == 0:
                self.log()

    def __call__(self, fn):
        '''For use as a decorator.'''
        def decorated_timer_function(*args, **kwargs):
            with self:
                return fn(*args, **kwargs)
        return decorated_timer_function

    @classmethod
    def time_str(cls, secs):
        if isinstance(secs, six.string_types):
            try:
                secs = float(secs)
            except:
                return "(bad time: %s)"%secs
        sign = lambda x: x
        if secs < 0:
            secs = -secs
            sign = lambda x: ("-" + x)
        return sign("%d secs"%int(secs) if secs >= 120 else
                    "%.2f secs" % secs if secs >= 1 else
                    "%d ms" % int(secs * 1000) if secs >= .01 else
                    "%.2f ms" % (secs * 1000) if secs >= .0001 else
                    "%d ns" % int(secs * 1000 * 10000) if secs >= 1e-9 else
                    "%s" % secs)

    def log(self):
        if not self.calls:
            logging.info("<Timer %s: no calls>"%self.name)
            return
        avg = 1.0 * self.total_time / self.calls
        var = 1.0 * self.total_squared_time / self.calls - avg*avg
        std_dev = self.time_str(math.sqrt(var))
        total = self.time_str(self.total_time)
        min, max, avg = [self.time_str(t) for t in [self.min, self.max, avg]]
        logging.info("<Timer %s: N=%s, total=%s, avg=%s, min/max=%s/%s, std=%s>"
                     %(self.name, self.calls, total, avg, min, max, std_dev))