Función de medida de tiempo de Python

121

Quiero crear una función de Python para probar el tiempo empleado en cada función e imprimir su nombre con su tiempo, cómo puedo imprimir el nombre de la función y si hay otra forma de hacerlo, por favor dígame

def measureTime(a):
    start = time.clock() 
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print "Time spent in (function name) is: ", elapsed
Wazery
fuente
Las herramientas de creación de perfiles de Python pueden mostrarle los nombres de las funciones y el tiempo dedicado a cada una. Lea aquí: docs.python.org/library/profile.html
Roadmaster el
Mejor uso timeitpara la medición. No es perfecto, pero supera con creces su puñalada y es mucho más fácil de usar timeitque preparar algo mejor usted mismo.

Respuestas:

240

En primer lugar, le recomiendo usar un perfilador o al menos usar el tiempo .

Sin embargo, si desea escribir su propio método de tiempo estrictamente para aprender, aquí hay un lugar para comenzar a usar un decorador.

Python 2:

def timing(f):
    def wrap(*args):
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0)
        return ret
    return wrap

Y el uso es muy simple, solo use el decorador @timing:

@timing
def do_work():
  #code

Python 3:

def timing(f):
    def wrap(*args, **kwargs):
        time1 = time.time()
        ret = f(*args, **kwargs)
        time2 = time.time()
        print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))

        return ret
    return wrap

Tenga en cuenta que estoy llamando f.func_namepara obtener el nombre de la función como una cadena (en Python 2) o f.__name__ en Python 3.

