2009-10-26 16 views
44

Estoy tratando de sincronizar el código. En primer lugar he utilizado un decorador de tiempo:timeit versus timing decorator

#!/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.) 

produce

% test.py 
time_indexing took 73.230ms. 
time_izip took 122.057ms. 

Y aquí yo 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 los resultados son prácticamente lo mismo, pero con el tiempo decorador se aparece time_indexing es más rápido que time_izip.

¿Cuál es la razón de esta diferencia?

¿Debería creerse cualquiera de los métodos?

Si es así, ¿cuál?

+1

Por cierto, el atributo 'nombre_func' no está confiablemente disponible para los métodos, solo para las funciones, por lo que' __name__' puede ser más preferible en su lugar. Pruébalo con 'time.sleep'. –

+0

@ A-B-B: Gracias por la corrección. – unutbu

Respuesta

15

Use timeit. Ejecutar la prueba más de una vez me da resultados mucho mejores.

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. 
+0

Sí, esa parece ser la razón. ¡Gracias! – unutbu

+18

Como un FYI, Timeit también deshabilita la recolección de basura durante la prueba. Esto puede ser otro gotcha. –

2

Solo supongo, pero ¿podría ser la diferencia en el orden de magnitud de la diferencia en los valores de rango()?

Desde su fuente original:

alist=range(1000000) 

Desde su timeit ejemplo:

alist=range(100000) 

Por lo que vale la pena, aquí están los resultados en mi sistema con el rango establecido a 1 millón:

$ 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 

No pude obtener su otro código para ejecutar, ya que no pude importar el módulo "decorador" en mi sistema.


actualización - veo la misma discrepancia se hace cuando corro el código sin el decorador involucrados.

$ ./test.py 
time_indexing took 84.846ms. 
time_izip took 132.574ms. 

Gracias por publicar esta pregunta; Aprendí algo hoy. =)

+0

He eliminado el módulo decorador para que mi código sea más fácil de ejecutar. ¿Lo intentarías? ¿Ve una clara diferencia en velocidad cuando ejecuta el script? Además, cambié el rango de 10^5 -> 10^6, por lo que la comparación es más parecida. Gracias. – unutbu

+0

Actualizado, por lo que vale, pero parece que ahora tienes tu respuesta. No hay problema. – mpontillo

0

independientemente de este ejercicio en particular, me imagino que el uso de timeit es una opción mucho más segura y confiable. también es multiplataforma, a diferencia de su solución.

6

Me cansé de from __main__ import foo, ahora utilizar este - de argumentos simples, para la cual trabaja% R, y no en IPython.
(¿Por qué timeit funciona solo en cadenas, no en thunks/closures, es decir, timefunc (f, args arbitrarios)?)


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) 

Agregado: véase también "ipython juega trucos extraños con principal", Martelli en running-doctests-through-ipython

+0

¡Gracias! Esto ciertamente hace que sea más fácil dejar caer las funciones en timeit. Puedes omitir 'argstr',' kwargstr', 'comma' si usas' fargs = '% s (*% s, **% s)'% (funcname, args, kwargs) ', pero tal vez haga que fargs a poco más difícil de leer – unutbu

+1

Esto no funciona con grandes matrices NumPy. –

24

me gustaría utilizar un decorador de tiempo, porque se puede utilizar anotaciones para rociar el momento alrededor de su código en lugar de hacer código desordenado con lógica de tiempo.

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 

Usando el decorador es fácil usar anotaciones.

@timeit 
def compute_magic(n): 
    #function definition 
    #.... 

O alias la función que desea medir el tiempo.

compute_magic = timeit(compute_magic) 
+6

Creo que el uso de functools.wraps aquí sería una pequeña mejora – kuzzooroo

+2

¿Por curiosidad fue esta respuesta copiada desde aquí ?: https://www.andreas-jung.com/contents/a-python-decorator-for-measuring-the -execution-time-of-methods – emschorsch

30

Uso envoltorio de functools para mejorar la respuesta de 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 

En un ejemplo:

@timing 
def f(a): 
    for _ in range(a): 
     i = 0 
    return -1 

método Invocación f envuelto con @timing:

func:'f' args:[(100000000,), {}] took: 14.2240 sec 
f(100000000) 

La ventaja de esto es que conserva los atributos de la función original; es decir, los metadatos como el nombre de la función y docstring se conservan correctamente en la función devuelta.