Làm cách nào tôi có thể đặt thời gian cho một đoạn mã để kiểm tra hiệu năng với thời gian của Pythons?


161

Tôi có một kịch bản python hoạt động như bình thường, nhưng tôi cần phải viết thời gian thực hiện. Tôi đã googled rằng tôi nên sử dụng timeitnhưng dường như tôi không thể làm cho nó hoạt động.

Kịch bản Python của tôi trông như thế này:

import sys
import getopt
import timeit
import random
import os
import re
import ibm_db
import time
from string import maketrans
myfile = open("results_update.txt", "a")

for r in range(100):
    rannumber = random.randint(0, 100)

    update = "update TABLE set val = %i where MyCount >= '2010' and MyCount < '2012' and number = '250'" % rannumber
    #print rannumber

    conn = ibm_db.pconnect("dsn=myDB","usrname","secretPWD")

for r in range(5):
    print "Run %s\n" % r        
    ibm_db.execute(query_stmt)
 query_stmt = ibm_db.prepare(conn, update)

myfile.close()
ibm_db.close(conn)

Những gì tôi cần là thời gian cần thiết để thực hiện truy vấn và ghi nó vào tệp results_update.txt. Mục đích là để kiểm tra một tuyên bố cập nhật cho cơ sở dữ liệu của tôi với các chỉ mục và cơ chế điều chỉnh khác nhau.


Là / là câu hỏi của bạn cụ thể về timeit? Tôi đoán là không. Trong trường hợp đó, có lẽ bạn nên xóa "với thời gian Pythons" khỏi tiêu đề.
Martin Thoma

Câu trả lời:


274

Bạn có thể sử dụng time.time()hoặc time.clock()trước và sau khối bạn muốn thời gian.

import time

t0 = time.time()
code_block
t1 = time.time()

total = t1-t0

Phương pháp này không chính xác như timeit(nó không trung bình một vài lần chạy) nhưng nó rất đơn giản.

time.time() (trong Windows và Linux) và time.clock() (trong Linux) không đủ chính xác cho các chức năng nhanh (bạn có tổng = 0). Trong trường hợp này hoặc nếu bạn muốn tính trung bình thời gian trôi qua vài lần chạy, bạn phải gọi hàm theo cách thủ công nhiều lần (Như tôi nghĩ bạn đã làm trong mã ví dụ và thời gian tự động thực hiện khi bạn đặt đối số số của nó )

import time

def myfast():
   code

n = 10000
t0 = time.time()
for i in range(n): myfast()
t1 = time.time()

total_n = t1-t0

Trong Windows, như Corey đã nêu trong nhận xét, time.clock()có độ chính xác cao hơn nhiều (micro giây thay vì giây) và được ưa thích hơn time.time().


8
fyi trên windows, sử dụng time.clock () thay vì time.time ()
Corey Goldberg

4
Cảm ơn Corey, tại sao? bởi vì đồng hồ chính xác hơn (micro giây) hoặc có gì hơn?
joaquin

11
Bạn có thể sử dụng timeit.default_timer () để làm cho nền tảng mã của bạn độc lập; nó trả về time.clock () hoặc time.time () nếu phù hợp với HĐH.
Marc Stober

6
Thay vì chọn đồng hồ bằng tay, hãy sử dụng timeit.default_timer; Python đã hoàn thành công việc cho bạn. Nhưng thực sự, bạn nên sử dụng timeit.timeit(myfast, number=n)thay vì phát minh lại bánh xe cuộc gọi lặp đi lặp lại (và bỏ lỡ thực tế là timeitvô hiệu hóa trình thu gom rác trong khi chạy mã liên tục).
Martijn Pieters

15
cập nhật: time.clock () hiện không dùng nữa. Bây giờ bạn nên sử dụng time.time (). Trên thực tế, kể từ phiên bản 3.3, tùy chọn tốt nhất sẽ là time.perf_count ()
Madlozoz

42

Nếu bạn đang lược tả mã của mình và có thể sử dụng IPython, nó có chức năng kỳ diệu %timeit.

%%timeit hoạt động trên các tế bào.

In [2]: %timeit cos(3.14)
10000000 loops, best of 3: 160 ns per loop

In [3]: %%timeit
   ...: cos(3.14)
   ...: x = 2 + 3
   ...: 
10000000 loops, best of 3: 196 ns per loop

36

Ngoài thời gian, mã này bạn hiển thị đơn giản là không chính xác: bạn thực hiện 100 kết nối (hoàn toàn bỏ qua tất cả trừ kết nối cuối cùng), và sau đó khi bạn thực hiện cuộc gọi thực hiện đầu tiên, bạn chuyển cho nó một biến cục bộ query_stmtmà bạn chỉ khởi tạo sau khi thực hiện gọi.

