Câu trả lời:
Cách thức hoạt động của timeit là chạy mã thiết lập một lần và sau đó thực hiện các cuộc gọi lặp lại cho một loạt các câu lệnh. Vì vậy, nếu bạn muốn kiểm tra sắp xếp, cần có sự cẩn thận để một lần vượt qua tại một vị trí tại chỗ không ảnh hưởng đến lần tiếp theo với dữ liệu đã được sắp xếp (tất nhiên, điều đó sẽ khiến Timsort thực sự tỏa sáng vì nó hoạt động tốt nhất khi dữ liệu đã được đặt hàng một phần).
Dưới đây là một ví dụ về cách thiết lập thử nghiệm để sắp xếp:
>>> import timeit
>>> setup = '''
import random
random.seed('slartibartfast')
s = [random.random() for i in range(1000)]
timsort = list.sort
'''
>>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000))
0.334147930145
Lưu ý rằng một loạt các báo cáo tạo một bản sao mới của dữ liệu chưa được sắp xếp trên mỗi lần vượt qua.
Ngoài ra, lưu ý kỹ thuật thời gian chạy bộ đo lường bảy lần và chỉ giữ thời gian tốt nhất - điều này thực sự có thể giúp giảm các biến dạng đo do các quy trình khác chạy trên hệ thống của bạn.
Đó là những lời khuyên của tôi để sử dụng thời gian chính xác. Hi vọng điêu nay co ich :-)
.repeat(7,1000)
đã làm điều này (bằng cách sử dụng cùng một hạt giống)! Vì vậy, giải pháp của bạn là IMO hoàn hảo.
.repeat(7, 1000)
so với so .repeat(2, 3500)
với .repeat(35, 200
) sẽ phụ thuộc vào mức độ lỗi do tải hệ thống so với lỗi do biến thiên đầu vào. Trong trường hợp cực đoan nếu hệ thống của bạn luôn chịu tải nặng và bạn thấy một cái đuôi dài mỏng ở bên trái phân phối thời gian thực hiện (khi bạn bắt nó ở trạng thái không hoạt động hiếm), bạn thậm chí có thể thấy .repeat(7000,1)
hữu ích hơn .repeat(7,1000)
nếu bạn ngân sách không thể hơn 7000 lượt chạy.
Nếu bạn muốn sử dụng timeit
trong phiên Python tương tác, có hai tùy chọn thuận tiện:
Sử dụng vỏ IPython . Nó có tính năng %timeit
đặc biệt thuận tiện :
In [1]: def f(x):
...: return x*x
...:
In [2]: %timeit for x in range(100): f(x)
100000 loops, best of 3: 20.3 us per loop
Trong trình thông dịch Python chuẩn, bạn có thể truy cập các hàm và các tên khác mà bạn đã xác định trước đó trong phiên tương tác bằng cách nhập chúng từ __main__
trong câu lệnh thiết lập:
>>> def f(x):
... return x * x
...
>>> import timeit
>>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f",
number=100000)
[2.0640320777893066, 2.0876040458679199, 2.0520210266113281]
from __main__ import f
kỹ thuật. Tôi không nghĩ rằng điều này được biết đến rộng rãi như nó phải được. Nó rất hữu ích trong các trường hợp như thế này trong đó một lệnh gọi hàm hoặc phương thức đang được định thời gian. Trong các trường hợp khác (định thời gian cho một loạt các bước), nó ít hữu ích hơn vì nó giới thiệu chức năng gọi vốn.
%timeit f(x)
sys._getframe(N).f_globals
) phải là mặc định ngay từ đầu.
Tôi sẽ cho bạn biết một bí mật: cách tốt nhất để sử dụng timeit
là trên dòng lệnh.
Trên dòng lệnh, timeit
phân tích thống kê phù hợp: nó cho bạn biết thời gian chạy ngắn nhất. Điều này là tốt bởi vì tất cả các lỗi trong thời gian là tích cực. Vì vậy, thời gian ngắn nhất có ít lỗi nhất trong đó. Không có cách nào để có được lỗi tiêu cực vì một máy tính không thể tính toán nhanh hơn nó có thể tính toán!
Vì vậy, giao diện dòng lệnh:
%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
Điều đó khá đơn giản, phải không?
Bạn có thể thiết lập công cụ:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
cái nào cũng hữu ích
Nếu bạn muốn nhiều dòng, bạn có thể sử dụng tiếp tục tự động của shell hoặc sử dụng các đối số riêng biệt:
%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop
Điều đó cho phép thiết lập
x = range(1000)
y = range(100)
và thời gian
sum(x)
min(y)
Nếu bạn muốn có các tập lệnh dài hơn, bạn có thể muốn chuyển sang timeit
bên trong tập lệnh Python. Tôi đề nghị tránh điều đó bởi vì phân tích và thời gian chỉ đơn giản là tốt hơn trên dòng lệnh. Thay vào đó, tôi có xu hướng tạo các kịch bản shell:
SETUP="
... # lots of stuff
"
echo Minmod arr1
python -m timeit -s "$SETUP" "Minmod(arr1)"
echo pure_minmod arr1
python -m timeit -s "$SETUP" "pure_minmod(arr1)"
echo better_minmod arr1
python -m timeit -s "$SETUP" "better_minmod(arr1)"
... etc
Việc này có thể mất nhiều thời gian hơn do nhiều lần khởi tạo, nhưng thông thường đó không phải là vấn đề lớn.
Nhưng nếu bạn muốn sử dụng timeit
bên trong mô-đun của bạn thì sao?
Cách đơn giản là làm:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
và điều đó cho bạn thời gian tích lũy ( không phải tối thiểu!) để chạy số lần đó.
Để có được một phân tích tốt, sử dụng .repeat
và lấy tối thiểu:
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Bạn thường nên kết hợp điều này với functools.partial
thay vì lambda: ...
để chi phí thấp hơn. Vì vậy, bạn có thể có một cái gì đó như:
from functools import partial
def to_time(items):
...
test_items = [1, 2, 3] * 100
times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000)
# Divide by the number of repeats
time_taken = min(times) / 1000
Bạn cũng có thể làm:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
sẽ cung cấp cho bạn một cái gì đó gần hơn với giao diện từ dòng lệnh, nhưng theo cách ít thú vị hơn nhiều. Việc "from __main__ import ..."
cho phép bạn sử dụng mã từ mô-đun chính của mình bên trong môi trường nhân tạo được tạo bởi timeit
.
Điều đáng chú ý là đây là một trình bao bọc tiện lợi Timer(...).timeit(...)
và vì vậy không đặc biệt tốt về thời gian. Cá nhân tôi thích sử dụng Timer(...).repeat(...)
như tôi đã trình bày ở trên.
Có một vài hãy cẩn thận timeit
mà giữ ở khắp mọi nơi.
Chi phí không được tính. Nói rằng bạn muốn thời gian x += 1
, để tìm hiểu thêm bao lâu:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Chà, không phải là 0,0476. Bạn chỉ biết rằng nó ít hơn thế. Tất cả lỗi là tích cực.
Vì vậy, hãy thử và tìm chi phí thuần túy :
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
Đó là một chi phí tốt 30% chỉ từ thời gian! Điều này có thể ồ ạt thời gian tương đối. Nhưng bạn chỉ thực sự quan tâm đến việc thêm thời gian; thời gian tra cứu x
cũng cần được đưa vào chi phí:
>>> python -m timeit -s "x = 0" "x"
100000000 loops, best of 3: 0.0166 usec per loop
Sự khác biệt không lớn hơn nhiều, nhưng nó ở đó.
Phương pháp đột biến là nguy hiểm.
>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop
Nhưng điều đó hoàn toàn sai! x
là danh sách trống sau lần lặp đầu tiên. Bạn sẽ cần phải xác định lại:
>>> python -m timeit "x = [0]*100000" "while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop
Nhưng sau đó bạn có rất nhiều chi phí. Tài khoản cho riêng biệt.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Lưu ý rằng trừ chi phí là hợp lý ở đây chỉ vì chi phí là một phần nhỏ của thời gian.
Ví dụ của bạn, đáng chú ý là cả Sắp xếp chèn và Sắp xếp thời gian có hành vi thời gian hoàn toàn bất thường cho các danh sách đã được sắp xếp. Điều này có nghĩa là bạn sẽ yêu cầu random.shuffle
giữa các loại nếu bạn muốn tránh làm hỏng thời gian của mình.
timeit
từ một chương trình nhưng hoạt động giống như dòng lệnh? .
timeit
thực thi một pass
tuyên bố khi không có đối số nào được đưa ra, tất nhiên, sẽ mất một thời gian. Nếu bất kỳ đối số nào được đưa ra, pass
sẽ không được thực thi, do đó, trừ đi một số 0.014
usec từ mọi thời điểm sẽ không chính xác.
Đối với tôi, đây là cách nhanh nhất:
import timeit
def foo():
print("here is my code to time...")
timeit.timeit(stmt=foo, number=1234567)
# Генерация целых чисел
def gen_prime(x):
multiples = []
results = []
for i in range(2, x+1):
if i not in multiples:
results.append(i)
for j in range(i*i, x+1, i):
multiples.append(j)
return results
import timeit
# Засекаем время
start_time = timeit.default_timer()
gen_prime(3000)
print(timeit.default_timer() - start_time)
# start_time = timeit.default_timer()
# gen_prime(1001)
# print(timeit.default_timer() - start_time)
Điều này làm việc tuyệt vời:
python -m timeit -c "$(cat file_name.py)"
cho phép thiết lập cùng một từ điển trong mỗi điều sau đây và kiểm tra thời gian thực hiện.
Đối số thiết lập về cơ bản là thiết lập từ điển
Số là để chạy mã 1000000 lần. Không phải thiết lập mà là stmt
Khi bạn chạy nó, bạn có thể thấy chỉ số đó nhanh hơn get. Bạn có thể chạy nó nhiều lần để xem.
Mã về cơ bản cố gắng để có được giá trị của c trong từ điển.
import timeit
print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))
print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))
Đây là kết quả của tôi, của bạn sẽ khác nhau.
theo chỉ số: 0.20900007452246427
bằng cách nhận: 0,54841166886888
chỉ cần chuyển toàn bộ mã của bạn dưới dạng đối số của timeit:
import timeit
print(timeit.timeit(
"""
limit = 10000
prime_list = [i for i in range(2, limit+1)]
for prime in prime_list:
for elem in range(prime*2, max(prime_list)+1, prime):
if elem in prime_list:
prime_list.remove(elem)
"""
, number=10))
import timeit
def oct(x):
return x*x
timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()
gc.enable()
gì
Mô-đun timeit tích hợp hoạt động tốt nhất từ dòng lệnh IPython.
Để chức năng thời gian từ trong một mô-đun:
from timeit import default_timer as timer
import sys
def timefunc(func, *args, **kwargs):
"""Time a function.
args:
iterations=3
Usage example:
timeit(myfunc, 1, b=2)
"""
try:
iterations = kwargs.pop('iterations')
except KeyError:
iterations = 3
elapsed = sys.maxsize
for _ in range(iterations):
start = timer()
result = func(*args, **kwargs)
elapsed = min(timer() - start, elapsed)
print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed)))
return result
Ví dụ về cách sử dụng trình thông dịch REPL của Python với chức năng chấp nhận các tham số.
>>> 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
Bạn sẽ tạo hai chức năng và sau đó chạy một cái gì đó tương tự như thế này. Lưu ý, bạn muốn chọn cùng số lần thực hiện / chạy để so sánh apple với apple.
Điều này đã được thử nghiệm theo Python 3.7.
Đây là mã để dễ dàng sao chép nó
!/usr/local/bin/python3
import timeit
def fibonacci(n):
"""
Returns the n-th Fibonacci number.
"""
if(n == 0):
result = 0
elif(n == 1):
result = 1
else:
result = fibonacci(n-1) + fibonacci(n-2)
return result
if __name__ == '__main__':
import timeit
t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")
timsort(a)
và lấy sự khác biệt :-)