Mike Lewis
fuente
44
exactamente lo que quiero :) ... pero ustedes me convencieron para usar el generador de perfiles de Python
Wazery
3
Parece que esto supone que time.time () informa el tiempo en microsegundos desde la época. La documentación dice que informa el tiempo en segundos docs.python.org/2/library/time.html#time.time .
Rahul Jha
Esto no puede tener efecto, después de usar el rendimiento en func. ¿Cómo puedo seguir usando este método y puedo usar el rendimiento?
jiamo
tiempo de def (f): def wrap (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () print '% s function tomó% 0.3 f ms '% (f.func_name, (time2-time1) * 1000) return ret return wrap
Vivek Bagaria
1
¿Cuál es la desventaja de escribirlo usted mismo? ¿No es lo suficientemente simple almacenar una lista de tiempos transcurridos y examinar su distribución?
3pitt
51

Después de jugar con el timeit módulo, no me gusta su interfaz, que no es tan elegante en comparación con los dos métodos siguientes.

El siguiente código está en Python 3.

El método decorador

Esto es casi lo mismo con el método de @ Mike. Aquí agrego kwargsy functoolsenvuelvo para hacerlo mejor.

def timeit(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
    return newfunc

@timeit
def foobar():
    mike = Person()
    mike.think(30)

El método del administrador de contexto

from contextlib import contextmanager

@contextmanager
def timeit_context(name):
    startTime = time.time()
    yield
    elapsedTime = time.time() - startTime
    print('[{}] finished in {} ms'.format(name, int(elapsedTime * 1000)))

Por ejemplo, puedes usarlo como:

with timeit_context('My profiling code'):
    mike = Person()
    mike.think()

Y el código dentro del with bloque será cronometrado.

Conclusión

Usando el primer método, puede comentar fácilmente el decorador para obtener el código normal. Sin embargo, solo puede cronometrar una función. Si tiene alguna parte del código que no sabe qué hacer para que sea una función, puede elegir el segundo método.

Por ejemplo, ahora tienes

images = get_images()
bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

Ahora quieres cronometrar la bigImage = ...línea. Si lo cambia a una función, será:

images = get_images()
bitImage = None
@timeit
def foobar():
    nonlocal bigImage
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

No se ve tan bien ... ¿Qué pasa si estás en Python 2, que no tiene nonlocalpalabra clave?

En cambio, usar el segundo método encaja muy bien aquí:

images = get_images()
with timeit_context('foobar'):
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)
Rayo
fuente
Contribución interesante, sin embargo, me parece inútil que en el método de decorador que mencionaste, tuvieras que cambiar la timeitinterfaz y usar la wraps()función del functoolsmódulo. Me refiero a que todo ese código extra no es necesario.
Billal Begueradj
1
Necesidadesimport functools
Guillaume Chevalier
1
Tenga en cuenta que su decorador pierde el valor de retorno de la función original
Marc Van Daele
11

No veo cuál es el problema con el timeitmódulo. Esta es probablemente la forma más sencilla de hacerlo.

import timeit
timeit.timeit(a, number=1)

También es posible enviar argumentos a las funciones. Todo lo que necesitas es terminar tu función usando decoradores. Más explicación aquí: http://www.pythoncentral.io/time-a-python-function/

El único caso en el que podría estar interesado en escribir sus propias declaraciones de temporización es si desea ejecutar una función solo una vez y también desea obtener su valor de retorno.

La ventaja de usar el timeitmódulo es que le permite repetir el número de ejecuciones. Esto puede ser necesario porque otros procesos pueden interferir con la precisión de su tiempo. Por lo tanto, debe ejecutarlo varias veces y mirar el valor más bajo.

Phani
fuente
3
¿Enviar argumentos a la función usando envoltorios y decoradores? ¿Por qué no timeit.timeit(lambda: func(a,b,c), number=1)? Lo uso cuando hago pruebas en una solución hipotética en una terminal.
Jack
10

Timeit tiene dos grandes fallas: no devuelve el valor de retorno de la función y usa eval, que requiere pasar un código de configuración adicional para las importaciones. Esto resuelve ambos problemas de manera simple y elegante:

def timed(f):
  start = time.time()
  ret = f()
  elapsed = time.time() - start
  return ret, elapsed

timed(lambda: database.foo.execute('select count(*) from source.apachelog'))
(<sqlalchemy.engine.result.ResultProxy object at 0x7fd6c20fc690>, 4.07547402381897)
Jonathan Ray
fuente
¡Gracias! timeit no funciona bien con Apache Spark porque tienes que importar todas las dependencias de Spark, y ¿quién quiere hacer una cadena grande y vieja que haga eso? Esta solución es mucho más simple y más flexible.
Paul
4

Hay una herramienta fácil para el tiempo. https://github.com/RalphMao/PyTimer

Puede funcionar como un decorador :

from pytimer import Timer
@Timer(average=False)      
def matmul(a,b, times=100):
    for i in range(times):
        np.dot(a,b)        

Salida:

matmul:0.368434
matmul:2.839355

También puede funcionar como un temporizador de complemento con control de espacio de nombres (útil si lo está insertando en una función que tiene muchos códigos y se puede llamar a cualquier otro lugar).

timer = Timer()                                           
def any_function():                                       
    timer.start()                                         

    for i in range(10):                                   

        timer.reset()                                     
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block1')                        

        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block2')                        
        np.dot(np.ones((100,1000)), np.zeros((1000,1000)))

    for j in range(20):                                   
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
    timer.summary()                                       

for i in range(2):                                        
    any_function()                                        

Salida:

========Timing Summary of Default Timer========
block2:0.065062
block1:0.032529
========Timing Summary of Default Timer========
block2:0.065838
block1:0.032891

Espero que ayude

ralphmao95
fuente
3

Método de decorador utilizando la biblioteca decoradora de Python:

import decorator

@decorator
def timing(func, *args, **kwargs):
    '''Function timing wrapper
        Example of using:
        ``@timing()``
    '''

    fn = '%s.%s' % (func.__module__, func.__name__)

    timer = Timer()
    with timer:
        ret = func(*args, **kwargs)

    log.info(u'%s - %0.3f sec' % (fn, timer.duration_in_seconds()))
    return ret

Ver publicación en mi blog:

publicar en el blog mobilepro.pl

mi publicación en Google Plus

MobilePro.pl
fuente
1

Mi forma de hacerlo:

from time import time

def printTime(start):
    end = time()
    duration = end - start
    if duration < 60:
        return "used: " + str(round(duration, 2)) + "s."
    else:
        mins = int(duration / 60)
        secs = round(duration % 60, 2)
        if mins < 60:
            return "used: " + str(mins) + "m " + str(secs) + "s."
        else:
            hours = int(duration / 3600)
            mins = mins % 60
            return "used: " + str(hours) + "h " + str(mins) + "m " + str(secs) + "s."

Establezca una variable como start = time()antes ejecute la función / bucles, y printTime(start)justo después del bloque.

y tienes la respuesta

Adam Liu
fuente