example - python timer
tic, funciones toc analógicas en Python (10)
Acabo de crear un módulo [tictoc.py] para lograr tocs tic anidados, que es lo que hace Matlab.
from time import time
tics = []
def tic():
tics.append(time())
def toc():
if len(tics)==0:
return None
else:
return time()-tics.pop()
Y funciona de esta manera:
from tictoc import tic, toc
# This keeps track of the whole process
tic()
# Timing a small portion of code (maybe a loop)
tic()
# -- Nested code here --
# End
toc() # This returns the elapse time (in seconds) since the last invocation of tic()
toc() # This does the same for the first tic()
Espero que ayude.
¿Cuál es el mejor análogo de las funciones de MATLAB tic y toc ( http://www.mathworks.com/help/techdoc/ref/tic.html ) en Python?
Actualizando la respuesta de Eli a Python 3:
class Timer(object):
def __init__(self, name=None):
self.name = name
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
message = ''Elapsed: %s'' % (time.time() - self.tstart)
if self.name:
message = ''[%s] '' % self.name + message
print(message)
Además del timeit
que ThiefMaster mencionó, una forma simple de hacerlo es justo (después de importar el time
):
t = time.time()
# do stuff
elapsed = time.time() - t
Tengo una clase de ayuda que me gusta usar:
class Timer(object):
def __init__(self, name=None):
self.name = name
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
if self.name:
print ''[%s]'' % self.name,
print ''Elapsed: %s'' % (time.time() - self.tstart)
Se puede usar como administrador de contexto:
with Timer(''foo_stuff''):
# do some foo
# do some stuff
A veces encuentro que esta técnica es más conveniente que timeit
, todo depende de lo que quieras medir.
Cambié un poco la respuesta de @Eli Bendersky para usar el ctor __init__()
y dtor __del__()
para hacer el cronometraje, para que se pueda usar más cómodamente sin sangrar el código original:
class Timer(object):
def __init__(self, name=None):
self.name = name
self.tstart = time.time()
def __del__(self):
if self.name:
print ''%s elapsed: %.2fs'' % (self.name, time.time() - self.tstart)
else:
print ''Elapsed: %.2fs'' % (time.time() - self.tstart)
Para usar, simplemente ponga Timer ("blahblah") al comienzo de algún ámbito local. El tiempo transcurrido se imprimirá al final del alcance:
for i in xrange(5):
timer = Timer("eigh()")
x = numpy.random.random((4000,4000));
x = (x+x.T)/2
numpy.linalg.eigh(x)
print i+1
timer = None
Imprime:
1
eigh() elapsed: 10.13s
2
eigh() elapsed: 9.74s
3
eigh() elapsed: 10.70s
4
eigh() elapsed: 10.25s
5
eigh() elapsed: 11.28s
Eche un vistazo al módulo timeit
. No es realmente equivalente, pero si el código que desea medir está dentro de una función, puede usarlo fácilmente.
El mejor análogo absoluto de tic y toc sería simplemente definirlos en python.
def tic():
#Homemade version of matlab tic and toc functions
import time
global startTime_for_tictoc
startTime_for_tictoc = time.time()
def toc():
import time
if ''startTime_for_tictoc'' in globals():
print "Elapsed time is " + str(time.time() - startTime_for_tictoc) + " seconds."
else:
print "Toc: start time not set"
Entonces puedes usarlos como:
tic()
# do stuff
toc()
Esto también se puede hacer usando un contenedor. Manera muy general de mantener el tiempo.
El contenedor de este código de ejemplo ajusta cualquier función e imprime la cantidad de tiempo necesaria para ejecutar la función:
def timethis(f):
import time
def wrapped(*args, **kwargs):
start = time.time()
r = f(*args, **kwargs)
print "Executing {0} took {1} seconds".format(f.func_name, time.time()-start)
return r
return wrapped
@timethis
def thistakestime():
for x in range(10000000):
pass
thistakestime()
Generalmente, el %time
de IPython, %timeit
, %prun
y %lprun
(si uno tiene instalado line_profiler
) satisfacen mis necesidades de perfil bastante bien. Sin embargo, surgió un caso de uso para la funcionalidad tipo tic-toc
cuando traté de perfilar cálculos que fueron conducidos de forma interactiva, es decir, por el movimiento del mouse del usuario en una GUI. Tenía ganas de enviar y toc
tic
deseados en las fuentes, mientras que la prueba interactiva sería la manera más rápida de revelar los cuellos de botella. Fui con la clase Timer
Eli Bendersky, pero no estaba del todo contento, ya que requería que cambiara la sangría de mi código, lo que puede ser un inconveniente para algunos editores y confunde el sistema de control de versiones. Además, puede existir la necesidad de medir el tiempo entre puntos en diferentes funciones, lo que no funcionaría con la instrucción with
. Después de probar mucho de la inteligencia de Python, aquí está la solución simple que encontré que funcionó mejor:
from time import time
_tstart_stack = []
def tic():
_tstart_stack.append(time())
def toc(fmt="Elapsed: %s s"):
print fmt % (time() - _tstart_stack.pop())
Dado que esto funciona presionando los tiempos de inicio en una pila, funcionará correctamente para múltiples niveles de tic
y toc
. También permite cambiar la cadena de formato de la instrucción toc
para mostrar información adicional, lo que me gustó de la clase Timer
de Eli.
Por alguna razón, me preocupé por la sobrecarga de una implementación pura de Python, así que probé un módulo de extensión C también:
#include <Python.h>
#include <mach/mach_time.h>
#define MAXDEPTH 100
uint64_t start[MAXDEPTH];
int lvl=0;
static PyObject* tic(PyObject *self, PyObject *args) {
start[lvl++] = mach_absolute_time();
Py_RETURN_NONE;
}
static PyObject* toc(PyObject *self, PyObject *args) {
return PyFloat_FromDouble(
(double)(mach_absolute_time() - start[--lvl]) / 1000000000L);
}
static PyObject* res(PyObject *self, PyObject *args) {
return tic(NULL, NULL), toc(NULL, NULL);
}
static PyMethodDef methods[] = {
{"tic", tic, METH_NOARGS, "Start timer"},
{"toc", toc, METH_NOARGS, "Stop timer"},
{"res", res, METH_NOARGS, "Test timer resolution"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
inittictoc(void) {
Py_InitModule("tictoc", methods);
}
Esto es para MacOSX, y he omitido el código para verificar si lvl
está fuera de límites por brevedad. Si bien tictoc.res()
arroja una resolución de aproximadamente 50 nanosegundos en mi sistema, descubrí que el jitter de medir cualquier enunciado de Python es fácilmente del orden de los microsegundos (y mucho más cuando se usa desde IPython). En este punto, la sobrecarga de la implementación de Python se vuelve insignificante, por lo que se puede usar con la misma confianza que la implementación de C.
Descubrí que la utilidad del enfoque de tic-toc
está prácticamente limitada a los bloques de código que tardan más de 10 microsegundos en ejecutarse. Debajo de eso, se requieren estrategias de promedio como en timeit
para obtener una medición fiel.
Sobre la base de las respuestas de Stefan y Antonimmo, terminé poniendo
def Tictoc():
start_stack = []
start_named = {}
def tic(name=None):
if name is None:
start_stack.append(time())
else:
start_named[name] = time()
def toc(name=None):
if name is None:
start = start_stack.pop()
else:
start = start_named.pop(name)
elapsed = time() - start
return elapsed
return tic, toc
en un módulo utils.py
, y lo uso con un
from utils import Tictoc
tic, toc = Tictoc()
De esta manera
- simplemente puede usar
tic()
,toc()
y anidarlos como en Matlab - alternativamente, puede nombrarlos:
tic(1)
,toc(1)
otic(''very-important-block'')
,toc(''very-important-block'')
y los temporizadores con nombres diferentes no interferirán - importarlos de esta manera evita la interferencia entre los módulos que lo usan.
(Aquí toc no imprime el tiempo transcurrido, sino que lo devuelve).
Tenía la misma pregunta cuando migré a Python desde Matlab. Con la ayuda de este hilo pude construir un análogo exacto de las funciones Matlab tic()
y toc()
. Simplemente inserte el siguiente código en la parte superior de su script.
import time
def TicTocGenerator():
# Generator that returns time differences
ti = 0 # initial time
tf = time.time() # final time
while True:
ti = tf
tf = time.time()
yield tf-ti # returns the time difference
TicToc = TicTocGenerator() # create an instance of the TicTocGen generator
# This will be the main function through which we define both tic() and toc()
def toc(tempBool=True):
# Prints the time difference yielded by generator instance TicToc
tempTimeInterval = next(TicToc)
if tempBool:
print( "Elapsed time: %f seconds./n" %tempTimeInterval )
def tic():
# Records a time in TicToc, marks the beginning of a time interval
toc(False)
¡Eso es! Ahora estamos listos para usar plenamente tic()
y toc()
al igual que en Matlab. Por ejemplo
tic()
time.sleep(5)
toc() # returns "Elapsed time: 5.00 seconds."
En realidad, esto es más versátil que las funciones integradas de Matlab. Aquí, podría crear otra instancia del TicTocGenerator
para realizar un seguimiento de las operaciones múltiples, o simplemente para TicTocGenerator
cosas de manera diferente. Por ejemplo, al cronometrar un guión, ahora podemos cronometrar cada parte del guión por separado, así como el guión completo. (Proporcionaré un ejemplo concreto)
TicToc2 = TicTocGenerator() # create another instance of the TicTocGen generator
def toc2(tempBool=True):
# Prints the time difference yielded by generator instance TicToc2
tempTimeInterval = next(TicToc2)
if tempBool:
print( "Elapsed time 2: %f seconds./n" %tempTimeInterval )
def tic2():
# Records a time in TicToc2, marks the beginning of a time interval
toc2(False)
Ahora debería poder sincronizar dos cosas por separado: en el siguiente ejemplo, calculamos el tiempo del guión total y las partes de un guión por separado.
tic()
time.sleep(5)
tic2()
time.sleep(3)
toc2() # returns "Elapsed time 2: 5.00 seconds."
toc() # returns "Elapsed time: 8.00 seconds."
En realidad, ni siquiera necesita usar tic()
cada vez. Si tiene una serie de comandos que desea cronometrar, puede escribir
tic()
time.sleep(1)
toc() # returns "Elapsed time: 1.00 seconds."
time.sleep(2)
toc() # returns "Elapsed time: 2.00 seconds."
time.sleep(3)
toc() # returns "Elapsed time: 3.00 seconds."
# and so on...
Espero que esto sea útil.