Função de medida de tempo Python

121

Eu quero criar uma função python para testar o tempo gasto em cada função e imprimir seu nome com o tempo, como posso imprimir o nome da função e se houver outra maneira de fazê-lo, por favor, diga-me

def measureTime(a):
    start = time.clock() 
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print "Time spent in (function name) is: ", elapsed
Wazery
fonte
As ferramentas de criação de perfil Python podem mostrar os nomes das funções e o tempo gasto em cada uma. Leia aqui: docs.python.org/library/profile.html
Roadmaster
Melhor uso timeitpara a medição. Não é perfeito, mas supera de longe a facada e é muito mais fácil de usar timeitdo que preparar algo melhor para si mesmo.

Respostas:

240

Em primeiro lugar, sugiro o uso de um criador de perfil ou, pelo menos, use timeit .

No entanto, se você quiser escrever seu próprio método de tempo estritamente para aprender, aqui está um lugar para começar a usar um decorador.

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 o uso é muito simples, basta usar o decorador @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

Note que estou ligando f.func_namepara obter o nome da função como uma string (no Python 2) ou f.__name__ no Python 3.

Mike Lewis
fonte
4
exatamente o que eu quero :) ... mas vocês me convenceu a usar o profiler python
Wazery
3
Parece que isso pressupõe que o time.time () relata o tempo em microssegundos desde a época? A documentação diz que reporta o tempo em segundos docs.python.org/2/library/time.html#time.time .
Rahul Jha
Isso não pode ter efeito, depois de usar yield em func. Como ainda posso usar esse método e usar o rendimento?
Jiamo 03/05
def timing (f): def wrap (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () print '% s função levou% 0.3 f ms '% (f.func_name, (time2-time1) * 1000) return ret return wrap
Vivek Bagaria
1
qual é a desvantagem de escrever você mesmo? O armazenamento de uma lista de tempos decorridos e o exame de sua distribuição não são suficientemente simples?
3pitt
51

Depois de jogar com o timeitmódulo, não gosto da sua interface, que não é tão elegante em comparação com os dois métodos a seguir.

O código a seguir está no Python 3.

O método decorador

Isso é quase o mesmo com o método @ Mike. Aqui eu adiciono kwargse functoolsenvolvo para torná-lo melhor.

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)

O método do gerenciador de contexto

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

Por exemplo, você pode usá-lo como:

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

E o código dentro do withbloco será cronometrado.

Conclusão

Usando o primeiro método, você pode comentar com facilidade o decorador para obter o código normal. No entanto, só pode cronometrar uma função. Se você tem alguma parte do código que não faz o que a torna uma função, pode escolher o segundo método.

Por exemplo, agora você tem

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

Agora você quer cronometrar a bigImage = ...linha. Se você mudar para uma função, será:

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

Não parece tão bom ... E se você estiver no Python 2, que não tem nonlocalpalavra-chave.

Em vez disso, o uso do segundo método se encaixa aqui muito bem:

images = get_images()
with timeit_context('foobar'):
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)
Raio
fonte
Contribuição interessante, no entanto, acho inútil que, no método decorador que você mencionou, você tenha que mudar de timeitinterface e usar a wraps()função do functoolsmódulo. Quero dizer, todo esse código extra não é necessário.
Billal Begueradj
1
Needsimport functools
Guillaume Chevalier
1
Note-se que o seu decorador perde o valor de retorno da função original
Marc Van Daele
11

Não vejo qual é o problema com o timeitmódulo. Esta é provavelmente a maneira mais simples de fazer isso.

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

Também é possível enviar argumentos para as funções. Tudo que você precisa é encerrar sua função usando decoradores. Mais explicações aqui: http://www.pythoncentral.io/time-a-python-function/

O único caso em que você pode estar interessado em escrever suas próprias instruções de tempo é se você deseja executar uma função apenas uma vez e também deseja obter seu valor de retorno.

A vantagem de usar o timeit módulo é que ele permite repetir o número de execuções. Isso pode ser necessário porque outros processos podem interferir na sua precisão de tempo. Portanto, você deve executá-lo várias vezes e observar o valor mais baixo.

Phani
fonte
3
Enviando argumentos para a função usando wrappers e decoradores? Por que não timeit.timeit(lambda: func(a,b,c), number=1)? Eu uso isso ao fazer testes em uma solução hipotética em um terminal.
Jack
10

O Timeit possui duas grandes falhas: não retorna o valor de retorno da função e usa eval, o que requer a passagem de um código de configuração extra para importações. Isso resolve os problemas de maneira simples e 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)
Jonathan Ray
fonte
Obrigado! timeit não funciona bem com o Apache Spark porque você precisa importar todas as dependências do Spark e quem deseja criar uma grande e antiga sequência que faça isso? Esta solução é muito mais simples e mais flexível.
Paul
4

Existe uma ferramenta fácil para cronometrar. https://github.com/RalphMao/PyTimer

Pode funcionar como um decorador :

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

Resultado:

matmul:0.368434
matmul:2.839355

Também pode funcionar como um timer de plug-in com controle de namespace (útil se você estiver inserindo-o em uma função que possui muitos códigos e pode ser chamada em qualquer outro lugar).

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

Resultado:

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

Espero que ajude

ralphmao95
fonte
3

Método Decorator usando a biblioteca Python do decorador:

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

Veja post no meu blog:

publicar no blog mobilepro.pl

minha postagem no Google Plus

MobilePro.pl
fonte
1

Minha maneira de fazer isso:

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

Defina uma variável como start = time()antes, execute a função / loops e printTime(start)logo após o bloco.

e você conseguiu a resposta.

Adam Liu
fonte