¿Cuál es el equivalente en Python de las funciones tic y toc de Matlab?

112

¿Cuál es el equivalente en Python de las funciones tic y toc de Matlab ?

Alex
fuente
7
Si realmente desea el equivalente directo, simplemente llame tic = time.time()y toc = time.time(), print toc-tic, 'sec Elapsed'como la gente ha dicho a continuación, timeites más robusto.
Joe Kington
Parece que obtengo mejores resultados usando el enfoque de @ JoeKington junto con timeit.default_timer (), como este, por ejemplo:, tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer()entonces print toc-tic.
littleO
1
La biblioteca pytictoc parece más conveniente, la sintaxis es incluso un poco más ordenada que la de ttictoc a continuación. pypi.org/project/pytictoc
FlorianH

Respuestas:

172

Aparte de lo timeitque ThiefMaster mencionó, una forma sencilla de hacerlo es simplemente (después de importar 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 utilizar como administrador de contexto:

with Timer('foo_stuff'):
   # do some foo
   # do some stuff

A veces encuentro esta técnica más conveniente que timeit... todo depende de lo que quieras medir.

Eli Bendersky
fuente
25
@eat: Respetuosamente no estoy de acuerdo. La gente ha estado usando el timecomando Unix para medir los tiempos de ejecución de los programas desde siempre, y este método replica esto dentro del código Python. No veo nada malo en ello, siempre que sea la herramienta adecuada para el trabajo. timeitno siempre es eso, y un generador de perfiles es una solución mucho más pesada para la mayoría de las necesidades
Eli Bendersky
4
Para la última línea, sugeriría print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'. Es difícil de entender sin% .2f. Gracias por la gran idea.
Can Kavaklıoğlu
4
Esto parece muy conveniente a primera vista, pero en la práctica requiere que uno haga una sangría en el bloque de código que desea cronometrar, lo que puede ser bastante inconveniente dependiendo de la longitud del bloque de código y del editor elegido. Sigue siendo una solución elegante, que se comporta correctamente en el caso de uso anidado.
Stefan
1
Creo que quieres elapsed = t - time.time(), en lugar de elapsed = time.time() - t. En este último transcurrido será negativo. Sugerí este cambio como una edición.
rysqui
3
@rysqui - ¿No es la hora actual siempre un número mayor que la hora anterior ? Creo que elapsed = time.time() - tes la forma que siempre da un valor positivo.
Scott Smith
32

Tuve la misma pregunta cuando migré a Python desde Matlab. Con la ayuda de este hilo pude construir un análogo exacto de Matlab tic()y toc()funciones. Simplemente inserte el siguiente código en la parte superior de su secuencia de comandos.

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 completamente 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í, puede crear otra instancia de TicTocGeneratorpara realizar un seguimiento de múltiples operaciones, o simplemente para cronometrar las cosas de manera diferente. Por ejemplo, mientras cronometramos un guión, ahora podemos cronometrar cada parte del guión por separado, así como todo el guión. (Daré 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 cronometrar dos cosas distintas: en el siguiente ejemplo, cronometramos el guión total y 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 usarlo 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.

Benben
fuente
22

La mejor analogía absoluta 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()
InvitadoPoster
fuente
6
Esto no se comportará correctamente en el caso del uso anidado de ticy toc, que Matlab admite. Se necesitaría un poco más de sofisticación.
Stefan
2
Implementé funciones similares en mi propio código cuando necesitaba una sincronización básica. Sin embargo, eliminaría el import timeexterior de ambas funciones, ya que potencialmente puede llevar bastante tiempo.
Bas Swinckels
Si insiste en usar esta técnica y la necesita para manejar tic / toc anidado, haga una lista global y déjela ticpresionar y salir tocde ella.
Ahmed Fasih
1
También leí en otra parte que timeit.default_timer()es mejor que time.time()porque time.clock()podría ser más apropiado según el sistema operativo
Miguel
@AhmedFasih Eso es lo que hace mi respuesta, aunque se podrían mejorar más cosas.
antonimmo
15

Por lo general, IPython de %time, %timeit, %pruny %lprun(si se ha line_profilerinstalado) satisfacer mis necesidades de perfiles bastante bien. Sin embargo, tic-tocsurgió un caso de uso para una funcionalidad similar a la de cuando traté de perfilar cálculos que eran impulsados ​​interactivamente, es decir, por el movimiento del mouse del usuario en una GUI. Me sentí como el correo basura tics y tocs en las fuentes mientras que las pruebas de forma interactiva sería la forma más rápida para revelar los cuellos de botella. Fui con la Timerclase de 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 inconveniente en algunos editores y confunde el sistema de control de versiones. Además, puede ser necesario medir el tiempo entre puntos en diferentes funciones, lo que no funcionaría con elwithdeclaración. Después de probar mucha inteligencia de Python, aquí está la solución simple que encontré que funcionaba 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 las horas de inicio en una pila, funcionará correctamente para múltiples niveles de ticsy tocs. También permite cambiar la cadena de formato de la tocdeclaración para mostrar información adicional, que me gustó de la Timerclase de Eli .

Por alguna razón, me preocupó la sobrecarga de una implementación de Python pura, así que también probé un módulo de extensión de C:

#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 lvlestá fuera de los límites por brevedad. Si bien tictoc.res()produce una resolución de aproximadamente 50 nanosegundos en mi sistema, descubrí que el jitter de medir cualquier declaración de Python está fácilmente en el rango de 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 tic-tocenfoque-está prácticamente limitada a bloques de código que tardan más de 10 microsegundos en ejecutarse. Por debajo de eso, timeitse requieren estrategias de promediado como en para obtener una medición fiel.

Stefan
fuente
1
Extremadamente elegante, @Stefan: no puedo creer que esto tenga una calificación tan baja. ¡Gracias!
espere el
10

Puede usar ticy tocdesde ttictoc. Instalarlo con

pip install ttictoc

Y simplemente impórtelos en su script de la siguiente manera

from ttictoc import tic,toc
tic()
# Some code
print(toc())
H. Sánchez
fuente
8

Acabo de crear un módulo [tictoc.py] para lograr tic tocs 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.

antonimmo
fuente
¡Buena réplica de tic / toc de MATLAB!
Matt
1
Debo advertirle que esto podría no comportarse como se desea cuando se usa simultáneamente por más de 1 módulo, ya que los módulos (AFAIK) se comportan como singletons.
antonimmo
3

Eche un vistazo al timeitmódulo. No es realmente equivalente, pero si el código que desea cronometrar está dentro de una función, puede usarlo fácilmente.

ThiefMaster
fuente
Sí, timeites mejor para los puntos de referencia. Ni siquiera tiene que ser una función única, puede pasar declaraciones abritamente complejas.
10
Bueno, pasar código que no es una llamada de función extremadamente simple como una cadena es muy feo.
ThiefMaster
2
pip install easy-tictoc

En el código:

from tictoc import tic, toc

tic()

#Some code

toc()

Descargo de responsabilidad: soy el autor de esta biblioteca.

Shakib Rahimi
fuente
Por favor, no copie simplemente otra respuesta, incluso si es la suya. stackoverflow.com/a/59271862/8239061
SecretAgentMan
1

Esto también se puede hacer usando una envoltura. Manera muy general de llevar el tiempo.

El contenedor en este código de ejemplo envuelve 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()
Coen Jonker
fuente
La función de envoltura, timethis, se llama decorador. Una explicación un poco más detallada, aquí: medium.com/pythonhive/…
Mircea
1

Cambié un poco la respuesta de @Eli Bendersky para usar ctor __init__()y dtor __del__()para hacer el tiempo, de modo que pueda usarse de manera más conveniente 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 usarlo, 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
Shaohua Li
fuente
3
Un problema con esta implementación es el hecho de que timerno se elimina después de la última llamada, si algún otro código sigue después del forciclo. Para obtener el último valor del temporizador, se debe eliminar o sobrescribir el timerdespués del forciclo, por ejemplo, a través de timer = None.
bastelflp
1
@bastelflp Me acabo de dar cuenta de que entendí mal lo que querías decir ... Ahora tu sugerencia se ha incorporado al código. Gracias.
Shaohua Li
1

Actualizando la respuesta de Eli a Python 3:

class Timer(object):
    def __init__(self, name=None, filename=None):
        self.name = name
        self.filename = filename

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        message = 'Elapsed: %.2f seconds' % (time.time() - self.tstart)
        if self.name:
            message = '[%s] ' % self.name + message
        print(message)
        if self.filename:
            with open(self.filename,'a') as file:
                print(str(datetime.datetime.now())+": ",message,file=file)

Al igual que Eli, se puede utilizar como administrador de contexto:

import time 
with Timer('Count'):
    for i in range(0,10_000_000):
        pass

Salida:

[Count] Elapsed: 0.27 seconds

También lo he actualizado para imprimir las unidades de tiempo reportadas (segundos) y recortar el número de dígitos como sugiere Can, y con la opción de agregar también a un archivo de registro. Debe importar la fecha y la hora para usar la función de registro:

import time
import datetime 
with Timer('Count', 'log.txt'):    
    for i in range(0,10_000_000):
        pass
Josiah Yoder
fuente
0

Basándome en 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 utils.pymódulo, y lo uso con un

from utils import Tictoc
tic, toc = Tictoc()

De esta manera

  • simplemente puedes usar tic(),toc() y nido de ellos como en Matlab
  • Como alternativa, puede nombrarlos: tic(1), toc(1)o tic('very-important-block'), toc('very-important-block')y temporizadores con diferentes nombres no interferirá
  • importarlos de esta manera evita interferencias entre los módulos que lo utilizan.

(aquí toc no imprime el tiempo transcurrido, pero lo devuelve).

Máxima
fuente