web-dev-qa-db-fra.com

Synchronisation précise des fonctions dans python

Je programme en python sur Windows et je voudrais mesurer avec précision le temps nécessaire à une fonction pour s'exécuter. J'ai écrit une fonction "time_it" qui prend une autre fonction, l'exécute et renvoie le temps nécessaire à l'exécution.

def time_it(f, *args):
    start = time.clock()
    f(*args)
    return (time.clock() - start)*1000

j'appelle cela 1000 fois et je fais la moyenne du résultat. (la constante de 1000 à la fin est de donner la réponse en millisecondes.)

Cette fonction semble fonctionner mais j'ai ce sentiment persistant que je fais quelque chose de mal, et qu'en le faisant de cette façon, j'utilise plus de temps que la fonction n'utilise réellement lors de son fonctionnement.

Existe-t-il une manière plus standard ou acceptée de procéder?

Lorsque j'ai changé ma fonction de test pour appeler une impression afin qu'elle prenne plus de temps, ma fonction time_it renvoie une moyenne de 2,5 ms tandis que le cProfile.run ('f ()') retourne et une moyenne de 7,0 ms. J'ai pensé que ma fonction surestimerait le temps si quoi que ce soit, que se passe-t-il ici?

Une remarque supplémentaire, c'est le temps relatif des fonctions les unes par rapport aux autres qui m'importe, pas le temps absolu car cela variera évidemment en fonction du matériel et d'autres facteurs.

62
Atilio Jobson

Au lieu d'écrire votre propre code de profilage, je vous suggère de vérifier les Python profilers intégrés (profile ou cProfile, selon vos besoins): - http://docs.python.org/library/profile.html

37
Daniel Lew

Utilisez le module timeit de la bibliothèque standard Python.

Utilisation de base:

from timeit import Timer

# first argument is the code to be run, the second "setup" argument is only run once,
# and it not included in the execution time.
t = Timer("""x.index(123)""", setup="""x = range(1000)""")

print t.timeit() # prints float, for example 5.8254
# ..or..
print t.timeit(1000) # repeat 1000 times instead of the default 1million
69
Alex Martelli

Vous pouvez créer un décorateur "timeme" comme ça

import time                                                

def timeme(method):
    def wrapper(*args, **kw):
        startTime = int(round(time.time() * 1000))
        result = method(*args, **kw)
        endTime = int(round(time.time() * 1000))

        print(endTime - startTime,'ms')
        return result

    return wrapper

@timeme
def func1(a,b,c = 'c',sleep = 1):
    time.sleep(sleep)
    print(a,b,c)

func1('a','b','c',0)
func1('a','b','c',0.5)
func1('a','b','c',0.6)
func1('a','b','c',1)
30
vdrmrt

Ce code est très inexact

total= 0
for i in range(1000):
    start= time.clock()
    function()
    end= time.clock()
    total += end-start
time= total/1000

Ce code est moins inexact

start= time.clock()
for i in range(1000):
    function()
end= time.clock()
time= (end-start)/1000

Le très imprécis souffre de biais de mesure si la durée d'exécution de la fonction est proche de la précision de l'horloge. La plupart des temps mesurés sont simplement des nombres aléatoires entre 0 et quelques ticks de l'horloge.

Selon la charge de travail de votre système, le "temps" que vous observez à partir d'une seule fonction peut être entièrement un artefact de la planification du système d'exploitation et d'autres frais généraux incontrôlables.

La deuxième version (moins imprécise) a moins de biais de mesure. Si votre fonction est vraiment rapide, vous devrez peut-être l'exécuter 10 000 fois pour atténuer la planification du système d'exploitation et d'autres frais généraux.

Les deux sont, bien sûr, terriblement trompeurs. Le temps d'exécution de votre programme - dans son ensemble - n'est pas la somme des temps d'exécution des fonctions. Vous ne pouvez utiliser les chiffres que pour des comparaisons relatives. Ce ne sont pas des mesures absolues qui ont beaucoup de sens.

23
S.Lott

Si vous voulez chronométrer une méthode python même si le bloc que vous mesurez peut lancer, une bonne approche consiste à utiliser l'instruction with. Définissez une classe Timer comme

import time

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self

    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

Ensuite, vous souhaiterez peut-être chronométrer une méthode de connexion qui peut lancer. Utilisation

import httplib

with Timer() as t:
    conn = httplib.HTTPConnection('google.com')
    conn.request('GET', '/')

print('Request took %.03f sec.' % t.interval)

La méthode __exit()__ sera appelée même si la demande de connexion le fait. Plus précisément, vous devez utiliser tryfinally pour voir le résultat au cas où il lancerait, comme avec

try:
    with Timer() as t:
        conn = httplib.HTTPConnection('google.com')
        conn.request('GET', '/')
finally:
    print('Request took %.03f sec.' % t.interval)

Plus de détails ici.

14
kiriloff

C'est plus propre

from contextlib import contextmanager

import time
@contextmanager
def timeblock(label):
    start = time.clock()
    try:
        yield
    finally:
        end = time.clock()
        print ('{} : {}'.format(label, end - start))



with timeblock("just a test"):
            print "yippee"
7
JasonEdinburgh

Semblable à la réponse de @ AlexMartelli

import timeit
timeit.timeit(fun, number=10000)

peut faire l'affaire.

4
Binu Jasim