如何在Python中测量经过时间?


1205

我想要的是开始在我的代码中的某个地方开始计时,然后获取经过的时间,以衡量执行少量功能所花费的时间。我认为我使用的timeit模块错误,但是文档对我来说却很混乱。

import timeit

start = timeit.timeit()
print("hello")
end = timeit.timeit()
print(end - start)

Answers:


1451

如果您只想测量两点之间经过的挂钟时间,则可以使用 time.time()

import time

start = time.time()
print("hello")
end = time.time()
print(end - start)

这给出了执行时间(以秒为单位)。

自3.3起,另一个选择可能是使用perf_counterprocess_time,具体取决于您的要求。在3.3之前,建议使用time.clock(感谢Amber)。但是,目前不推荐使用:

在Unix上,以秒为单位返回当前处理器时间,以浮点数表示。精度,实际上是“处理器时间”含义的确切定义,取决于同名C函数的精度。

在Windows上,基于Win32函数,此函数返回自第一次调用此函数以来经过的时间(以秒为单位),以浮点数表示。 QueryPerformanceCounter()。分辨率通常优于一微秒。

从版本3.3开始不推荐使用:此功能的行为取决于平台:根据需要,使用perf_counter()process_time()相反,以具有明确定义的行为。


17
并以毫秒为单位,使用datetime.time()
印加

110
(对于性能测量,time.clock()实际上是首选,因为它不会因系统时钟混乱而受到干扰,但.time()实际上可以达到相同的目的。)
琥珀色

4
我认为python -mtimeit会更好,因为它可以运行更多次,并且可以作为一种本地方法来测量python中的时间
Visgean Skeloru 2014年

4
有没有一种很好的方法可以将以秒为单位的执行时间转换为类似HH:MM :: SS的内容?
Danijel

12
@Danijel: print(timedelta(seconds=execution_time))。虽然这是一个单独的问题。
jfs 2016年

685

使用timeit.default_timer代替timeit.timeit。前者会自动在您的平台和Python版本上提供最佳时钟:

from timeit import default_timer as timer

start = timer()
# ...
end = timer()
print(end - start) # Time in seconds, e.g. 5.38091952400282

根据操作系统,将timeit.default_timer分配给time.time()或time.clock()。在Python 3.3+ 上,在所有平台上,default_timertime.perf_counter()。参见Python-time.clock()与time.time()-准确性?

也可以看看:


28
极好的答案-使用timeit会产生更准确的结果,因为它将自动考虑垃圾收集和操作系统差异之类的问题
lkgarrison

1
这会以毫秒或秒为单位吗?
凯蒂

3
@KhushbooTiwari,以秒为单位。
jfs

5
我认为需要添加官方文档中的注释default_timer() measurations can be affected by other programs running on the same machine, so the best thing to do when accurate timing is necessary is to repeat the timing a few times and use the best time. The -r option is good for this; the default of 3 repetitions is probably enough in most cases. On Unix, you can use time.clock() to measure CPU time.
-KGS

1
@KGS:绩效衡量以一种微妙的方式非常棘手(很容易误导自己)。这里还有许多其他相关的评论。按照答案中的链接。您可能还对提供相同接口的perf模块(在回答时不存在)感兴趣,但是有时它与timeit模块如何测量时间性能的决定会有所不同。
jfs

128

仅限Python 3:

由于从Python 3.3开始不推荐使用 time.clock(),因此您将希望time.perf_counter()用于系统范围的计时或time.process_time()进程范围的计时,就像您以前使用的方式一样time.clock()

import time

t = time.process_time()
#do some stuff
elapsed_time = time.process_time() - t

新功能process_time将不包括睡眠期间经过的时间。


28
使用timeit.default_timer代替time.perf_counter。前者将选择适当的计时器来衡量针对您的平台和Python版本调整的时间性能。process_time()确实睡眠时包括时间,因此是不恰当的测量经过时间。
jfs 2015年

2
我正在使用Pierre建议的实现,以秒为单位给出值吗?
ugotchi '16

这个答案似乎是题外话(嗯,这个问题不是很具体)。有两个“时间”度量:两点之间的挂钟时间,表示该进程的cpu消耗。
富兰克林·皮亚特

