100 votes

timeit versus décorateur de chronométrage

J'essaie de chronométrer un code. J'ai d'abord utilisé un décorateur de chronométrage :

#!/usr/bin/env python

import time
from itertools import izip
from random import shuffle

def timing_val(func):
    def wrapper(*arg, **kw):
        '''source: http://www.daniweb.com/code/snippet368.html'''
        t1 = time.time()
        res = func(*arg, **kw)
        t2 = time.time()
        return (t2 - t1), res, func.__name__
    return wrapper

@timing_val
def time_izip(alist, n):
    i = iter(alist)
    return [x for x in izip(*[i] * n)]

@timing_val
def time_indexing(alist, n):
    return [alist[i:i + n] for i in range(0, len(alist), n)]

func_list = [locals()[key] for key in locals().keys()
             if callable(locals()[key]) and key.startswith('time')]
shuffle(func_list)  # Shuffle, just in case the order matters

alist = range(1000000)
times = []
for f in func_list:
    times.append(f(alist, 31))

times.sort(key=lambda x: x[0])
for (time, result, func_name) in times:
    print '%s took %0.3fms.' % (func_name, time * 1000.)

donne

% test.py
time_indexing took 73.230ms.
time_izip took 122.057ms.

Et ici j'utilise timeit :

%  python - m timeit - s '' 'alist=range(1000000);[alist[i:i+31] for i in range(0, len(alist), 31)]'
10 loops, best of 3:
    64 msec per loop
% python - m timeit - s 'from itertools import izip' 'alist=range(1000000);i=iter(alist);[x for x in izip(*[i]*31)]'
10 loops, best of 3:
    66.5 msec per loop

En utilisant timeit, les résultats sont pratiquement les mêmes, mais en utilisant le décorateur de synchronisation, il apparaît que time_indexing est plus rapide que time_izip .

Qu'est-ce qui explique cette différence ?

Faut-il croire l'une ou l'autre de ces méthodes ?

Si oui, lesquels ?

139voto

jonaprieto Points 725

Utilisez l'emballage de functools pour améliorer la réponse de Matt Alcock.

from functools import wraps
from time import time

def timing(f):
    @wraps(f)
    def wrap(*args, **kw):
        ts = time()
        result = f(*args, **kw)
        te = time()
        print 'func:%r args:[%r, %r] took: %2.4f sec' % \
          (f.__name__, args, kw, te-ts)
        return result
    return wrap

Dans un exemple :

@timing
def f(a):
    for _ in range(a):
        i = 0
    return -1

Méthode d'appel f enveloppé de @timing :

func:'f' args:[(100000000,), {}] took: 14.2240 sec
f(100000000)

L'avantage de cette méthode est qu'elle préserve les attributs de la fonction d'origine, c'est-à-dire que les métadonnées telles que le nom de la fonction et la chaîne documentaire sont correctement conservées sur la fonction renvoyée.

48voto

Matt Alcock Points 1913

J'utiliserais un décorateur de timing, car vous pouvez utiliser des annotations pour saupoudrer le timing dans votre code plutôt que de rendre votre code désordonné avec une logique de timing.

import time

def timeit(f):

    def timed(*args, **kw):

        ts = time.time()
        result = f(*args, **kw)
        te = time.time()

        print 'func:%r args:[%r, %r] took: %2.4f sec' % \
          (f.__name__, args, kw, te-ts)
        return result

    return timed

L'utilisation du décorateur est simple, soit utiliser des annotations.

@timeit
def compute_magic(n):
     #function definition
     #....

Ou réaliaser la fonction que vous voulez chronométrer.

compute_magic = timeit(compute_magic)

24voto

Jochen Ritzel Points 42916

Utilisez timeit. Exécuter le test plus d'une fois me donne de bien meilleurs résultats.

func_list=[locals()[key] for key in locals().keys() 
           if callable(locals()[key]) and key.startswith('time')]

alist=range(1000000)
times=[]
for f in func_list:
    n = 10
    times.append( min(  t for t,_,_ in (f(alist,31) for i in range(n)))) 

for (time,func_name) in zip(times, func_list):
    print '%s took %0.3fms.' % (func_name, time*1000.)

->

<function wrapper at 0x01FCB5F0> took 39.000ms.
<function wrapper at 0x01FCB670> took 41.000ms.

8voto

newbee Points 61

Inspiré par la réponse de Micah Smith, j'ai fait en sorte que funcy imprime directement à la place (et n'utilise pas le module de journalisation).

Ce qui suit est pratique pour une utilisation au google colab.

# pip install funcy
from funcy import print_durations

@print_durations()
def myfunc(n=0):
  for i in range(n):
    pass

myfunc(123)
myfunc(123456789)

# 5.48 mks in myfunc(123)
# 3.37 s in myfunc(123456789)

6voto

denis Points 7316

J'en ai eu assez de from __main__ import foo maintenant utiliser ceci -- pour les args simples, pour lesquels %r fonctionne, et pas dans Ipython.
(Pourquoi timeit ne fonctionne que sur les chaînes de caractères, pas sur les thunks / fermetures, par exemple timefunc( f, arbitrary args ) ?)

import timeit

def timef( funcname, *args, **kwargs ):
    """ timeit a func with args, e.g.
            for window in ( 3, 31, 63, 127, 255 ):
                timef( "filter", window, 0 )
    This doesn't work in ipython;
    see Martelli, "ipython plays weird tricks with __main__" in Stackoverflow        
    """
    argstr = ", ".join([ "%r" % a for a in args]) if args  else ""
    kwargstr = ", ".join([ "%s=%r" % (k,v) for k,v in kwargs.items()]) \
        if kwargs  else ""
    comma = ", " if (argstr and kwargstr)  else ""
    fargs = "%s(%s%s%s)" % (funcname, argstr, comma, kwargstr)
        # print "test timef:", fargs
    t = timeit.Timer( fargs, "from __main__ import %s" % funcname )
    ntime = 3
    print "%.0f usec %s" % (t.timeit( ntime ) * 1e6 / ntime, fargs)

#...............................................................................
if __name__ == "__main__":
    def f( *args, **kwargs ):
        pass

    try:
        from __main__ import f
    except:
        print "ipython plays weird tricks with __main__, timef won't work"
    timef( "f")
    timef( "f", 1 )
    timef( "f", """ a b """ )
    timef( "f", 1, 2 )
    timef( "f", x=3 )
    timef( "f", x=3 )
    timef( "f", 1, 2, x=3, y=4 )

Ajouté : voir aussi "ipython joue des tours étranges avec principal ", Martelli dans exécuter-doctests-par-ipython

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X