关于测试:python中函数的准确计时

Accurate timing of functions in python

我正在Windows上用python编程,希望准确地测量函数运行所需的时间。我编写了一个函数"timeu it",它接受另一个函数,运行它,并返回运行它所花费的时间。

1
2
3
4
def time_it(f, *args):
    start = time.clock()
    f(*args)
    return (time.clock() - start)*1000

我称之为1000次,平均结果。(结尾的1000个常量以毫秒为单位给出答案。)

这个函数似乎可以工作,但是我有一种烦躁的感觉,我在做一些错误的事情,通过这样做,我使用的时间比函数运行时实际使用的时间要多。

有没有一种更为标准或公认的方法来做到这一点?

当我将测试函数更改为调用打印以便花费更长时间时,我的时间函数返回平均2.5 ms,而cprofile.run("f()")返回平均7.0 ms。我认为我的函数会高估时间,如果有什么情况,这里会发生什么?

另一个注意事项是,我关心的是函数之间的相对时间,而不是绝对时间,因为这明显取决于硬件和其他因素。


使用python标准库中的timeit模块。

基本用途:

1
2
3
4
5
6
7
8
9
from timeit import Timer

# first argument is the code to be run, the second"setup" argument is only run once,
# and it not included in the execution time.
t = Timer("""x.index(123)""", setup="""x = range(1000)""")

print t.timeit() # prints float, for example 5.8254
# ..or..
print t.timeit(1000) # repeat 1000 times instead of the default 1million


我建议您检查内置的python profiler(profilecProfile,这取决于您的需要),而不是编写自己的分析代码:http://docs.python.org/library/profile.html


你可以这样做一个"TimeMe"装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import time                                                

def timeme(method):
    def wrapper(*args, **kw):
        startTime = int(round(time.time() * 1000))
        result = method(*args, **kw)
        endTime = int(round(time.time() * 1000))

        print(endTime - startTime,'ms')
        return result

    return wrapper

@timeme
def func1(a,b,c = 'c',sleep = 1):
    time.sleep(sleep)
    print(a,b,c)

func1('a','b','c',0)
func1('a','b','c',0.5)
func1('a','b','c',0.6)
func1('a','b','c',1)


这个代码非常不准确

1
2
3
4
5
6
7
total= 0
for i in range(1000):
    start= time.clock()
    function()
    end= time.clock()
    total += end-start
time= total/1000

这个代码不太准确

1
2
3
4
5
start= time.clock()
for i in range(1000):
    function()
end= time.clock()
time= (end-start)/1000

如果函数的运行时间接近时钟的精度,那么非常不准确的函数会受到测量偏差的影响。大多数被测量的时间只是时钟0到几滴答之间的随机数。

根据系统工作负载的不同,从单个函数观察到的"时间"可能完全是操作系统调度和其他不可控制开销的产物。

第二个版本(不太准确)的测量偏差较小。如果您的函数真的很快,您可能需要运行它10000次,以减少操作系统调度和其他开销。

当然,这两种方法都具有严重的误导性。程序的运行时间(作为一个整体)不是函数运行时间的总和。您只能使用这些数字进行相对比较。它们不是绝对的度量标准,没有多大意义。


如果您想给python方法计时,即使您度量的块可能抛出,一个好的方法是使用with语句。将一些Timer类定义为

1
2
3
4
5
6
7
8
9
10
import time

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self

    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

然后,您可能需要对可能抛出的连接方法进行计时。使用

1
2
3
4
5
6
7
import httplib

with Timer() as t:
    conn = httplib.HTTPConnection('google.com')
    conn.request('GET', '/')

print('Request took %.03f sec.' % t.interval)

即使连接请求失败,也将调用__exit()__方法。更准确地说,您应该使用tryfinally来查看结果,以防它抛出,就像

1
2
3
4
5
6
try:
    with Timer() as t:
        conn = httplib.HTTPConnection('google.com')
        conn.request('GET', '/')
finally:
    print('Request took %.03f sec.' % t.interval)

更多详细信息。


这个比较干净

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from contextlib import contextmanager

import time
@contextmanager
def timeblock(label):
    start = time.clock()
    try:
        yield
    finally:
        end = time.clock()
        print ('{} : {}'.format(label, end - start))



with timeblock("just a test"):
            print"yippee"


类似于@alexmartelli的答案

1
2
import timeit
timeit.timeit(fun, number=10000)

能做到这一点。