87

有了您想计时的功能,

test.py:

def foo(): 
    # print "hello"   
    return "hello"

最简单的使用方法timeit是从命令行调用它:

% python -mtimeit -s'import test' 'test.foo()'
1000000 loops, best of 3: 0.254 usec per loop

请勿尝试使用time.timetime.clock(天真)比较功能的速度。他们可能会产生误导性的结果

PS。不要将打印语句放在您希望计时的函数中;否则,测量的时间将取决于终端速度


65

使用上下文管理器执行此操作很有趣,该上下文管理器会自动记住进入with块时的开始时间,然后冻结块退出时的结束时间。只需一点技巧,您甚至可以通过相同的上下文管理器功能在块内获得运行时间计数。

核心库没有这个(但应该这样做)。放置到位后,您可以执行以下操作:

with elapsed_timer() as elapsed:
    # some lengthy code
    print( "midpoint at %.2f seconds" % elapsed() )  # time so far
    # other lengthy code

print( "all done at %.2f seconds" % elapsed() )

这是足以完成此任务的contextmanager代码:

from contextlib import contextmanager
from timeit import default_timer

@contextmanager
def elapsed_timer():
    start = default_timer()
    elapser = lambda: default_timer() - start
    yield lambda: elapser()
    end = default_timer()
    elapser = lambda: end-start

还有一些可运行的演示代码:

import time

with elapsed_timer() as elapsed:
    time.sleep(1)
    print(elapsed())
    time.sleep(2)
    print(elapsed())
    time.sleep(3)

请注意,根据该函数的设计,的退出值将elapsed()在块退出时冻结,并且进一步的调用将返回相同的持续时间(在此玩具示例中约为6秒)。


2
其他情况管理器例如:dabeaz.blogspot.fr/2010/02/...
杰罗姆

1
@Jérôme很好的例子-我将其改编为另一个答案-stackoverflow.com/a/41408510/243392
Brian Burns

62

以秒为单位的测量时间

from timeit import default_timer as timer
from datetime import timedelta

start = timer()
end = timer()
print(timedelta(seconds=end-start))

输出

0:00:01.946339

1
这是最简洁,输出最干净的答案。
刘德华

56

我喜欢这个。timeitdoc太混乱了。

from datetime import datetime 

start_time = datetime.now() 

# INSERT YOUR CODE 

time_elapsed = datetime.now() - start_time 

print('Time elapsed (hh:mm:ss.ms) {}'.format(time_elapsed))

请注意,这里没有任何格式,我只是写到hh:mm:ss打印输出中,以便可以解释time_elapsed


有人告诉我timeit计算CPU时间,datetime是否也考虑了使用的CPU时间?这些是同一回事吗?
Sreehari R

3
用这种方法测量经过时间是有风险的,因为datetime.now()可以在两个调用之间更改,原因是网络时间同步,夏令时切换或用户调整时钟。
user1318499 '19

45

这是执行此操作的另一种方法:

>> from pytictoc import TicToc
>> t = TicToc() # create TicToc instance
>> t.tic() # Start timer
>> # do something
>> t.toc() # Print elapsed time
Elapsed time is 2.612231 seconds.

与传统方式比较:

>> from time import time
>> t1 = time()
>> # do something
>> t2 = time()
>> elapsed = t2 - t1
>> print('Elapsed time is %f seconds.' % elapsed)
Elapsed time is 2.612231 seconds.

安装:

pip install pytictoc

有关更多详细信息,请参阅PyPi页面


13
最好说明使用此库相对于其他方法的优势。
hlg

嵌套功能实际上已损坏。我打开了一个问题,描述了代码中的问题在哪里,但是回购在一年内没有得到维护,因此我不希望有任何更改。
PetarMI '19

我发现嵌套有点混乱。如果我遇到了t.tic()埋在代码中的问题,则由开发人员自行决定在该系列文章的预期范围之内。您是否发现自己设置了嵌套或只是多个tictocs?
ScottieB

1
@PetarMI:仅供参考,我刚刚解决了这个问题ttictoc。我一团糟,但现在应该很好。
H.Sánchez