Trước tiên, hãy làm cho mã của bạn chính xác, không phải lo lắng về thời gian: tức là một chức năng tạo hoặc nhận kết nối và thực hiện 100 hoặc 500 hoặc bất kỳ số lượng cập nhật nào trên kết nối đó, sau đó đóng kết nối. Một khi bạn có mã của bạn hoạt động chính xác là điểm chính xác để suy nghĩ về việc sử dụng timeitnó!

Cụ thể, nếu hàm bạn muốn thời gian là một hàm không tham số được gọi, foobarbạn có thể sử dụng timeit.timeit (2.6 trở lên - nó phức tạp hơn trong 2.5 và trước đó):

timeit.timeit('foobar()', number=1000)

Bạn nên chỉ định số lần chạy vì mặc định, một triệu, có thể cao cho trường hợp sử dụng của bạn (dẫn đến việc dành nhiều thời gian cho mã này ;-).


26
Sau khi vật lộn với điều này trong vài phút cuối, tôi muốn cho người xem trong tương lai biết rằng bạn cũng có thể muốn chuyển một biến thiết lập nếu chức năng của bạn foobarnằm trong một tệp chính. Như thế này: timeit.timeit('foobar()','from __main__ import foobar',number=1000)
Rich

3
Trong Python 2.7.8, bạn chỉ cần sử dụngtimeit.timeit( foobar, number=1000 )

9

Tập trung vào một điều cụ thể . Đĩa I / O chậm, vì vậy tôi sẽ đưa nó ra khỏi bài kiểm tra nếu tất cả những gì bạn định chỉnh sửa là truy vấn cơ sở dữ liệu.

Và nếu bạn cần thời gian thực hiện cơ sở dữ liệu của mình, thay vào đó hãy tìm các công cụ cơ sở dữ liệu, như hỏi về kế hoạch truy vấn và lưu ý rằng hiệu suất không chỉ thay đổi với truy vấn chính xác và chỉ mục nào bạn có, mà còn với tải dữ liệu (bao nhiêu dữ liệu bạn đã lưu trữ).

Điều đó nói rằng, bạn có thể chỉ cần đặt mã của mình vào một hàm và chạy hàm đó với timeit.timeit():

def function_to_repeat():
    # ...

duration = timeit.timeit(function_to_repeat, number=1000)

Điều này sẽ vô hiệu hóa bộ sưu tập rác, liên tục gọi function_to_repeat()hàm và thời gian tổng thời lượng của các cuộc gọi đó bằng cách sử dụngtimeit.default_timer() , đây là đồng hồ có sẵn chính xác nhất cho nền tảng cụ thể của bạn.

Bạn nên di chuyển mã thiết lập ra khỏi chức năng lặp lại; ví dụ: bạn nên kết nối với cơ sở dữ liệu trước, sau đó chỉ truy vấn thời gian. Sử dụng setupđối số để nhập hoặc tạo các phụ thuộc đó và chuyển chúng vào hàm của bạn:

def function_to_repeat(var1, var2):
    # ...

duration = timeit.timeit(
    'function_to_repeat(var1, var2)',
    'from __main__ import function_to_repeat, var1, var2', 
    number=1000)

sẽ lấy toàn cầu function_to_repeat, var1var2từ tập lệnh của bạn và chuyển chúng cho hàm mỗi lần lặp lại.


Đặt mã vào một hàm là một bước tôi đang tìm kiếm - chỉ đơn giản là tạo mã thành một chuỗi và evaling sẽ không bay cho bất cứ thứ gì không hoàn toàn tầm thường. thx
javadba

2

Tôi thấy câu hỏi đã được trả lời, nhưng vẫn muốn thêm 2 xu của tôi cho cùng.

Tôi cũng đã phải đối mặt với kịch bản tương tự trong đó tôi phải kiểm tra thời gian thực hiện cho một số cách tiếp cận và do đó đã viết một tập lệnh nhỏ, gọi thời gian trên tất cả các chức năng được viết trong đó.

Kịch bản cũng có sẵn như là ý chính của github ở đây .

Hy vọng nó sẽ giúp bạn và những người khác.

from random import random
import types

def list_without_comprehension():
    l = []
    for i in xrange(1000):
        l.append(int(random()*100 % 100))
    return l

def list_with_comprehension():
    # 1K random numbers between 0 to 100
    l = [int(random()*100 % 100) for _ in xrange(1000)]
    return l


