Jak korzystać z modułu timeit

Rozumiem pojęcie tego, co robi timeit, ale nie jestem pewien, jak zaimplementować to w moim kodzie.

Jak mogę porównać dwie funkcje, powiedzmy insertion_sort i tim_sort, z timeit?

Author: BoshWash, 2011-11-22

13 answers

The way timeit prace polega na jednokrotnym uruchomieniu kodu instalacyjnego, a następnie wielokrotnym wywołaniu szeregu instrukcji. Tak więc, jeśli chcesz przetestować sortowanie, wymagana jest pewna ostrożność, aby jedno przejście w sortowaniu na miejscu nie miało wpływu na następne przejście z już posortowanymi danymi (to oczywiście sprawi, że Timsort będzie naprawdę świecić, ponieważ działa najlepiej, gdy dane są już częściowo uporządkowane).

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

Uwaga że seria oświadczeń tworzy świeżą kopię niesortowanych danych na każdym przejściu.

Zwróć również uwagę na technikę pomiaru czasu siedmiokrotnie i zachowując tylko najlepszy czas - może to naprawdę pomóc zmniejszyć zakłócenia pomiaru spowodowane innymi procesami działającymi w Twoim systemie.

Oto moje wskazówki dotyczące prawidłowego korzystania z timeit. Mam nadzieję, że to pomoże: -)

 214
Author: Raymond Hettinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-22 19:55:57

Jeśli chcesz użyć timeit w interaktywnej sesji Pythona, są dwie wygodne opcje:

  1. Użyj powłoki IPython. Posiada wygodną %timeit specjalną funkcję:

    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
    
  2. W standardowym interpreterze Pythona można uzyskać dostęp do funkcji i innych nazw zdefiniowanych wcześniej podczas sesji interaktywnej, importując je z __main__ w instrukcji setup:

    >>> 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]
    
 217
Author: Sven Marnach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-22 01:41:48

Powiem ci w tajemnicy: najlepszym sposobem użycia {[16] } jest linia komend.

W wierszu poleceń, timeit wykonuje właściwą analizę statystyczną: mówi, jak długo trwało najkrótsze uruchomienie. Jest to dobre, ponieważ wszystkie błąd w czasie jest pozytywny. Więc najkrótszy czas ma w sobie najmniejszy błąd. Nie ma sposobu, aby uzyskać negatywny błąd, ponieważ komputer nigdy nie może obliczyć szybciej niż może obliczyć!

Więc interfejs wiersza poleceń:

%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop

To całkiem proste, co?

Możesz ustawić 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 użyć automatycznej kontynuacji powłoki lub użyć oddzielnych 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 razy

sum(x)
min(y)

Jeśli chcesz mieć dłuższe Skrypty, możesz pokusić się o przeniesienie do timeit wewnątrz skryptu Pythona. Sugeruję unikanie tego, ponieważ analiza i czas są po prostu lepsze na wiersz poleceń. Zamiast tego mam tendencję do tworzenia skryptów 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 ze względu na wielokrotne inicjalizacje, ale zwykle nie jest to wielka sprawa.


Ale co jeślichcesz użyć timeit wewnątrz twojego modułu?

Cóż, prosty sposób jest do zrobienia:

def function(...):
    ...

timeit.Timer(function).timeit(number=NUMBER)

I to daje zbiorcze (a nie minimum!) czas uruchomić tę liczbę razy.

Aby uzyskać dobrą analizę, użyj .repeat i weź minimum:

min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))

Powinieneś normalnie połączyć to z functools.partial zamiast lambda: ..., aby obniżyć koszty. Można więc mieć coś w stylu:

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 również zrobić:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)

Co da ci coś bliższego interfejsowi z linii poleceń, ale w znacznie mniej fajny sposób. "from __main__ import ..." pozwala na użycie kodu z głównego modułu wewnątrz sztucznego środowiska stworzonego przez timeit.

