Risposte:
Il modo in cui timeit funziona consiste nell'eseguire il codice di installazione una volta e quindi effettuare chiamate ripetute a una serie di istruzioni. Quindi, se si desidera testare l'ordinamento, è necessario prestare attenzione affinché un passaggio in un ordinamento sul posto non influisca sul passaggio successivo con dati già ordinati (che, ovviamente, renderebbero il Timsort davvero brillante perché funziona meglio quando i dati sono già parzialmente ordinati).
Ecco un esempio di come impostare un test per l'ordinamento:
>>> 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
Si noti che la serie di istruzioni crea una nuova copia dei dati non ordinati su ogni passaggio.
Inoltre, prendi nota della tecnica di temporizzazione dell'esecuzione della suite di misurazione sette volte e del mantenimento solo del momento migliore: ciò può davvero aiutare a ridurre le distorsioni di misurazione dovute ad altri processi in esecuzione sul tuo sistema.
Questi sono i miei consigli per usare correttamente timeit. Spero che sia di aiuto :-)
.repeat(7,1000)
già (usando lo stesso seme)! Quindi la tua soluzione è IMO perfetta.
.repeat(7, 1000)
vs .repeat(2, 3500)
vs .repeat(35, 200
) dovrebbe dipendere da come l'errore dovuto al carico del sistema si confronta con l'errore dovuto alla variabilità dell'input. Nel caso estremo se il tuo sistema è sempre sotto carico pesante e vedi una coda lunga e sottile a sinistra della distribuzione del tempo di esecuzione (quando la prendi in uno stato di inattività raro), potresti persino trovare .repeat(7000,1)
più utile che .repeat(7,1000)
se non è possibile programmare più di 7000 corse.
Se vuoi usarlo timeit
in una sessione interattiva di Python, ci sono due comode opzioni:
Usa la shell IPython . Presenta la comoda %timeit
funzione speciale:
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
In un interprete Python standard, è possibile accedere a funzioni e altri nomi definiti in precedenza durante la sessione interattiva importandoli dall'istruzione __main__
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]
from __main__ import f
tecnica. Non penso che questo sia così conosciuto come dovrebbe essere. È utile in casi come questo in cui viene programmata una chiamata di funzione o metodo. In altri casi (tempistica di una serie di passaggi), è meno utile perché introduce un overhead di chiamata di funzione.
%timeit f(x)
sys._getframe(N).f_globals
) avrebbero dovuto essere il default dall'inizio.
Ti farò conoscere un segreto: il modo migliore di usare timeit
è dalla riga di comando.
Sulla riga di comando, timeit
esegue un'analisi statistica corretta: ti dice quanto tempo ha richiesto la corsa più breve. Questo è positivo perché tutti gli errori nei tempi sono positivi. Quindi il minor tempo ha il minimo errore. Non c'è modo di ottenere errori negativi perché un computer non può mai calcolare più velocemente di quanto possa calcolare!
Quindi, l'interfaccia della riga di comando:
%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
È abbastanza semplice, eh?
Puoi impostare cose:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
che è utile anche!
Se si desidera più righe, è possibile utilizzare la continuazione automatica della shell o utilizzare argomenti separati:
%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop
Questo dà una configurazione di
x = range(1000)
y = range(100)
e tempi
sum(x)
min(y)
Se vuoi avere script più lunghi, potresti essere tentato di spostarti timeit
all'interno di uno script Python. Suggerisco di evitarlo perché l'analisi e i tempi sono semplicemente migliori sulla riga di comando. Invece, tendo a creare script di 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
Questo può richiedere un po 'più di tempo a causa delle molteplici inizializzazioni, ma normalmente non è un grosso problema.
Ma cosa succede se si desidera utilizzare timeit
all'interno del modulo?
Bene, il modo semplice è fare:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
e questo ti dà il tempo cumulativo ( non minimo!) per eseguire quel numero di volte.
Per ottenere una buona analisi, utilizzare .repeat
e prendere il minimo:
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Normalmente dovresti combinarlo con functools.partial
invece di lambda: ...
abbassare il sovraccarico. Quindi potresti avere qualcosa del tipo:
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
Puoi anche fare:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
che ti darebbe qualcosa di più vicino all'interfaccia dalla riga di comando, ma in un modo molto meno interessante. Il "from __main__ import ..."
consente di utilizzare codice dal modulo principale all'interno dell'ambiente artificiale creato da timeit
.
Vale la pena notare che questo è un involucro pratico per Timer(...).timeit(...)
e quindi non è particolarmente bravo nei tempi. Personalmente preferisco di gran lunga usare Timer(...).repeat(...)
come ho mostrato sopra.
Ci sono alcuni avvertimenti con timeit
quella presa ovunque.
Le spese generali non sono contabilizzate. Di 'che vuoi tempo x += 1
, per scoprire quanto tempo richiede l'aggiunta:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Bene, non è 0,0476 µs. Sai solo che è meno di quello. Tutto l'errore è positivo.
Quindi prova a trovare un overhead puro :
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
Questo è un buon overhead del 30% solo dal momento! Ciò può distorcere enormemente i tempi relativi. Ma ti importavi solo dei tempi di aggiunta ; anche i tempi di ricerca per x
devono essere inclusi nell'overhead:
>>> python -m timeit -s "x = 0" "x"
100000000 loops, best of 3: 0.0166 usec per loop
La differenza non è molto più grande, ma è lì.
I metodi mutanti sono pericolosi.
>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop
Ma è completamente sbagliato! x
è l'elenco vuoto dopo la prima iterazione. Dovrai reinizializzare:
>>> python -m timeit "x = [0]*100000" "while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop
Ma poi hai un sacco di spese generali. Conto per quello separatamente.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Si noti che sottraendo l'overhead è ragionevole qui solo perché l'overhead è una frazione del tempo minima.
Per il tuo esempio, vale la pena notare che sia Insertion Sort che Tim Sort hanno comportamenti di temporizzazione completamente insoliti per gli elenchi già ordinati. Questo significa che avrai bisogno di una via di random.shuffle
mezzo se vuoi evitare di rovinare i tuoi tempi.
timeit
da un programma ma funzionando allo stesso modo della riga di comando? .
timeit
esegue una pass
dichiarazione quando non vengono forniti argomenti, il che, ovviamente, richiede del tempo. Se vengono forniti argomenti, nonpass
verranno eseguiti, quindi sottraendo alcuni usec da ogni tempistica sarebbe errato. 0.014
per me, questo è il modo più veloce:
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)
Questo funziona alla grande:
python -m timeit -c "$(cat file_name.py)"
consente di impostare lo stesso dizionario in ciascuno dei seguenti e testare il tempo di esecuzione.
L'argomento setup è fondamentalmente l'impostazione del dizionario
Il numero deve eseguire il codice 1000000 volte. Non l'installazione ma lo stmt
Quando esegui questo, puoi vedere che l'indice è molto più veloce di ottenere. Puoi eseguirlo più volte per vedere.
Fondamentalmente il codice cerca di ottenere il valore di c nel dizionario.
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))
Ecco i miei risultati, i tuoi differiranno.
per indice: 0.20900007452246427
per ottenere: 0,54841166886888
passa semplicemente l'intero codice come argomento di 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()
?
Il modulo timeit incorporato funziona meglio dalla riga di comando di IPython.
Per temporizzare le funzioni all'interno di un modulo:
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
Esempio di come utilizzare l'interprete REPL di Python con la funzione che accetta i parametri.
>>> 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
Dovresti creare due funzioni e quindi eseguire qualcosa di simile a questo. Si noti che si desidera scegliere lo stesso numero di esecuzione / esecuzione per confrontare Apple con Apple.
Questo è stato testato sotto Python 3.7.
Ecco il codice per facilitarne la copia
!/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)
e prendi la differenza :-)