# operations on list_without_comprehension
def sort_list_without_comprehension():
    list_without_comprehension().sort()

def reverse_sort_list_without_comprehension():
    list_without_comprehension().sort(reverse=True)

def sorted_list_without_comprehension():
    sorted(list_without_comprehension())


# operations on list_with_comprehension
def sort_list_with_comprehension():
    list_with_comprehension().sort()

def reverse_sort_list_with_comprehension():
    list_with_comprehension().sort(reverse=True)

def sorted_list_with_comprehension():
    sorted(list_with_comprehension())


def main():
    objs = globals()
    funcs = []
    f = open("timeit_demo.sh", "w+")

    for objname in objs:
        if objname != 'main' and type(objs[objname]) == types.FunctionType:
            funcs.append(objname)
    funcs.sort()
    for func in funcs:
        f.write('''echo "Timing: %(funcname)s"
python -m timeit "import timeit_demo; timeit_demo.%(funcname)s();"\n\n
echo "------------------------------------------------------------"
''' % dict(
                funcname = func,
                )
            )

    f.close()

if __name__ == "__main__":
    main()

    from os import system

    #Works only for *nix platforms
    system("/bin/bash timeit_demo.sh")

    #un-comment below for windows
    #system("cmd timeit_demo.sh")

2

Đây là một trình bao bọc đơn giản cho câu trả lời của steven. Hàm này không thực hiện chạy / lấy trung bình lặp lại, chỉ giúp bạn không phải lặp lại mã thời gian ở mọi nơi :)

'''function which prints the wall time it takes to execute the given command'''
def time_func(func, *args): #*args can take 0 or more 
  import time
  start_time = time.time()
  func(*args)
  end_time = time.time()
  print("it took this long to run: {}".format(end_time-start_time))

0

Bộ thử nghiệm không cố gắng sử dụng phần nhập khẩu, timeitvì vậy thật khó để biết ý định đó là gì. Tuy nhiên, đây là một câu trả lời kinh điển nên một ví dụ hoàn chỉnh về timeitdường như theo thứ tự, xây dựng trên câu trả lời của Martijn .

Các tài liệu đểtimeit cung cấp nhiều ví dụ và cờ đáng để kiểm tra. Cách sử dụng cơ bản trên dòng lệnh là:

$ python -mtimeit "all(True for _ in range(1000))"
2000 loops, best of 5: 161 usec per loop
$ python -mtimeit "all([True for _ in range(1000)])"
2000 loops, best of 5: 116 usec per loop

Chạy với -hđể xem tất cả các tùy chọn. Python MOTW có một phần tuyệt vời trên timeitđó cho thấy cách chạy các mô-đun thông qua các chuỗi mã nhập và đa dòng từ dòng lệnh.

Ở dạng kịch bản, tôi thường sử dụng nó như thế này:

import argparse
import copy
import dis
import inspect
import random
import sys
import timeit

def test_slice(L):
    L[:]

def test_copy(L):
    L.copy()

def test_deepcopy(L):
    copy.deepcopy(L)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--n", type=int, default=10 ** 5)
    parser.add_argument("--trials", type=int, default=100)
    parser.add_argument("--dis", action="store_true")
    args = parser.parse_args()
    n = args.n
    trials = args.trials
    namespace = dict(L = random.sample(range(n), k=n))
    funcs_to_test = [x for x in locals().values() 
                     if callable(x) and x.__module__ == __name__]
    print(f"{'-' * 30}\nn = {n}, {trials} trials\n{'-' * 30}\n")

    for func in funcs_to_test:
        fname = func.__name__
        fargs = ", ".join(inspect.signature(func).parameters)
        stmt = f"{fname}({fargs})"
        setup = f"from __main__ import {fname}"
        time = timeit.timeit(stmt, setup, number=trials, globals=namespace)
        print(inspect.getsource(globals().get(fname)))

        if args.dis:
            dis.dis(globals().get(fname))

        print(f"time (s) => {time}\n{'-' * 30}\n")

Bạn có thể dễ dàng thả các hàm và đối số bạn cần. Hãy thận trọng khi sử dụng các chức năng không tinh khiết và chăm sóc nhà nước.

Đầu ra mẫu:

$ python benchmark.py --n 10000
------------------------------
n = 10000, 100 trials
------------------------------

def test_slice(L):
    L[:]

time (s) => 0.015502399999999972
------------------------------

def test_copy(L):
    L.copy()

time (s) => 0.01651419999999998
------------------------------

def test_deepcopy(L):
    copy.deepcopy(L)

time (s) => 2.136012
------------------------------
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.