2009-05-20 48 views
55

Tôi đang lập trình trong python trên các cửa sổ và muốn đo chính xác thời gian cần cho một hàm để chạy. Tôi đã viết một hàm "time_it" có chức năng khác, chạy nó và trả về thời gian cần thiết để chạy.Thời gian chính xác của các hàm trong python

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

tôi gọi 1000 lần này và tính trung bình kết quả. (hằng số 1000 ở cuối là trả lời bằng mili giây.)

Chức năng này có vẻ hoạt động nhưng tôi có cảm giác dai dẳng rằng tôi đang làm điều gì sai, và bằng cách làm theo cách này, tôi đang sử dụng nhiều thời gian hơn chức năng thực sự sử dụng khi hoạt động của nó.

Có cách nào khác hơn hoặc được chấp nhận để thực hiện việc này?

Khi tôi thay đổi chức năng thử nghiệm để gọi in sao cho nó mất nhiều thời gian hơn, hàm time_it của tôi trả về trung bình 2,5 ms trong khi cProfile.run ('f()') trả về và trung bình 7,0 ms. Tôi cho rằng chức năng của tôi sẽ đánh giá quá cao thời gian nếu có gì, chuyện gì đang xảy ra ở đây?

Một lưu ý bổ sung, đó là thời gian tương đối của các hàm so với nhau mà tôi quan tâm, chứ không phải thời gian tuyệt đối vì điều này rõ ràng sẽ thay đổi tùy theo phần cứng và các yếu tố khác.

Trả lời

33

Thay vì viết mã hồ sơ riêng của bạn, tôi khuyên bạn nên kiểm tra profilers Python built-in (profile hoặc cProfile, tùy thuộc vào nhu cầu của bạn): http://docs.python.org/library/profile.html

+0

Bỏ qua tôi - chuỗi đó không phải là tên hàm, nó là một khối mã được đánh giá. Vì vậy, bạn có thể sử dụng nó cho thời gian nhanh chóng. Đây là câu trả lời đúng. Và trong các tin tức khác - "không phải" nhanh hơn đáng kể so với "! =" - nhưng có thể có những tác động khác. –

+1

Và tắt trên đó ốp - trước khi sử dụng "không phải là" cho bất cứ điều gì thông minh - chịu điều này trong tâm trí - http://stackoverflow.com/questions/1392433/python-why-is-hello-is-hello –

65

Sử dụng từ thư viện chuẩn của Python.

sử dụng cơ bản:

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ôi muốn chức năng của tôi được gọi với các tranh cãi khác nhau, nhưng khi tôi gọi t = timeit.Timer ("f()", "từ ___main___ import f") với các đối số khác nhau và chạy t.timeit (10000) một lần nữa, tôi nhận được kết quả tương tự mặc dù các tranh luận khác nhau sẽ dẫn đến các thời gian hoạt động rất khác nhau. –

20

Mã này là rất không chính xác

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

Mã này là ít chính xác

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

Các đau khổ rất chính xác từ thiên vị đo lường nếu thời gian chạy của hàm gần với độ chính xác của đồng hồ. Hầu hết thời gian đo được chỉ là số ngẫu nhiên giữa 0 và một vài dấu tích của đồng hồ.

Tùy thuộc vào khối lượng công việc hệ thống của bạn, "thời gian" mà bạn quan sát từ một chức năng có thể hoàn toàn là tạo tác vụ lập lịch OS và các chi phí không kiểm soát được khác.

Phiên bản thứ hai (không chính xác) có độ lệch đo lường ít hơn. Nếu chức năng của bạn thực sự nhanh, bạn có thể cần phải chạy nó 10.000 lần để giảm thiểu việc lên lịch hệ điều hành và các chi phí khác.

Cả hai đều, tất nhiên, gây hiểu lầm khủng khiếp. Thời gian chạy cho chương trình của bạn - nói chung - không phải là tổng của thời gian chạy hàm. Bạn chỉ có thể sử dụng các con số để so sánh tương đối. Chúng không phải là những phép đo tuyệt đối truyền đạt nhiều ý nghĩa.

+1

Tại sao/1000? Phương thức time.clock() trả về giây làm giá trị dấu chấm động. Nếu bạn mong đợi nó trở lại mili giây, điều này sẽ có ý nghĩa, tuy nhiên chia cho 1000 chuyển đổi thành kilô-giây, một đơn vị mà tôi chưa từng thấy trước đây. – pixelgrease

+0

@pixelgrease milli/1000 = micro, không phải kilo :) – stenci

+0

@stenci Anh ấy đề xuất rằng giá trị kết quả bằng giây, ví dụ: 1000 giây. Nếu bạn chia nó cho 1000, bạn sẽ nhận được 1 "kiloseconds". – zehelvion

13

Nếu bạn muốn thời gian một phương pháp python ngay cả khi khối bạn đo có thể ném, một cách tiếp cận tốt là sử dụng câu lệnh with. Xác định một số lớp học 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 

Sau đó, bạn có thể muốn thời gian có thể kết thúc.Sử dụng phương pháp

import httplib 

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

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

__exit()__ sẽ được gọi ngay cả khi yêu cầu kết nối bị gián đoạn. Chính xác hơn, bạn muốn có bạn sử dụng tryfinally để xem kết quả trong trường hợp nó ném, như với

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

Bạn có thể tạo một "timeme" trang trí như vậy

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

+ n cho câu trả lời này. Tôi ước có một lựa chọn như vậy. Làm đẹp là tôi có thể xuất kết quả nhật ký sang tệp bên ngoài và thêm vào bất kỳ nơi nào tôi cần! Cảm ơn một tỷ. – curlyreggie

+5

Điều này làm việc tuyệt vời cho các chức năng không đệ quy. Đối với các hàm đệ quy, nó trả về một thời gian cho mỗi lần lặp của hàm. – tachijuan

6

Đây là neater

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" 
+0

Câu trả lời hay, đơn giản và cho đến nay chỉ có một tôi đã tìm thấy cho phép bạn gửi một nhãn đến chức năng hẹn giờ. – Gabriel

+0

Giải pháp gọn gàng nhưng hơi quá phức tạp. Đã dành một giờ cố gắng tìm ra lý do tại sao 'time.sleep (10)' chỉ mất 0,002 giây để thực thi theo mã này. (btw, sự khác biệt lớn giữa 'time.clock()' và 'time.time()' trong python) – swdev

4

Tương tự như câu trả lời của @ AlexMartelli

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

có thể thực hiện thủ thuật.

Các vấn đề liên quan