Warto zauważyć, że jest to wygoda wrapper dla Timer(...).timeit(...) i tak nie jest szczególnie dobry w czasie. Osobiście zdecydowanie wolę używać Timer(...).repeat(...), jak pokazałem powyżej.


Ostrzeżenia

Jest kilka zastrzeżeń z timeit które trzymają się wszędzie.

  • / Align = "left" / Powiedz, że chcesz 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 jest nie 0.0476 µs. Wiesz tylko, że jest mniej niż to. Wszystkie błędy są pozytywne.

    Więc spróbuj i znaleźć czysty nad głową:

    >>> python -m timeit -s "x = 0" ""      
    100000000 loops, best of 3: 0.014 usec per loop
    

    That 's a good 30% / align = "left" / Może to znacznie przekrzywiać względne czasy. Ale naprawdę zależało Ci tylko na dodaniutimingów; czasy wyszukiwania dla x również muszą być uwzględnione w napowietrznych:

    >>> 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 mutacji 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 jest całkowicie błędne! x jest pusta lista 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
    
    / Align = "left" / Rozlicz to osobno.
    >>> python -m timeit "x = [0]*100000"                   
    1000 loops, best of 3: 261 usec per loop
    

    Zauważ, że odejmowanie narzutu jest rozsądne tutaj tylko dlatego, że narzut jest małym ułamkiem czasu.

    Dla Twojego przykładu warto zauważyć, że zarówno sortowanie wstawiania, jak i sortowanie Tim mają zupełnie nietypowe zachowanie czasu dla już posortowanych list. To oznacza, że ty wymaga random.shuffle pomiędzy rodzajami, jeśli chcesz uniknąć niszczenia czasu.

 106
Author: Veedrac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-18 22:53:32

Jeśli chcesz szybko porównać dwa bloki kodu / funkcji, możesz to zrobić:

import timeit

start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func2()
print(timeit.default_timer() - start_time)
 71
Author: zzart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-08 10:33:05

Najłatwiej jest użyć timeit z linii poleceń:

Given test.py :

def InsertionSort(): ...
def TimSort(): ...

Uruchom czas TAK:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'
 41
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-16 13:51:51
# Генерация целых чисел

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)
 9
Author: David Webb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-01 22:31:07

Dla mnie to najszybsza droga:

import timeit
def foo():
    print("here is my code to time...")


timeit.timeit(stmt=foo, number=1234567)
 8
Author: Rodrigo Laguna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 13:49:51

To działa świetnie:

  python -m timeit -c "$(cat file_name.py)"
 6
Author: Ohad Rubin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-21 00:33:56

Pozwala ustawić ten sam słownik w każdym z poniższych i przetestować czas wykonania.

Argument setup jest w zasadzie konfiguracją słownika

Liczba ma uruchomić kod 1000000 razy. Nie konfiguracja, ale stmt

Kiedy to uruchomisz, zobaczysz, że indeks jest o wiele szybszy niż get. Możesz uruchomić go wiele razy, aby zobaczyć.

Kod zasadniczo 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ą / align = "left" /

By index: 0.20900007452246427

By get: 0.54841166886888

 3
Author: Stryker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 15:31:21

Po prostu podaj 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))
 1
Author: tryptofan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-04 19:30:13

Wbudowany Moduł timeit działa najlepiej z linii poleceń IPython.

Do funkcji czasu z 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
 0
Author: ChaimG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-05 23:40:57

Przykład użycia interpretera Pythona REPL 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                                                                                        
 0
Author: Vlad Bezden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-19 16:56:15

Można utworzyć dwie funkcje, a następnie uruchomić coś podobnego do tego. Zauważ, że chcesz wybrać tę samą liczbę wykonania/uruchomienia, aby porównać apple do apple.
Zostało to przetestowane pod Pythonem 3.7.

Tutaj wpisz opis obrazka Oto kod ułatwiający jego 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")
 0
Author: CPU 100,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-12 23:17:13