Qual é o equivalente em Python das funções tic e toc do Matlab?


112

Qual é o equivalente em Python das funções tic e toc do Matlab ?


7
Se você realmente deseja o equivalente direto, basta ligar tic = time.time()e toc = time.time(), print toc-tic, 'sec Elapsed'como o pessoal disse abaixo, porém, timeité mais robusto.
Joe Kington

Parece que consigo melhores resultados usando a abordagem de @JoeKington em conjunto com timeit.default_timer (), como este por exemplo:, tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer()então print toc-tic.
LittleO

1
A biblioteca pytictoc parece mais conveinente, a sintaxe é até um pouco mais organizada que ttictoc abaixo. pypi.org/project/pytictoc
FlorianH

Respostas:


172

Além do timeitque o ThiefMaster mencionou, uma maneira simples de fazer isso é apenas (após a importação time):

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

Tenho uma classe auxiliar que gosto de 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))

Ele pode ser usado como um gerenciador de contexto:

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

Às vezes, acho essa técnica mais conveniente do que timeit- tudo depende do que você deseja medir.


25
@eat: discordo respeitosamente. As pessoas têm usado o timecomando unix para medir tempos de execução de programas desde sempre, e esse método replica isso dentro do código Python. Não vejo nada de errado nisso, desde que seja a ferramenta certa para o trabalho. timeitnem sempre é isso, e um profiler é uma solução muito mais pesada para a maioria das necessidades
Eli Bendersky

4
Para a última linha, eu sugeriria print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'. É difícil entender sem o% .2f. Obrigado pela ótima ideia.
Can Kavaklıoğlu

4
Isso parece muito conveniente à primeira vista, mas na prática requer que se indente o bloco de código desejado, o que pode ser bastante inconveniente dependendo do comprimento do bloco de código e do editor de escolha. Ainda uma solução elegante, que se comporta corretamente no caso de uso aninhado.
Stefan

1
Eu acho que você quer elapsed = t - time.time(), em vez de elapsed = time.time() - t. No último, decorrido será negativo. Eu sugeri essa mudança como uma edição.
rysqui

3
@rysqui - A hora atual não é sempre um número maior do que a hora anterior ? Eu acho que elapsed = time.time() - té a forma que sempre produz um valor positivo.
Scott Smith

32

Eu tive a mesma pergunta quando migrei para python do Matlab. Com a ajuda deste tópico, fui capaz de construir um análogo exato do Matlab tic()e toc()funções. Basta inserir o código a seguir na parte superior do seu 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)

É isso aí! Agora estamos prontos para usar totalmente tic()e toc()exatamente como no Matlab. Por exemplo

tic()

time.sleep(5)

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

Na verdade, isso é mais versátil do que as funções integradas do Matlab. Aqui, você pode criar outra instância do TicTocGeneratorpara controlar várias operações ou apenas para cronometrar as coisas de forma diferente. Por exemplo, enquanto cronometramos um script, agora podemos cronometrar cada parte do script separadamente, bem como o script inteiro. (Vou dar um exemplo 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)

Agora você deve ser capaz de cronometrar duas coisas distintas: No exemplo a seguir, cronometramos o script total e partes de um script separadamente.

tic()

time.sleep(5)

tic2()

time.sleep(3)

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

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

Na verdade, você nem precisa usar tic()cada vez. Se você tem uma série de comandos que deseja cronometrar, pode escrever

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 isso seja útil.


22

O melhor análogo absoluto de tic e toc seria simplesmente defini-los em 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"

Então você pode usá-los como:

tic()
# do stuff
toc()

6
Isso não se comportará corretamente no caso de uso aninhado de tice toc, que o Matlab suporta. Um pouco mais de sofisticação seria necessário.
Stefan

2
Implementei funções semelhantes em meu próprio código quando precisei de algum tempo básico. No entanto, eu removeria a parte import timeexterna de ambas as funções, já que pode levar algum tempo.
Bas Swinckels de

Se você insiste em usar essa técnica e precisa dela para lidar com tic / toc aninhado, faça do global uma lista e deixe-o ticempurrar e tocsair dela.
Ahmed Fasih

1
Também li em outro lugar que timeit.default_timer()é melhor do que time.time()porque time.clock()pode ser mais apropriado dependendo do sistema operacional
Miguel

@AhmedFasih É o que minha resposta faz, embora mais coisas possam ser melhoradas.
antonimmo

15

Normalmente, IPython de %time, %timeit, %prune %lprun(caso tenha line_profilerinstalado) satisfazer as minhas necessidades de perfis muito bem. No entanto, um caso de uso para tic-tocfuncionalidade semelhante surgiu quando tentei traçar o perfil de cálculos que eram dirigidos interativamente, ou seja, pelo movimento do mouse do usuário em uma GUI. Achei que enviar spam para tics e tocs nas fontes enquanto testava interativamente seria a maneira mais rápida de revelar os gargalos. Fui com a Timeraula de Eli Bendersky , mas não fiquei muito feliz, pois me obrigou a alterar a indentação do meu código, o que pode ser inconveniente em alguns editores e confunde o sistema de controle de versão. Além disso, pode haver a necessidade de medir o tempo entre pontos em diferentes funções, o que não funcionaria com owithdeclaração. Depois de tentar muita inteligência Python, aqui está a solução simples que achei que funcionou melhor:

