Python时间测量函数

2025-04-16 08:57:00
admin
原创
20
摘要:问题描述:我想创建一个 python 函数来测试每个函数所花费的时间并打印其名称及其时间,我如何打印函数名称,如果有其他方法可以做到这一点,请告诉我def measureTime(a): start = time.clock() a() elapsed = time.clock() ...

问题描述:

我想创建一个 python 函数来测试每个函数所花费的时间并打印其名称及其时间,我如何打印函数名称,如果有其他方法可以做到这一点,请告诉我

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

解决方案 1:

首先,我强烈建议使用分析器或至少使用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 中获取。

解决方案 2:

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

以下代码是 Python 3 中的。

装饰器方法

这个和@Mike的方法差不多,这里我加了个kwargs换行符,functools让效果更好一些。

def timeit(func):
    @functools.wraps(func)
    def new_func(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        elapsed_time = time.time() - start_time
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsed_time * 1_000)))
        return result
    return new_func

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

上下文管理器方法

from contextlib import contextmanager

@contextmanager
def timeit_context(name):
    start_time = time.time()
    yield
    elapsed_time = time.time() - start_time
    print('[{}] finished in {} ms'.format(name, int(elapsed_time * 1_000)))

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

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

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

结论

使用第一种方法,您可以轻松注释掉装饰器以获取正常代码。但是,它只能用于计时函数。如果您不想将部分代码变成函数,则可以选择第二种方法。

例如,现在你有

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

现在你需要计算这big_image = ...条线的时长。如果将其改为函数,则计算结果如下:

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

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

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

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

解决方案 3:

我不明白这个timeit模块有什么问题。这可能是最简单的方法了。

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

也可以向函数发送参数。你只需要用装饰器包装你的函数即可。更多解释请见:http://www.pythoncentral.io/time-a-python-function/

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

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

解决方案 4:

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)

解决方案 5:

有一个简单的计时工具。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

希望有帮助

解决方案 6:

使用装饰器 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

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

在 mobilepro.pl 博客上发表文章

我在 Google Plus 上的帖子

解决方案 7:

我的做法是:

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)块之后设置一个变量。

你得到了答案。

解决方案 8:

详细说明@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('
') 
    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

解决方案 9:

下面是一个 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))

解决方案 10:

您可以timeit.default_timer与一起使用contextmanager

from timeit import default_timer
from contextlib import contextmanager

@contextmanager
def timer():
    start_time = default_timer()
    try:
        yield
    finally:
        print("--- %s seconds ---" % (default_timer() - start_time))

与语句一起使用with

def looper():
    for i in range(0, 100000000):
        pass

with timer():
    looper()

输出:

--- 2.651526927947998 seconds ---

解决方案 11:

这是一个通用的解决方案

def timed(fn):
    # make sure wherever u used this, imports will be ready
    from time import perf_counter
    from functools import wraps
    # wraps preserves the metadata of fn
    @wraps(fn)
    def inner(*args, **kwargs):
        start = perf_counter()
        result = fn(*args, **kwargs)
        end = perf_counter()
        elapsed = end - start
        args_ = [str(a) for a in args]
        kwargs_ = ["{0}={1}".format(k, v) for (k, v) in kwargs.items()]
        all_args = args_ + kwargs_
        args_str = ",".join(all_args)
        print("{0} ({1}) took {2:.6f} to run.".format(fn.__name__, args_str, elapsed))
        return result
    return inner

定义一个函数:

@timed
def sum_up(a,b):
     return a+b

现在称之为:

sum_up(2,9)

在此处输入图片描述

解决方案 12:

timeit.timeit对于使用if 命令的情况

timeit.timeit(function_to_test, n=10000)

引发错误ValueError: stmt is neither a string nor callable

或命令

timeit.timeit('function_to_test', n=10000)

引发错误name 'function_to_test' is not defined,那么你需要:

替换 function_to_test'function_to_test'str(function_to_test),即

timeit.timeit(str(function_to_test), n=10000)

或者如果 Python 版本 >= 3.6,另一种方法是使用 f 字符串作为

timeit.timeit(f'{function_to_test}', n=10000)

关于版本使用 lambda,即timeit.timeit(lambda: function_to_test, n=10000),它可以工作,但是,从我的测试来看,它需要更长的时间。

这是一个具体的例子:

import timeit

def function_to_test(n):
    s = 1
    for i in range(n):
        s += 1
    return s
    
print("time run function_to_test: ", timeit.timeit(str(function_to_test(1000000)), number=10000))
print("time run function_to_test: ", timeit.timeit(f'{function_to_test(1000000)}', number=10000))

解决方案 13:

也许您发现此代码很有用:

import time

def start_time_measure(print_res = True):
  start_time = time.time()
  def end_time_measure():
      elapsed_time = time.time() - start_time
      if print_res:
          print("Elapsed time: ", elapsed_time)
      return elapsed_time
  return end_time_measure

例子:

end_time_measure = start_time_measure()

# Here is some job to do
a = []
for i in range(100000):
  a.append(i)

end_time_measure()

通过调用 2 个函数,可以帮助您为代码的任何部分运行计时器:
start_time_measure创建一个闭包并返回start_time_measure可以停止计时器并打印结果 + 返回时间值的函数。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用