Fonction de mesure du temps Python


123

Je veux créer une fonction python pour tester le temps passé dans chaque fonction et imprimer son nom avec son heure, comment je peux imprimer le nom de la fonction et s'il y a une autre façon de le faire, veuillez me le dire

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

Les outils de profilage Python peuvent vous montrer les noms des fonctions et le temps passé dans chacune d'elles. Lisez ici: docs.python.org/library/profile.html
Roadmaster

Meilleure utilisation timeitpour la mesure. Ce n'est pas parfait, mais cela bat de loin votre coup et c'est beaucoup plus facile à utiliser timeitque de concocter quelque chose de mieux vous-même.

Réponses:


241

Tout d'abord, je suggère fortement d'utiliser un profileur ou au moins utiliser timeit .

Cependant, si vous vouliez écrire votre propre méthode de chronométrage strictement pour apprendre, voici un endroit pour commencer à utiliser un décorateur.

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

Et l'utilisation est très simple, il suffit d'utiliser le décorateur @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

Notez que j'appelle f.func_namepour obtenir le nom de la fonction sous forme de chaîne (en Python 2) ou f.__name__ en Python 3.


4
exactement ce que je veux :) ... mais vous m'avez convaincu d'utiliser le profileur python
Wazery

3
On dirait que cela suppose que time.time () rapporte le temps en microsecondes depuis l'époque? La documentation indique qu'elle rapporte l'heure en secondes docs.python.org/2/library/time.html#time.time .
Rahul Jha

Cela ne peut pas prendre effet, après avoir utilisé yield dans func. Comment puis-je continuer à utiliser cette méthode et utiliser yield?
jiamo

def timing (f): def wrap (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () La fonction% s d'impression a pris% 0.3 f ms '% (f.func_name, (time2-time1) * 1000) retour retour retour wrap
Vivek Bagaria

1
quel est l'inconvénient de l'écrire vous-même? Le stockage d'une liste des temps écoulés et l'examen de leur distribution n'est-il pas assez simple?
3pitt le

51

Après avoir joué avec le timeitmodule, je n'aime pas son interface, qui n'est pas si élégante comparée aux deux méthodes suivantes.

Le code suivant est en Python 3.

La méthode décoratrice

C'est presque la même chose avec la méthode de @ Mike. Ici, j'ajoute kwargset functoolsj'emballe pour l'améliorer.

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)

La méthode du gestionnaire de contexte

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

Par exemple, vous pouvez l'utiliser comme:

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

Et le code dans le withbloc sera chronométré.

Conclusion

En utilisant la première méthode, vous pouvez facilement commenter le décorateur pour obtenir le code normal. Cependant, il ne peut chronométrer qu'une fonction. Si vous avez une partie de code dont vous ne savez pas quoi faire pour en faire une fonction, vous pouvez choisir la deuxième méthode.

Par exemple, maintenant vous avez

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

Maintenant vous voulez chronométrer le bigImage = ... ligne. Si vous le changez en fonction, ce sera:

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

Ça n'a pas l'air génial ... Et si vous êtes en Python 2, qui n'a pas de nonlocalmot-clé.

Au lieu de cela, utiliser la deuxième méthode convient très bien ici:

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

Contribution intéressante, cependant je trouve inutile que dans la méthode décoratrice que vous avez mentionnée, vous ayez dû changer d' timeitinterface et utiliser la wraps()fonction du functoolsmodule. Je veux dire que tout ce code supplémentaire n'est pas nécessaire.
Billal Begueradj

1
Besoinsimport functools
Guillaume Chevalier

1
Notez que votre décorateur perd la valeur de retour de la fonction d'origine
Marc Van Daele

11

Je ne vois pas quel est le problème avec le timeitmodule. C'est probablement la manière la plus simple de le faire.

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

Il est également possible d'envoyer des arguments aux fonctions. Tout ce dont vous avez besoin est de terminer votre fonction à l'aide de décorateurs. Plus d'explications ici: http://www.pythoncentral.io/time-a-python-function/

Le seul cas où vous pourriez être intéressé par l'écriture de vos propres instructions de minutage est si vous ne souhaitez exécuter une fonction qu'une seule fois et que vous souhaitez également obtenir sa valeur de retour.

L'avantage d'utiliser le timeitmodule est qu'il permet de répéter le nombre d'exécutions. Cela peut être nécessaire car d'autres processus peuvent interférer avec votre précision de synchronisation. Vous devez donc l'exécuter plusieurs fois et rechercher la valeur la plus basse.


3
Envoi d'arguments à la fonction en utilisant des wrappers et des décorateurs? Pourquoi pas timeit.timeit(lambda: func(a,b,c), number=1)? J'utilise ceci lorsque je fais des tests sur une solution hypothétique dans un terminal.
Jack

11

Timeit a deux gros défauts: il ne renvoie pas la valeur de retour de la fonction, et il utilise eval, qui nécessite de passer du code de configuration supplémentaire pour les importations. Cela résout les deux problèmes simplement et élégamment:

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)

Merci! timeit ne fonctionne pas bien avec Apache Spark car vous devez importer toutes les dépendances Spark, et qui veut créer une grosse vieille chaîne qui fait cela? Cette solution est beaucoup plus simple et plus flexible.
Paul

4

Il existe un outil simple pour chronométrer. https://github.com/RalphMao/PyTimer

Cela peut fonctionner comme un décorateur :

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

Production:

matmul:0.368434
matmul:2.839355

Il peut également fonctionner comme une minuterie de plug-in avec contrôle de l'espace de noms (utile si vous l'insérez dans une fonction qui a beaucoup de codes et peut être appelée n'importe où ailleurs).

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

Production:

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

J'espère que cela aidera


3

Méthode Decorator utilisant la bibliothèque Decorator Python:

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

Voir l'article sur mon blog:

publier sur le blog mobilepro.pl

mon message sur Google Plus


1

Ma façon de faire:

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

Définissez une variable comme start = time()avant d'exécuter la fonction / les boucles, et printTime(start)juste après le bloc.

et vous avez la réponse.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.