Funzione di misurazione del tempo di Python


123

Voglio creare una funzione python per testare il tempo trascorso in ciascuna funzione e stamparne il nome con il suo tempo, come posso stampare il nome della funzione e se c'è un altro modo per farlo per favore dimmelo

def measureTime(a):
    start = time.clock() 
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print "Time spent in (function name) is: ", elapsed

Gli strumenti di profiling di Python possono mostrarti i nomi delle funzioni e il tempo trascorso in ciascuna di esse. Leggi qui: docs.python.org/library/profile.html
Roadmaster

Migliore utilizzo timeitper la misurazione. Non è perfetto, ma batte di gran lunga la tua pugnalata ed è molto più facile da usare timeitche creare qualcosa di meglio da solo.

Risposte:


241

Innanzitutto, consiglio vivamente di utilizzare un profiler o almeno di utilizzare timeit .

Tuttavia, se si desidera scrivere rigorosamente il proprio metodo di temporizzazione per imparare, ecco da qualche parte per iniziare a utilizzare un decoratore.

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

E l'utilizzo è molto semplice, basta usare il decoratore @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

Nota sto chiamando f.func_nameper ottenere il nome della funzione come stringa (in Python 2) o f.__name__ in Python 3.


4
esattamente quello che voglio :) ... ma voi ragazzi mi avete convinto a usare il profiler python
Wazery

3
Sembra che questo presupponga che time.time () riporti il ​​tempo in microsecondi dall'epoca? La documentazione dice che riporta il tempo in secondi docs.python.org/2/library/time.html#time.time .
Rahul Jha

Questo non può avere effetto, dopo aver usato yield in func. Come posso usare ancora questo metodo e posso usare il rendimento?
jiamo

def timing (f): def wrap (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () print '% s funzione ha preso% 0.3 f ms '% (f.func_name, (time2-time1) * 1000) return ret return wrap
Vivek Bagaria

1
qual è lo svantaggio di scriverlo da soli? Memorizzare un elenco di tempi trascorsi ed esaminare la loro distribuzione non è abbastanza semplice?
3pitt

51

Dopo aver giocato con il timeitmodulo, non mi piace la sua interfaccia, che non è così elegante rispetto ai due metodi seguenti.

Il codice seguente è in Python 3.

Il metodo del decoratore

Questo è quasi lo stesso con il metodo di @ Mike. Qui aggiungo kwargse functoolsavvolgo per renderlo migliore.

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)

Il metodo del gestore di contesto

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

Ad esempio, puoi usarlo come:

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

E il codice all'interno del withblocco verrà temporizzato.

Conclusione

Usando il primo metodo, puoi facilmente commentare il decoratore per ottenere il codice normale. Tuttavia, può solo cronometrare una funzione. Se hai una parte di codice che non sai come renderlo una funzione, puoi scegliere il secondo metodo.

Ad esempio, ora hai

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

Ora vuoi cronometrare la bigImage = ...linea. Se lo cambi in una funzione, sarà:

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

Non sembra così eccezionale ... E se fossi in Python 2, che non ha nonlocalparole chiave.

Invece, l'uso del secondo metodo si adatta molto bene qui:

images = get_images()
with timeit_context('foobar'):
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

Contributo interessante, tuttavia trovo inutile che nel metodo decorator che hai citato, hai dovuto cambiare timeitinterfaccia e utilizzare la wraps()funzione del functoolsmodulo. Voglio dire, tutto quel codice extra non è necessario.
Billal Begueradj

1
Esigenzeimport functools
Guillaume Chevalier

1
Nota che il tuo decoratore perde il valore restituito dalla funzione originale
Marc Van Daele

11

Non vedo quale sia il problema con il timeitmodulo. Questo è probabilmente il modo più semplice per farlo.

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

È anche possibile inviare argomenti alle funzioni. Tutto ciò di cui hai bisogno è concludere la tua funzione usando i decoratori. Ulteriori spiegazioni qui: http://www.pythoncentral.io/time-a-python-function/

L'unico caso in cui potresti essere interessato a scrivere le tue istruzioni di temporizzazione è se desideri eseguire una funzione solo una volta e vuoi anche ottenere il suo valore di ritorno.

Il vantaggio dell'utilizzo del timeitmodulo è che consente di ripetere il numero di esecuzioni. Ciò potrebbe essere necessario perché altri processi potrebbero interferire con la precisione dei tempi. Quindi, dovresti eseguirlo più volte e guardare il valore più basso.


3
Invio di argomenti alla funzione utilizzando wrapper e decoratori? Perché no timeit.timeit(lambda: func(a,b,c), number=1)? Lo uso quando eseguo test su una soluzione ipotetica in un terminale.
Jack

11

Timeit ha due grossi difetti: non restituisce il valore di ritorno della funzione e utilizza eval, che richiede il passaggio di codice di configurazione aggiuntivo per le importazioni. Questo risolve entrambi i problemi in modo semplice ed 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)

Grazie! timeit non funziona bene con Apache Spark perché devi importare tutte le dipendenze di Spark e chi vuole creare una vecchia stringa grande che lo faccia? Questa soluzione è molto più semplice e flessibile.
Paul

4

C'è uno strumento facile per il tempismo. https://github.com/RalphMao/PyTimer

Può funzionare come un decoratore :

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

Produzione:

matmul:0.368434
matmul:2.839355

Può anche funzionare come un timer plug-in con controllo dello spazio dei nomi (utile se lo stai inserendo in una funzione che ha molti codici e può essere chiamata altrove).

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

Produzione:

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

Spero che possa aiutare


3

Metodo di decoratore che utilizza la libreria Python del decoratore:

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

Vedi post sul mio blog:

pubblicare sul blog mobilepro.pl

il mio post su Google Plus


1

Il mio modo di farlo:

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

Imposta una variabile come start = time()prima di eseguire la funzione / i cicli e printTime(start)subito dopo il blocco.

e hai la risposta.

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.