from time import time
_tstart_stack = []

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

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

Uma vez que isso funciona empurrando os tempos iniciais em uma pilha, funcionará corretamente para vários níveis de tics e tocs. Ele também permite alterar a string de formato da tocinstrução para exibir informações adicionais, o que eu gostei na Timeraula de Eli .

Por algum motivo, fiquei preocupado com a sobrecarga de uma implementação Python pura, então testei um módulo de extensão C também:

#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);
}

Isso é para MacOSX e omiti o código para verificar se lvlestá fora dos limites por questão de brevidade. Embora tictoc.res()produza uma resolução de cerca de 50 nanossegundos em meu sistema, descobri que o jitter de medir qualquer instrução Python está facilmente na faixa de microssegundos (e muito mais quando usado no IPython). Nesse ponto, a sobrecarga da implementação Python torna-se insignificante, de modo que pode ser usada com a mesma confiança da implementação C.

Descobri que a utilidade da tic-tocabordagem -é praticamente limitada a blocos de código que levam mais de 10 microssegundos para serem executados. Abaixo disso, estratégias de média como em timeitsão necessárias para obter uma medição fiel.


1
Extremamente elegante, @Stefan - não posso acreditar que isso é tão mal avaliado. Obrigado!
thclark

10

Você pode usar tice tocde ttictoc. Instale com

pip install ttictoc

E basta importá-los em seu script da seguinte forma

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

8

Acabei de criar um módulo [tictoc.py] para obter tic tocs aninhados, que é o que o Matlab faz.

from time import time

tics = []

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

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

E funciona assim:

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


Boa replicação do tic / toc do MATLAB!
Matt

1
Devo avisar que isso pode não se comportar como desejado quando usado simultaneamente por mais de 1 módulo, uma vez que os módulos (AFAIK) se comportam como singletons.
antonimmo

3

Dê uma olhada no timeitmódulo. Não é realmente equivalente, mas se o código que você deseja cronometrar estiver dentro de uma função, você pode usá-lo facilmente.


Sim, timeité melhor para benchmarks. Não precisa ser uma única função, você pode passar instruções abritariamente complexas.

10
Bem, passar um código que não é uma chamada de função extremamente simples como uma string é muito feio.
ThiefMaster


1

Isso também pode ser feito usando um invólucro. Maneira muito geral de marcar o tempo.

O wrapper neste código de exemplo envolve qualquer função e imprime a quantidade de tempo necessária para executar a função:

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

A função de wrapper, timethis, é chamada de decorador. Uma explicação um pouco mais detalhada, aqui: medium.com/pythonhive/…
Mircea

1

Mudei um pouco a resposta de @Eli Bendersky para usar o ctor __init__()e o dtor __del__()para fazer o tempo, para que possa ser usado de maneira mais conveniente sem recuar o 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, basta colocar Timer ("blahblah") no início de algum escopo local. O tempo decorrido será impresso no final do escopo:

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

Ele 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

3
Um problema com essa implementação é o fato de que timernão é excluída após a última chamada, se qualquer outro código segue após o forloop. Para obter o último valor do temporizador, deve-se excluir ou sobrescrever o timerafter the forloop, por exemplo, via timer = None.
bastelflp

1
@bastelflp Acabei de perceber que entendi mal o que você quis dizer ... Sua sugestão foi incorporada ao código agora. Obrigado.
Shaohua Li

1

Atualizando a resposta de Eli para 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)

Assim como o de Eli, ele pode ser usado como um gerenciador de contexto:

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

Resultado:

[Count] Elapsed: 0.27 seconds

Também o atualizei para imprimir as unidades de tempo relatadas (segundos) e cortar o número de dígitos conforme sugerido por Can, e com a opção de também anexar a um arquivo de log. Você deve importar data e hora para usar o recurso de registro:

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

0

Com base nas respostas de Stefan e antonimmo, acabei colocando

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

em um utils.pymódulo, e eu o uso com um

from utils import Tictoc
tic, toc = Tictoc()

Deste jeito

  • você pode simplesmente usar tic(), toc()e ninho-los como em Matlab
  • Alternativamente, você pode nomeá-los: tic(1), toc(1)ou tic('very-important-block'), toc('very-important-block')e temporizadores com nomes diferentes não irá interferir
  • importá-los dessa forma evita a interferência entre os módulos que o utilizam.

(aqui toc não imprime o tempo decorrido, mas o retorna.)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.