32

这是我在这里经过许多不错的回答以及其他几篇文章后的发现。

首先,如果您在timeit和之间进行辩论time.time,则timeit有两个优点:

  1. timeit 选择操作系统和Python版本上可用的最佳计时器。
  2. timeit 禁用垃圾收集,但是,这不是您可能想要或不想要的东西。

现在的问题是,timeit使用起来并不是那么简单,因为它需要设置,并且当您进行大量导入时,情况变得很糟。理想情况下,您只需要一个装饰器或使用with块并测量时间。不幸的是,对此没有内置的功能,因此您有两个选择:

选项1:使用时间预算库

timebudget是一个多功能的,非常简单的库,你可以在一行代码只使用PIP后安装。

@timebudget  # Record how long this function takes
def my_method():
    # my code

选项2:直接使用代码模块

我在下面创建了小实用程序模块。

# utils.py
from functools import wraps
import gc
import timeit

def MeasureTime(f, no_print=False, disable_gc=False):
    @wraps(f)
    def _wrapper(*args, **kwargs):
        gcold = gc.isenabled()
        if disable_gc:
            gc.disable()
        start_time = timeit.default_timer()
        try:
            result = f(*args, **kwargs)
        finally:
            elapsed = timeit.default_timer() - start_time
            if disable_gc and gcold:
                gc.enable()
            if not no_print:
                print('"{}": {}s'.format(f.__name__, elapsed))
        return result
    return _wrapper

class MeasureBlockTime:
    def __init__(self,name="(block)", no_print=False, disable_gc=False):
        self.name = name
        self.no_print = no_print
        self.disable_gc = disable_gc
    def __enter__(self):
        self.gcold = gc.isenabled()
        if self.disable_gc:
            gc.disable()
        self.start_time = timeit.default_timer()
    def __exit__(self,ty,val,tb):
        self.elapsed = timeit.default_timer() - self.start_time
        if self.disable_gc and self.gcold:
            gc.enable()
        if not self.no_print:
            print('Function "{}": {}s'.format(self.name, self.elapsed))
        return False #re-raise any exceptions

现在,您只需在其前面放置装饰器即可计时任何功能:

import utils

@utils.MeasureTime
def MyBigFunc():
    #do something time consuming
    for i in range(10000):
        print(i)

如果要计时部分代码,只需将其放在代码with块中:

import utils

#somewhere in my code

with utils.MeasureBlockTime("MyBlock"):
    #do something time consuming
    for i in range(10000):
        print(i)

# rest of my code

优点:

有几个半支持的版本,所以我想指出一些重点:

  1. 出于前面所述的原因,请使用timeit中的timer代替time.time。
  2. 如果需要,可以在计时期间禁用GC。
  3. 装饰器接受带有已命名或未命名参数的函数。
  4. 能够按块定时禁用打印(先使用with utils.MeasureBlockTime() as t,然后再使用t.elapsed)。
  5. 能够为块定时保持启用gc的能力。

28

使用 time.time度量执行可以为您提供命令的整体执行时间,包括计算机上其他进程花费的运行时间。这是用户注意到的时间,但是如果您要比较不同的代码段/算法/函数/ ...,则效果不佳。

有关更多信息timeit

如果您想对配置文件有更深入的了解:

更新:去年我大量使用了http://pythonhosted.org/line_profiler/,发现它非常有用,建议使用它代替Pythons profile模块。


19

这是一个微小的计时器类,它返回“ hh:mm:ss”字符串:

class Timer:
  def __init__(self):
    self.start = time.time()

  def restart(self):
    self.start = time.time()

  def get_time_hhmmss(self):
    end = time.time()
    m, s = divmod(end - self.start, 60)
    h, m = divmod(m, 60)
    time_str = "%02d:%02d:%02d" % (h, m, s)
    return time_str

用法:

# Start timer
my_timer = Timer()

# ... do something

# Get time string:
time_hhmmss = my_timer.get_time_hhmmss()
print("Time elapsed: %s" % time_hhmmss )

# ... use the timer again
my_timer.restart()

# ... do something

# Get time:
time_hhmmss = my_timer.get_time_hhmmss()

