有没有办法对执行多次的代码块计时?

问题描述

给定一个函数

do_something_big():
  do_1()
  do_2()
  do_3()

很容易获得运行 do_something_big()do_1() 的单个实例所需的时间。但假设我有

for _ in range(100000):
  do_something_big()

是否有一种简单的方法可以计算 100,000 个 do_1 花费了多长时间?这并不难 - 您只需为每个人计时并更新一些全局状态以跟踪总时间。但是是否已经构建了一个实用程序来为我抽象它?

解决方法

我建议使用例如c配置文件。这个包会为你的代码中的每个函数计时,并以一种很好的格式输出:

import cProfile

pr = cProfile.Profile()
pr.enable()

run_your_program_here()

pr.disable()
pr.print_stats(sort='tottime')

最后一行中的 sort 参数将根据您给定的选项对输出进行排序。您可以阅读更多相关信息here

ncalls – 函数/方法被调用的次数(如果递归调用相同的函数/方法,则 ncalls 有两个值,例如 120/20,其中第一个是真实调用次数,第二个是直接调用次数)

tottime – 不包括其他函数/方法时间的总时间(以秒为单位)

percall – 执行函数的平均时间(每次调用)

cumtime——以秒为单位的总时间包括它调用的其他函数的时间

percall – 与之前的 percall 类似,但是这个包括网络延迟线程睡眠等......

在您的情况下,我会使用 'tottime' 并查看 do_1 花费了多少时间。

,

这是一个装饰器作为您正在寻找的实用程序。灵感来自herehere

我们创建了一个装饰器 @timeit。我们使用这个装饰器来注释我们的方法,它带有一个可选的 n 参数。当达到 n 次调用时,打印此方法的时间指标,否则在每次调用时打印。

from functools import wraps
from time import time
from collections import defaultdict
timedata = defaultdict(lambda : (0,0.0))
    
def timeit(*decArgs,**decKw):
    def _timeit(func):
        @wraps(func)
        def timed(*args,**kw):
            name = func.__name__.upper()
            n = decKw.get('n',1)
            if timedata[name][0] >= n:
                return func(*args,**kw)

            ts = time()
            result = func(*args,**kw)
            te = time()
            duration = int((te - ts) * 1000)
            
            cnt,ave = timedata[name]
            cnt += 1
            timedata[name] = (cnt,((cnt-1) * ave + duration) / cnt)
            if n == cnt:
                print(f'{name:30s}: [{n:6d}x] -> {ave*cnt:7.3f}ms ({ave:6.3f}ms ave)')
                timedata[name] = (0,0.0)
            return result
        return timed
    return _timeit

用法:

from random import random

@timeit(n=100)
def do_1():
    sleep(random()/1000)

@timeit(n=3)
def do_something_big():
    for _ in range(200):
        do_1()

for _ in range(5):
    do_something_big()

输出:

DO_1                          : [   100x] ->  19.192ms ( 0.192ms ave)
DO_1                          : [   100x] ->  23.232ms ( 0.232ms ave)
DO_1                          : [   100x] ->  24.242ms ( 0.242ms ave)
DO_1                          : [   100x] ->  18.182ms ( 0.182ms ave)
DO_1                          : [   100x] ->  14.141ms ( 0.141ms ave)
DO_1                          : [   100x] ->  23.232ms ( 0.232ms ave)
DO_SOMETHING_BIG              : [     3x] -> 393.000ms (131.000ms ave)
DO_1                          : [   100x] ->  27.273ms ( 0.273ms ave)
DO_1                          : [   100x] ->  22.222ms ( 0.222ms ave)
DO_1                          : [   100x] ->  21.212ms ( 0.212ms ave)
DO_1                          : [   100x] ->  24.242ms ( 0.242ms ave)
,

以下是我在 Internet https://realpython.com/python-timer/ 上找到的内容: 您将为 Python 计时器添加可选名称。您可以将该名称用于两个不同的目的:

稍后在您的代码中查找经过的时间 累积同名定时器 要将名称添加到您的 Python 计时器,您需要对 timer.py 进行另外两项更改。首先,Timer 应该接受名称作为参数。其次,当计时器停止时,应将经过的时间添加到 .timers 中:

类定时器: 计时器 = dict()

def __init__(
    self,name=None,text="Elapsed time: {:0.4f} seconds",logger=print,):
    self._start_time = None
    self.name = name
    self.text = text
    self.logger = logger

    # Add new named timers to dictionary of timers
    if name:
        self.timers.setdefault(name,0)

# Other methods are unchanged

def stop(self):
    """Stop the timer,and report the elapsed time"""
    if self._start_time is None:
        raise TimerError(f"Timer is not running. Use .start() to start it")

    elapsed_time = time.perf_counter() - self._start_time
    self._start_time = None

    if self.logger:
        self.logger(self.text.format(elapsed_time))
    if self.name:
        self.timers[self.name] += elapsed_time

    return elapsed_time

请注意,将新的 Python 计时器添加到 .timers 时,您使用了 .setdefault()。这是一个很棒的功能,它仅在字典中尚未定义名称时才设置值。如果名称已在 .timers 中使用,则该值保持不变。这允许您累积多个计时器:

>>> from timer import Timer
>>> t = Timer("accumulate")
>>> t.start()

>>> t.stop()  # A few seconds later
Elapsed time: 3.7036 seconds
3.703554293999332

>>> t.start()

>>> t.stop()  # A few seconds later
Elapsed time: 2.3449 seconds
2.3448921170001995

>>> Timer.timers
{'accumulate': 6.0484464109995315}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...