Qual è l'equivalente Python delle funzioni tic e toc di Matlab?


112

Qual è l'equivalente Python delle funzioni tic e toc di Matlab ?


7
Se vuoi davvero l'equivalente diretto, chiama tic = time.time()e toc = time.time(), quindi print toc-tic, 'sec Elapsed'come la gente ha detto di seguito, però, timeitè più robusto.
Joe Kington

Mi sembra di ottenere risultati migliori usando l'approccio di @ JoeKington in combinazione con timeit.default_timer (), come questo ad esempio:, tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer()then print toc-tic.
littleO

1
La libreria pytictoc sembra la più comoda, la sintassi è anche leggermente più ordinata di ttictoc sotto. pypi.org/project/pytictoc
FlorianH

Risposte:


172

A parte timeitquello menzionato da ThiefMaster, un modo semplice per farlo è solo (dopo l'importazione time):

t = time.time()
# do stuff
elapsed = time.time() - t

Ho una classe di supporto che mi piace usare:

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))

Può essere utilizzato come gestore di contesto:

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

A volte trovo questa tecnica più comoda di timeit- tutto dipende da ciò che vuoi misurare.


25
@eat: rispettosamente non sono d'accordo. Le persone hanno sempre utilizzato il timecomando unix per misurare i tempi di esecuzione dei programmi e questo metodo lo replica all'interno del codice Python. Non ci vedo niente di sbagliato, purché sia ​​lo strumento giusto per il lavoro. timeitnon è sempre così, e un profiler è una soluzione molto più pesante per la maggior parte delle esigenze
Eli Bendersky

4
Per l'ultima riga suggerirei print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'. È difficile da capire senza% .2f. Grazie per l'ottima idea.
Can Kavaklıoğlu

4
Questo sembra molto conveniente a prima vista, ma in pratica richiede di indentare il blocco di codice che si desidera temporizzare, il che può essere abbastanza scomodo a seconda della lunghezza del blocco di codice e dell'editor scelto. Ancora una soluzione elegante, che si comporta correttamente in caso di utilizzo annidato.
Stefan

1
Penso che tu voglia elapsed = t - time.time(), invece di elapsed = time.time() - t. In quest'ultimo trascorso sarà negativo. Ho suggerito questa modifica come modifica.
rysqui

3
@rysqui - L' ora corrente non è sempre un numero maggiore di quella precedente ? Penso che elapsed = time.time() - tsia la forma che dà sempre un valore positivo.
Scott Smith

32

Ho avuto la stessa domanda quando sono migrato a Python da Matlab. Con l'aiuto di questo thread sono stato in grado di costruire un analogo esatto di Matlab tic()e toc()funzioni. Inserisci semplicemente il codice seguente all'inizio dello 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)

Questo è tutto! Ora siamo pronti per l'uso completo tic()e toc()proprio come in Matlab. Per esempio

tic()

time.sleep(5)

toc() # returns "Elapsed time: 5.00 seconds."

In realtà, questo è più versatile delle funzioni Matlab integrate. Qui, puoi creare un'altra istanza di TicTocGeneratorper tenere traccia di più operazioni o semplicemente per temporizzare le cose in modo diverso. Ad esempio, durante il cronometraggio di uno script, ora possiamo cronometrare separatamente ogni parte dello script, così come l'intero script. (Fornirò un esempio 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)

Ora dovresti essere in grado di cronometrare due cose separate: Nell'esempio seguente, cronometriamo lo script totale e le parti di uno script separatamente.

tic()

time.sleep(5)

tic2()

time.sleep(3)

toc2() # returns "Elapsed time 2: 5.00 seconds."

toc() # returns "Elapsed time: 8.00 seconds."

In realtà, non è nemmeno necessario utilizzarlo tic()ogni volta. Se hai una serie di comandi che vuoi cronometrare, puoi scrivere

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

Spero che questo sia di aiuto.


22

Il miglior analogo in assoluto di tic e toc sarebbe semplicemente definirli in 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"

Quindi puoi usarli come:

tic()
# do stuff
toc()

6
Ciò non si comporterà correttamente in caso di utilizzo annidato di tice toc, che Matlab supporta. Sarebbe richiesto un po 'più di raffinatezza.
Stefan

2
Ho implementato funzioni simili nel mio codice quando avevo bisogno di un tempismo di base. Tuttavia, rimuoverei l' import timeesterno di entrambe le funzioni, poiché potenzialmente può richiedere un po 'di tempo.
Bas Swinckels

Se insisti nell'usare questa tecnica e ne hai bisogno per gestire tic / toc annidati, rendi il globale un elenco e lascia che ticspinga ad esso ed tocesca da esso.
Ahmed Fasih

1
Inoltre ho letto altrove che timeit.default_timer()è meglio che time.time()perché time.clock()potrebbe essere più appropriato a seconda del sistema operativo
Miguel

@AhmedFasih Questo è ciò che fa la mia risposta, anche se più cose potrebbero essere migliorate.
antonimmo

15

Di solito, IPython di %time, %timeit, %prune %lprun(se si è line_profilerinstallato) soddisfare i miei bisogni di profilazione abbastanza bene. Tuttavia, un caso d'uso per tic-tocfunzionalità simili è emerso quando ho cercato di profilare calcoli guidati in modo interattivo, cioè dal movimento del mouse dell'utente in una GUI. Sentivo che lo spamming di tics e tocs nelle fonti durante il test interattivo sarebbe stato il modo più veloce per rivelare i colli di bottiglia. Sono andato con la Timerclasse di Eli Bendersky , ma non ero completamente soddisfatto, poiché mi ha richiesto di modificare il rientro del mio codice, il che può essere scomodo in alcuni editor e confondere il sistema di controllo delle versioni. Inoltre, potrebbe essere necessario misurare il tempo tra i punti in diverse funzioni, che non funzionerebbero con l'estensionewithdichiarazione. Dopo aver provato un sacco di intelligenza Python, ecco la semplice soluzione che ho trovato funzionava meglio:

