timeit contro temporizzatore decoratore
Domanda
Sto cercando di cronometrare un po 'di codice. Per prima cosa ho usato un decoratore di tempistiche:
#!/usr/bin/env python
import time
from itertools import izip
from random import shuffle
def timing_val(func):
def wrapper(*arg, **kw):
'''source: http://www.daniweb.com/code/snippet368.html'''
t1 = time.time()
res = func(*arg, **kw)
t2 = time.time()
return (t2 - t1), res, func.__name__
return wrapper
@timing_val
def time_izip(alist, n):
i = iter(alist)
return [x for x in izip(*[i] * n)]
@timing_val
def time_indexing(alist, n):
return [alist[i:i + n] for i in range(0, len(alist), n)]
func_list = [locals()[key] for key in locals().keys()
if callable(locals()[key]) and key.startswith('time')]
shuffle(func_list) # Shuffle, just in case the order matters
alist = range(1000000)
times = []
for f in func_list:
times.append(f(alist, 31))
times.sort(key=lambda x: x[0])
for (time, result, func_name) in times:
print '%s took %0.3fms.' % (func_name, time * 1000.)
rendimenti
% test.py
time_indexing took 73.230ms.
time_izip took 122.057ms.
E qui uso timeit:
% python - m timeit - s '' 'alist=range(1000000);[alist[i:i+31] for i in range(0, len(alist), 31)]'
10 loops, best of 3:
64 msec per loop
% python - m timeit - s 'from itertools import izip' 'alist=range(1000000);i=iter(alist);[x for x in izip(*[i]*31)]'
10 loops, best of 3:
66.5 msec per loop
Usando timeit i risultati sono praticamente gli stessi, ma usando il decoratore di temporizzazione appare time_indexing
è più veloce di time_izip
.
Che cosa spiega questa differenza?
Dovrebbe essere creduto uno dei due metodi?
In caso affermativo, quale?
Soluzione
Usa timeit. L'esecuzione del test più di una volta mi dà risultati molto migliori.
func_list=[locals()[key] for key in locals().keys()
if callable(locals()[key]) and key.startswith('time')]
alist=range(1000000)
times=[]
for f in func_list:
n = 10
times.append( min( t for t,_,_ in (f(alist,31) for i in range(n))))
for (time,func_name) in zip(times, func_list):
print '%s took %0.3fms.' % (func_name, time*1000.)
- >
<function wrapper at 0x01FCB5F0> took 39.000ms.
<function wrapper at 0x01FCB670> took 41.000ms.
Altri suggerimenti
Usa il wrapping da functools
per migliorare la risposta di Matt Alcock.
from functools import wraps
from time import time
def timing(f):
@wraps(f)
def wrap(*args, **kw):
ts = time()
result = f(*args, **kw)
te = time()
print 'func:%r args:[%r, %r] took: %2.4f sec' % \
(f.__name__, args, kw, te-ts)
return result
return wrap
In un esempio:
@timing
def f(a):
for _ in range(a):
i = 0
return -1
Metodo di invocazione f
racchiuso in @timing
:
func:'f' args:[(100000000,), {}] took: 14.2240 sec
f(100000000)
Il vantaggio è che conserva gli attributi della funzione originale; vale a dire, metadati come il nome della funzione e il docstring sono correttamente conservati sulla funzione restituita.
Vorrei usare un decoratore di temporizzazione, perché puoi usare le annotazioni per cospargere i tempi attorno al tuo codice piuttosto che renderti disordinato con la logica dei tempi.
import time
def timeit(f):
def timed(*args, **kw):
ts = time.time()
result = f(*args, **kw)
te = time.time()
print 'func:%r args:[%r, %r] took: %2.4f sec' % \
(f.__name__, args, kw, te-ts)
return result
return timed
Usare il decoratore è facile o usare le annotazioni.
@timeit
def compute_magic(n):
#function definition
#....
O ricollegare la funzione che si desidera cronometrare.
compute_magic = timeit(compute_magic)
Mi sono stancato di da __main__ import foo
, ora lo uso - per argomenti semplici, per i quali% r funziona,
e non in Ipython.
(Perché timeit
funziona solo su stringhe, non su thunk / chiusure, ad esempio timefunc (f, arbitrs args)?)
import timeit
def timef( funcname, *args, **kwargs ):
""" timeit a func with args, e.g.
for window in ( 3, 31, 63, 127, 255 ):
timef( "filter", window, 0 )
This doesn't work in ipython;
see Martelli, "ipython plays weird tricks with __main__" in Stackoverflow
"""
argstr = ", ".join([ "%r" % a for a in args]) if args else ""
kwargstr = ", ".join([ "%s=%r" % (k,v) for k,v in kwargs.items()]) \
if kwargs else ""
comma = ", " if (argstr and kwargstr) else ""
fargs = "%s(%s%s%s)" % (funcname, argstr, comma, kwargstr)
# print "test timef:", fargs
t = timeit.Timer( fargs, "from __main__ import %s" % funcname )
ntime = 3
print "%.0f usec %s" % (t.timeit( ntime ) * 1e6 / ntime, fargs)
#...............................................................................
if __name__ == "__main__":
def f( *args, **kwargs ):
pass
try:
from __main__ import f
except:
print "ipython plays weird tricks with __main__, timef won't work"
timef( "f")
timef( "f", 1 )
timef( "f", """ a b """ )
timef( "f", 1, 2 )
timef( "f", x=3 )
timef( "f", x=3 )
timef( "f", 1, 2, x=3, y=4 )
Aggiunto: vedi anche " ipython gioca strani trucchi con principale " ;, Martelli in running-doctests-through-ipython
Solo un'ipotesi, ma potrebbe la differenza essere nell'ordine di grandezza della differenza nei valori range ()?
Dalla tua fonte originale:
alist=range(1000000)
Dal tuo timeit
esempio:
alist=range(100000)
Per quello che vale, ecco i risultati sul mio sistema con l'intervallo impostato su 1 milione:
$ python -V
Python 2.6.4rc2
$ python -m timeit -s 'from itertools import izip' 'alist=range(1000000);i=iter(alist);[x for x in izip(*[i]*31)]'
10 loops, best of 3: 69.6 msec per loop
$ python -m timeit -s '' 'alist=range(1000000);[alist[i:i+31] for i in range(0, len(alist), 31)]'
10 loops, best of 3: 67.6 msec per loop
Non sono riuscito a far funzionare il tuo altro codice, poiché non sono riuscito a importare il "decoratore" modulo sul mio sistema.
Aggiorna - Vedo la stessa discrepanza che fai quando eseguo il codice senza il decoratore coinvolto.
$ ./test.py
time_indexing took 84.846ms.
time_izip took 132.574ms.
Grazie per aver pubblicato questa domanda; Ho imparato qualcosa oggi. =)
indipendentemente da questo particolare esercizio, immagino che l'uso di timeit
sia un'opzione molto più sicura e affidabile. è anche multipiattaforma, a differenza della tua soluzione.