# ... etc

17

python cProfile和pstats模块为测量某些功能所经过的时间提供了强大的支持,而无需在现有功能周围添加任何代码。

例如,如果您有python脚本timeFunctions.py:

import time

def hello():
    print "Hello :)"
    time.sleep(0.1)

def thankyou():
    print "Thank you!"
    time.sleep(0.05)

for idx in range(10):
    hello()

for idx in range(100):
    thankyou()

要运行事件探查器并为文件生成统计信息,您可以运行:

python -m cProfile -o timeStats.profile timeFunctions.py

这样做是使用cProfile模块来分析timeFunctions.py中的所有功能,并在timeStats.profile文件中收集统计信息。请注意,我们不必向现有模块(timeFunctions.py)添加任何代码,并且可以使用任何模块来完成此操作。

拥有stats文件后,您可以按以下方式运行pstats模块:

python -m pstats timeStats.profile

这将运行交互式统计浏览器,从而为您提供许多不错的功能。对于您的特定用例,您可以只检查功能的统计信息。在我们的示例中,检查这两个功能的统计信息显示以下内容:

Welcome to the profile statistics browser.
timeStats.profile% stats hello
<timestamp>    timeStats.profile

         224 function calls in 6.014 seconds

   Random listing order was used
   List reduced from 6 to 1 due to restriction <'hello'>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10    0.000    0.000    1.001    0.100 timeFunctions.py:3(hello)

timeStats.profile% stats thankyou
<timestamp>    timeStats.profile

         224 function calls in 6.014 seconds

   Random listing order was used
   List reduced from 6 to 1 due to restriction <'thankyou'>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      100    0.002    0.000    5.012    0.050 timeFunctions.py:7(thankyou)

这个虚拟的例子并没有做太多,但是让您知道可以做什么。关于这种方法的最好之处在于,我不必编辑任何现有代码即可获得这些数字,并且显然可以帮助进行性能分析。


所有这些都很好,但是AFAICT仍然可以测量CPU时间,而不是挂钟时间。
ShreevatsaR 2014年

1
实际上有些混乱;默认情况下,cProfile确实会查看时钟时间。我赞成你的回答。
ShreevatsaR 2014年

仅供参考:如果您python -m pstats timeStats.profile ValueError: bad marshal data (unknown type code)检查自己的python版本,则说明您正在运行。我得到这个,当我跑python3 -m cProfile...python -m pstats。我的错误让我有一秒钟的忙,所以,我想分享一下don't forget consistency。=)
JayRizzo

17

这是另一个用于计时代码的上下文管理器-

用法:

from benchmark import benchmark

with benchmark("Test 1+1"):
    1+1
=>
Test 1+1 : 1.41e-06 seconds

或者,如果您需要时间值

with benchmark("Test 1+1") as b:
    1+1
print(b.time)
=>
Test 1+1 : 7.05e-07 seconds
7.05233786763e-07

Benchmark.py

from timeit import default_timer as timer

class benchmark(object):

    def __init__(self, msg, fmt="%0.3g"):
        self.msg = msg
        self.fmt = fmt

    def __enter__(self):
        self.start = timer()
        return self

    def __exit__(self, *args):
        t = timer() - self.start
        print(("%s : " + self.fmt + " seconds") % (self.msg, t))
        self.time = t

改编自http://dabeaz.blogspot.fr/2010/02/context-manager-for-timing-benchmarks.html


17

使用探查器模块。它提供了非常详细的配置文件。

import profile
profile.run('main()')

它输出类似:

          5 function calls in 0.047 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 :0(exec)
        1    0.047    0.047    0.047    0.047 :0(setprofile)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        0    0.000             0.000          profile:0(profiler)
        1    0.000    0.000    0.047    0.047 profile:0(main())
        1    0.000    0.000    0.000    0.000 two_sum.py:2(twoSum)

我发现它非常有用。


1
什么main()啊 如果您可以提供一个简单的代码示例,它将更加有用。
not2qubit

15

我喜欢它简单(python 3):

from timeit import timeit

timeit(lambda: print("hello"))

单个执行的输出为微秒

2.430883963010274

