Odpowiedzi:
Sposób działania timeit polega na jednokrotnym uruchomieniu kodu instalacyjnego, a następnie wielokrotnym wywoływaniu szeregu instrukcji. Tak więc, jeśli chcesz przetestować sortowanie, należy zachować ostrożność, aby jedno przejście w sortowaniu lokalnym nie wpłynęło na następne przejście z już posortowanymi danymi (co oczywiście sprawiłoby, że Timsort naprawdę zabłysnął, ponieważ działa najlepiej gdy dane są już częściowo zamówione).
Oto przykład, jak skonfigurować test do sortowania:
>>> 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
Zauważ, że seria instrukcji tworzy nową kopię nieposortowanych danych przy każdym przejściu.
Zwróć także uwagę na technikę pomiaru czasu, która polega na uruchomieniu pakietu pomiarowego siedem razy i zachowaniu najlepszego czasu - może to naprawdę pomóc w zmniejszeniu zniekształceń pomiaru spowodowanych innymi procesami działającymi w systemie.
To są moje wskazówki, jak prawidłowo używać timeit. Mam nadzieję że to pomoże :-)
.repeat(7,1000)
już to robisz (używając tego samego ziarna)! Więc twoje rozwiązanie jest idealne IMO.
.repeat(7, 1000)
vs. ) powinien zależeć od tego, jak błąd spowodowany obciążeniem systemu porównuje się z błędem wynikającym ze zmienności danych wejściowych. W skrajnym przypadku, jeśli twój system jest zawsze obciążony i widzisz długi cienki ogon po lewej stronie rozkładu czasu wykonania (gdy złapiesz go w rzadkim stanie bezczynności), możesz nawet okazać się bardziej przydatny niż gdybyś nie może przeznaczyć więcej niż 7000 uruchomień .repeat(2, 3500)
.repeat(35, 200
.repeat(7000,1)
.repeat(7,1000)
Jeśli chcesz korzystać timeit
z interaktywnej sesji Pythona, istnieją dwie wygodne opcje:
Użyj powłoki IPython . Posiada wygodną %timeit
funkcję specjalną:
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
W standardowym interpretatorze Pythona można uzyskać dostęp do funkcji i innych nazw zdefiniowanych wcześniej podczas sesji interaktywnej, importując je z __main__
instrukcji konfiguracji:
>>> 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
techniki. Nie sądzę, że jest to tak powszechnie znane, jak powinno być. Przydaje się w takich przypadkach, w których odmierzana jest funkcja lub wywołanie metody. W innych przypadkach (odmierzanie serii kroków) jest to mniej pomocne, ponieważ wprowadza narzut wywołania funkcji.
%timeit f(x)
sys._getframe(N).f_globals
) powinny być od początku domyślne.
Powiem ci sekret: najlepszy sposób użycia timeit
to w wierszu poleceń.
W wierszu poleceń timeit
wykonuje odpowiednią analizę statystyczną: pokazuje, jak długo trwał najkrótszy bieg. Jest to dobre, ponieważ każdy błąd w pomiarze czasu jest dodatni. Zatem najkrótszy czas zawiera najmniej błędów. Nie ma sposobu, aby uzyskać błąd ujemny, ponieważ komputer nigdy nie może wykonać obliczeń szybciej niż potrafi!
Interfejs wiersza poleceń:
%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
To całkiem proste, prawda?
Możesz skonfigurować różne rzeczy:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
co też jest przydatne!
Jeśli chcesz mieć wiele linii, możesz albo użyć automatycznej kontynuacji powłoki, albo użyć osobnych argumentów:
%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop
To daje konfigurację
x = range(1000)
y = range(100)
i czasy
sum(x)
min(y)
Jeśli chcesz mieć dłuższe skrypty, możesz ulec pokusie przejścia do timeit
wnętrza skryptu Python. Sugeruję unikanie tego, ponieważ analiza i synchronizacja są po prostu lepsze w wierszu poleceń. Zamiast tego robię skrypty powłoki:
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
Może to potrwać nieco dłużej z powodu wielu inicjalizacji, ale zwykle nie jest to wielka sprawa.
Ale co jeśli chcesz używać timeit
w swoim module?
Cóż, prosty sposób to zrobić:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
a to daje łączny ( nie minimalny!) czas na uruchomienie tej liczby razy.
Aby uzyskać dobrą analizę, użyj .repeat
i weź minimum:
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Zwykle powinieneś połączyć to z functools.partial
zamiast lambda: ...
obniżać koszty ogólne. W ten sposób możesz mieć coś takiego:
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
Możesz także:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
co dałoby ci coś bliższego interfejsu z wiersza poleceń, ale w znacznie mniej fajny sposób. "from __main__ import ..."
Pozwala używać kodu z głównego modułu wewnątrz środowiska stworzonego przez sztuczną timeit
.
Warto zauważyć, że jest to wygodne opakowanie, Timer(...).timeit(...)
więc nie jest szczególnie dobre w czasie. Osobiście zdecydowanie wolę używać, Timer(...).repeat(...)
jak pokazano powyżej.
timeit
Wszędzie istnieje kilka zastrzeżeń z tym związanych.
Koszty ogólne nie są rozliczane. Powiedz, że chcesz mieć czas x += 1
, aby dowiedzieć się, jak długo trwa dodawanie:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Cóż, to nie jest 0,0476 µs. Wiesz tylko, że to mniej niż to. Wszystkie błędy są pozytywne.
Więc spróbuj znaleźć czysty narzut:
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
To dobre 30% narzutu od samego momentu! Może to znacznie wypaczyć względne czasy. Ale tak naprawdę zależało ci tylko na dodawaniu czasu; czasy przeglądania x
również muszą być uwzględnione w kosztach ogólnych:
>>> python -m timeit -s "x = 0" "x"
100000000 loops, best of 3: 0.0166 usec per loop
Różnica nie jest dużo większa, ale jest.
Metody mutowania są niebezpieczne.
>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop
Ale to całkowicie źle! x
jest pustą listą po pierwszej iteracji. Musisz ponownie zainicjować:
>>> python -m timeit "x = [0]*100000" "while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop
Ale wtedy masz dużo kosztów ogólnych. Uwzględnij to osobno.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Zauważ, że odejmowanie narzutu jest tutaj uzasadnione tylko dlatego, że narzut stanowi ułamek czasu.
Na przykład warto zauważyć, że zarówno Sortowanie wstawiane, jak i Sortowanie czasowe mają zupełnie nietypowe zachowania czasowe dla już posortowanych list. Oznacza to, że będziesz potrzebować pośredniego random.shuffle
sortowania, jeśli chcesz uniknąć rujnowania swoich czasów.
timeit
z programu, ale działając tak samo jak w wierszu poleceń? .
timeit
wykonuje pass
polecenie, gdy nie podano żadnych argumentów, co oczywiście zajmuje trochę czasu. Jeśli podane pass
zostaną jakiekolwiek argumenty, nie zostaną wykonane, więc odjęcie niektórych 0.014
usecs od każdego czasu byłoby niepoprawne.
Uważam, że najłatwiejszym sposobem użycia timeit jest z wiersza poleceń:
Biorąc pod uwagę test.py :
def InsertionSort(): ...
def TimSort(): ...
uruchom czas w następujący sposób:
% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'
dla mnie to najszybszy sposób:
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)
Działa to świetnie:
python -m timeit -c "$(cat file_name.py)"
pozwala skonfigurować ten sam słownik w każdym z poniższych i przetestować czas wykonania.
Argumentem konfiguracji jest w zasadzie konfiguracja słownika
Liczba ma uruchomić kod 1000000 razy. Nie konfiguracja, ale stmt
Po uruchomieniu możesz zobaczyć, że indeks jest znacznie szybszy niż get. Możesz go uruchomić wiele razy, aby zobaczyć.
Kod w zasadzie próbuje uzyskać wartość c w słowniku.
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))
Oto moje wyniki, twoje będą się różnić.
według indeksu: 0.20900007452246427
przez get: 0,54841166886888
po prostu przekaż cały kod jako argument 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()
?
Wbudowany moduł timeit działa najlepiej z wiersza polecenia IPython.
Do funkcji czasowych z poziomu modułu:
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
Przykład użycia interpretera REPL w języku Python z funkcją akceptującą parametry.
>>> 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
Utworzyłbyś dwie funkcje, a następnie uruchomiłeś coś podobnego do tego. Zauważ, że chcesz wybrać tę samą liczbę wykonania / uruchomienia, aby porównać jabłko z jabłkiem.
Zostało to przetestowane pod Pythonem 3.7.
Oto kod ułatwiający kopiowanie
!/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)
i weź różnicę :-)