2009-05-20 89 views
55

我正在用python編程windows,並希望準確測量函數運行所需的時間。我寫了一個函數「time_it」,它接受另一個函數,運行它並返回運行所需的時間。python中函數的準確定時

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

我把這1000次稱爲平均值。 (最後的1000常數以毫秒爲單位給出答案)

此功能似乎有效,但我有這種嘮叨的感覺,我做錯了什麼,而且通過這樣做我正在使用比運行時實際使用的更多時間。

有沒有一個更標準或接受的方式來做到這一點?

當我改變我的測試函數來調用打印,以便它需要更長時間時,我的time_it函數返回平均2.5毫秒,而cProfile.run('f()')返回並平均爲7.0毫秒。我想我的功能會高估時間,如果有的話,這裏發生了什麼?

另外需要注意的是,我關心的是相互比較的功能相對時間,而不是絕對時間,因爲硬件和其他因素會明顯改變。

回答

33

而不是寫自己的監測代碼的,我建議你檢查出內置Python分析器(profilecProfile,根據您的需要):http://docs.python.org/library/profile.html

+0

忽略我 - 該字符串不是函數名稱,它是一個eval'd代碼塊。所以你可以用它來快速計時。這是正確的答案。而在其他消息中 - 「不是」比「!=」快得多 - 但可能有其他影響。 – 2012-06-11 10:20:09

+1

關於該切線 - 使用之前「不是」的任何聰明 - 記住這一點 - http://stackoverflow.com/questions/1392433/python-why-is-hello-is-hello – 2012-06-11 10:41:43

65

使用從Python標準庫。

基本用法:

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 
+2

我想我的功能被稱爲不同的爭論,但是當我用不同的論證調用t = timeit.Timer(「f()」,「from ___main___ import f」)並再次運行t.timeit(10000)時,我得到了相同的結果,儘管不同的論證應該導致運行時間非常不同。 – 2009-05-20 20:47:39

20

此代碼是非常不準確的

total= 0 
for i in range(1000): 
    start= time.clock() 
    function() 
    end= time.clock() 
    total += end-start 
time= total/1000 

此代碼是不太準確的測量偏差

start= time.clock() 
for i in range(1000): 
    function() 
end= time.clock() 
time= (end-start)/1000 

非常不準確遭受如果運行時的功能接近於時鐘的精度。大部分測量時間僅僅是0到幾個時鐘週期之間的隨機數。

根據您的系統工作負載,您從單個函數觀察到的「時間」可能完全是操作系統調度和其他不可控制的開銷的人爲因素。

第二個版本(不太不準確)的測量偏差較小。如果你的功能非常快,你可能需要運行10,000次,以減少操作系統調度和其他開銷。

當然,這兩者都是非常具有誤導性的。程序的運行時間 - 整體而言 - 不是函數運行時間的總和。您只能使用數字進行相對比較。它們不是絕對的量度,傳達了很多意義。

+1

爲什麼/ 1000? time.clock()方法返回秒數作爲浮點值。如果你期望它返回毫秒,這是有道理的,但是除以1000轉換爲千位,這是我以前從未見過的單位。 – pixelgrease 2014-08-15 20:25:19

+0

@pixelgrease milli/1000 =微,而不是公斤:) – stenci 2014-10-22 20:06:17

13

如果您想要測量python方法,即使您測量的塊可能會丟失,一個好方法是使用with語句。定義一些Timer類作爲

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 

那麼你可能需要的時間可能會拋出一個連接方法。使用

import httplib 

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

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

__exit()__即使連接請求流逝,方法也會被調用。更準確地說,你必須使用tryfinally看到情況下,它拋出的結果是,與

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

More details here.

23

您可以創建一個像這樣

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) 
6
一個「timeme」裝飾

這是整潔的

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" 
4

類似於@ AlexMartelli的回答

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

可以做到這一點。