说明:timeit 默认执行匿名函数一百万次,结果以秒为单位。因此,单次执行的结果是相同的数量,但平均为微秒


对于缓慢的操作添加较低数量的迭代,或者你可能会永远等待:

import time

timeit(lambda: time.sleep(1.5), number=1)

输出总是在数目迭代:

1.5015795179999714

14

(仅对于Ipython)可以使用%timeit来衡量平均处理时间:

def foo():
    print "hello"

然后:

%timeit foo()

结果是这样的:

10000 loops, best of 3: 27 µs per loop

4
值得一提的是,可以将标志传递给%timeit,例如,-n指定应将代码重复多少次。
raacer

11

使用timeit的另一种方法:

from timeit import timeit

def func():
    return 1 + 1

time = timeit(func, number=1)
print(time)

10

在python3上:

from time import sleep, perf_counter as pc
t0 = pc()
sleep(1)
print(pc()-t0)

优雅而短暂。


这是什么?多发性硬化症?
KIC

9

有点超级后来的反应,但也许对某人有用。我认为这是一种超级干净的方法。

import time

def timed(fun, *args):
    s = time.time()
    r = fun(*args)
    print('{} execution took {} seconds.'.format(fun.__name__, time.time()-s))
    return(r)

timed(print, "Hello")

请记住,“ print”是Python 3中的功能,而不是Python 2.7中的功能。但是,它可以与任何其他功能一起使用。干杯!


如何打印很少的时间?我总会获得0.0秒的时间
罗兰·梅特齐(Roland Mtetezi)

您可以将其变成装饰器。这对我来说看起来更好。
丹尼尔·莫斯科维奇

8

您可以使用timeit。

这是一个有关如何使用Python REPL测试带参数的naive_func的示例:

>>> import timeit                                                                                         

>>> def naive_func(x):                                                                                    
...     a = 0                                                                                             
...     for i in range(a):                                                                                
...         a += i                                                                                        
...     return a                                                                                          

>>> def wrapper(func, *args, **kwargs):                                                                   
...     def wrapper():                                                                                    
...         return func(*args, **kwargs)                                                                  
...     return wrapper                                                                                    

>>> wrapped = wrapper(naive_func, 1_000)                                                                  

>>> timeit.timeit(wrapped, number=1_000_000)                                                              
0.4458435332577161  

如果function没有任何参数,则不需要包装函数。


1
一个lambda会更简洁:print(timeit.timeit(lambda: naive_func(1_000), number=1_000_000))
西罗桑蒂利冠状病毒审查六四事件法轮功

7

我们还可以将时间转换为人类可以理解的时间。

import time, datetime

start = time.clock()

def num_multi1(max):
    result = 0
    for num in range(0, 1000):
        if (num % 3 == 0 or num % 5 == 0):
            result += num

    print "Sum is %d " % result

num_multi1(1000)

end = time.clock()
value = end - start
timestamp = datetime.datetime.fromtimestamp(value)
print timestamp.strftime('%Y-%m-%d %H:%M:%S')

6

我为此创建了一个库,如果您想测量一个函数,可以像这样做


from pythonbenchmark import compare, measure
import time

a,b,c,d,e = 10,10,10,10,10
something = [a,b,c,d,e]

@measure
def myFunction(something):
    time.sleep(0.4)

@measure
def myOptimizedFunction(something):
    time.sleep(0.2)

myFunction(input)
myOptimizedFunction(input)

https://github.com/Karlheinzniebuhr/pythonbenchmark


6

要了解每个函数的递归调用,请执行以下操作:

%load_ext snakeviz
%%snakeviz

它只需在Jupyter笔记本中使用这两行代码,并生成一个漂亮的交互式图表。例如:

在此处输入图片说明

这是代码。再次,以2开头的%行是使用snakeviz所需的仅有的额外代码行:

# !pip install snakeviz
%load_ext snakeviz
import glob
import hashlib

%%snakeviz

files = glob.glob('*.txt')
def print_files_hashed(files):
    for file in files:
        with open(file) as f:
            print(hashlib.md5(f.read().encode('utf-8')).hexdigest())
print_files_hashed(files)