from time import time
_tstart_stack = []

def tic():
    _tstart_stack.append(time())

def toc(fmt="Elapsed: %s s"):
    print fmt % (time() - _tstart_stack.pop())

Da questa opere spingendo l'orario di inizio su una pila, che possa funzionare correttamente per diversi livelli di tics e tocs. Permette anche di cambiare la stringa di formato dell'istruzione tocper visualizzare informazioni aggiuntive, cosa che mi è piaciuta della Timerclasse di Eli .

Per qualche motivo mi sono preoccupato del sovraccarico di un'implementazione Python pura, quindi ho testato anche un modulo di estensione 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);
}

Questo è per MacOSX e ho omesso il codice per verificare se lvlè fuori dai limiti per brevità. Sebbene tictoc.res()produca una risoluzione di circa 50 nanosecondi sul mio sistema, ho scoperto che il jitter della misurazione di qualsiasi istruzione Python è facilmente dell'ordine dei microsecondi (e molto di più se usato da IPython). A questo punto, il sovraccarico dell'implementazione di Python diventa trascurabile, in modo che possa essere utilizzato con la stessa sicurezza dell'implementazione C.

Ho scoperto che l'utilità tic-tocdell'approccio-è praticamente limitata ai blocchi di codice che richiedono più di 10 microsecondi per essere eseguiti. Al di sotto di questo, timeitsono necessarie strategie di media come in per ottenere una misurazione fedele.


1
Estremamente elegante, @Stefan - non posso credere che sia così basso. Grazie!
lunedì

10

Puoi usare tice tocda ttictoc. Installalo con

pip install ttictoc

E importali nel tuo script come segue

from ttictoc import tic,toc
tic()
# Some code
print(toc())

8

Ho appena creato un modulo [tictoc.py] per ottenere tic toc annidati, che è ciò che fa Matlab.

from time import time

tics = []

def tic():
    tics.append(time())

def toc():
    if len(tics)==0:
        return None
    else:
        return time()-tics.pop()

E funziona in questo modo:

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()

Spero possa essere d'aiuto.


Bella replica di tic / toc da MATLAB!
Matt

1
Devo avvertirti che questo potrebbe non comportarsi come desiderato se usato contemporaneamente da più di 1 modulo, poiché i moduli (AFAIK) si comportano come singleton.
antonimmo

3

Dai un'occhiata al timeitmodulo. Non è proprio equivalente ma se il codice che vuoi cronometrare è all'interno di una funzione puoi usarlo facilmente.


Sì, timeitè il migliore per i benchmark. Non deve nemmeno essere una singola funzione, puoi passare istruzioni complesse in modo anomalo.

10
Bene, passare del codice che non è una chiamata di funzione estremamente semplice come stringa è molto brutto.
ThiefMaster


1

Questo può essere fatto anche usando un wrapper. Modo molto generale di tenere il tempo.

Il wrapper in questo codice di esempio racchiude qualsiasi funzione e stampa la quantità di tempo necessaria per eseguire la funzione:

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()

La funzione wrapper, timethis è chiamata decoratore. Una spiegazione un po 'più dettagliata, qui: medium.com/pythonhive/…
Mircea

1

Ho cambiato un po 'la risposta di @Eli Bendersky per usare ctor __init__()e dtor __del__()per fare il tempismo, in modo che possa essere usato più comodamente senza indentare il codice originale:

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)

Per usarlo, metti semplicemente Timer ("blahblah") all'inizio di un ambito locale. Il tempo trascorso verrà stampato alla fine dell'ambito:

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

Stampa:

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

3
Un problema con questa implementazione è il fatto che timernon viene cancellato dopo l'ultima chiamata, se qualsiasi altro codice segue dopo il forciclo. Per ottenere l'ultimo valore del timer, è necessario eliminare o sovrascrivere il timerdopo il forciclo, ad esempio tramite timer = None.
bastelflp

1
@bastelflp Mi sono appena reso conto di aver frainteso quello che volevi dire ... Il tuo suggerimento è stato incorporato nel codice ora. Grazie.
Shaohua Li

1

Aggiornamento della risposta di 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)

Proprio come quello di Eli, può essere utilizzato come gestore di contesto:

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

Produzione:

[Count] Elapsed: 0.27 seconds

L'ho anche aggiornato per stampare le unità di tempo riportate (secondi) e tagliare il numero di cifre come suggerito da Can, e con la possibilità di aggiungerlo anche a un file di registro. È necessario importare datetime per utilizzare la funzione di registrazione:

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

0

Basandomi sulle risposte di Stefan e antonimmo, ho finito per mettere

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

in un utils.pymodulo e lo uso con un file

from utils import Tictoc
tic, toc = Tictoc()

Per di qua

  • si può semplicemente utilizzare tic(), toc()e nidificano loro come in Matlab
  • In alternativa, è possibile assegnare un nome: tic(1), toc(1)o tic('very-important-block'), toc('very-important-block')e timer con nomi diversi non interferire
  • importandoli in questo modo si evita l'interferenza tra i moduli che lo utilizzano.

(qui toc non stampa il tempo trascorso, ma lo restituisce.)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.