在笔记本外运行snakeviz也似乎是可能的。在snakeviz网站上有更多信息。


2
import time

def getElapsedTime(startTime, units):
    elapsedInSeconds = time.time() - startTime
    if units == 'sec':
        return elapsedInSeconds
    if units == 'min':
        return elapsedInSeconds/60
    if units == 'hour':
        return elapsedInSeconds/(60*60)

2

这种独特的基于类的方法提供了可打印的字符串表示形式,可自定义的舍入功能,并且可以方便地访问作为字符串或浮点数的经过时间。它是使用Python 3.7开发的。

import datetime
import timeit


class Timer:
    """Measure time used."""
    # Ref: https://stackoverflow.com/a/57931660/

    def __init__(self, round_ndigits: int = 0):
        self._round_ndigits = round_ndigits
        self._start_time = timeit.default_timer()

    def __call__(self) -> float:
        return timeit.default_timer() - self._start_time

    def __str__(self) -> str:
        return str(datetime.timedelta(seconds=round(self(), self._round_ndigits)))

用法:

# Setup timer
>>> timer = Timer()

# Access as a string
>>> print(f'Time elapsed is {timer}.')
Time elapsed is 0:00:03.
>>> print(f'Time elapsed is {timer}.')
Time elapsed is 0:00:04.

# Access as a float
>>> timer()
6.841332235
>>> timer()
7.970274425

1

测量小代码段的执行时间。

时间单位以秒为单位,以浮点数表示

import timeit
t = timeit.Timer('li = list(map(lambda x:x*2,[1,2,3,4,5]))')
t.timeit()
t.repeat()
>[1.2934070999999676, 1.3335035000000062, 1.422568500000125]

repeat()方法可以方便地多次调用timeit()并返回结果列表。

repeat(repeat=3

有了这个清单,我们可以花很多时间。

默认情况下,timeit()在计时期间临时关闭垃圾收集。time.Timer()解决了这个问题。

优点:

timeit.Timer()使独立计时更具可比性。gc可能是被测功能性能的重要组成部分。如果是这样,可以将gc(垃圾收集器)作为设置字符串中的第一条语句重新启用。例如:

timeit.Timer('li = list(map(lambda x:x*2,[1,2,3,4,5]))',setup='gc.enable()')

Python文档


1

如果您希望能够方便地计时功能,可以使用一个简单的装饰器:

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        original_return_val = func(*args, **kwargs)
        end = time.time()
        print("time elapsed in ", func.__name__, ": ", end - start, sep='')
        return original_return_val

    return wrapper

您可以在想要计时的函数上使用它,如下所示:

@timing_decorator
def function_to_time():
    time.sleep(1)

然后function_to_time,在您每次调用时,它都会打印花费了多长时间以及函数的名称。


1

基于https://stackoverflow.com/a/30024601/5095636提供的contextmanager解决方案,以下是lambda免费版本,因为flake8根据E731警告使用lambda :

from contextlib import contextmanager
from timeit import default_timer

@contextmanager
def elapsed_timer():
    start_time = default_timer()

    class _Timer():
      start = start_time
      end = default_timer()
      duration = end - start

    yield _Timer

    end_time = default_timer()
    _Timer.end = end_time
    _Timer.duration = end_time - start_time

测试:

from time import sleep

with elapsed_timer() as t:
    print("start:", t.start)
    sleep(1)
    print("end:", t.end)

t.start
t.end
t.duration

0

我能想到的唯一方法是使用time.time()

import time
start = time.time()
sleep(5) #just to give it some delay to show it working
finish = time.time()
elapsed = finish - start
print(elapsed)

希望对您有所帮助。


0

timeit模块非常适合计时一小段Python代码。它至少可以以三种形式使用:

1-作为命令行模块

python2 -m timeit 'for i in xrange(10): oct(i)' 

2-对于短代码,请将其作为参数传递。

import timeit
timeit.Timer('for i in xrange(10): oct(i)').timeit()

3-对于更长的代码为:

import timeit
code_to_test = """
a = range(100000)
b = []
for i in a:
    b.append(i*2)
"""
elapsed_time = timeit.timeit(code_to_test, number=100)/100
print(elapsed_time)
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.