web-dev-qa-db-fra.com

Comment utiliser le module timeit

Je comprends le concept de ce que timeit fait mais je ne sais pas comment le mettre en œuvre dans mon code.

Comment comparer deux fonctions, par exemple insertion_sort et tim_sort, avec timeit?

275
Neemaximo

Le fonctionnement de timeit consiste à exécuter le code d'installation une fois, puis à faire des appels répétés à une série d'instructions. Donc, si vous voulez tester le tri, vous devez faire attention à ce qu'un passage à un tri sur place n'affecte pas le passage suivant avec des données déjà triées (bien entendu, cela ferait vraiment briller le Timsort car il fonctionne mieux lorsque les données sont déjà partiellement ordonnées).

Voici un exemple de configuration d'un test pour le tri:

>>> import timeit

>>> setup = '''
import random

random.seed('slartibartfast')
s = [random.random() for i in range(1000)]
timsort = list.sort
'''

>>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000))
0.334147930145

Notez que la série d'instructions crée une nouvelle copie des données non triées à chaque passage.

Notez également la technique de synchronisation consistant à exécuter la suite de mesures sept fois et à ne conserver que le meilleur temps. Cela peut réellement aider à réduire les distorsions de mesure dues aux autres processus en cours d'exécution sur votre système.

Ce sont mes conseils pour utiliser timeit correctement. J'espère que cela t'aides :-)

230
Raymond Hettinger

Si vous souhaitez utiliser timeit dans une session Python interactive, deux options pratiques vous sont proposées:

  1. Utilisez le IPython Shell. Il comporte la fonction spéciale pratique %timeit:

    In [1]: def f(x):
       ...:     return x*x
       ...: 
    
    In [2]: %timeit for x in range(100): f(x)
    100000 loops, best of 3: 20.3 us per loop
    
  2. Dans un interpréteur Python standard, vous pouvez accéder aux fonctions et aux autres noms que vous avez définis précédemment au cours de la session interactive en les important depuis __main__ dans l'instruction d'installation:

    >>> def f(x):
    ...     return x * x 
    ... 
    >>> import timeit
    >>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f",
                      number=100000)
    [2.0640320777893066, 2.0876040458679199, 2.0520210266113281]
    
229
Sven Marnach

Je vais vous révéler un secret: la meilleure façon d'utiliser timeit est en ligne de commande.

Sur la ligne de commande, timeit effectue une analyse statistique correcte: elle vous indique le temps nécessaire à l'exécution la plus courte. C'est bien parce que tous l'erreur de chronométrage est positive. Donc, le temps le plus court a la moindre erreur. Il n'y a aucun moyen d'obtenir une erreur négative, car un ordinateur ne peut jamais calculer plus vite qu'il ne peut le faire!

Donc, l'interface de ligne de commande:

%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop

C'est assez simple, hein?

Vous pouvez configurer des choses:

%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop

ce qui est utile aussi!

Si vous souhaitez plusieurs lignes, vous pouvez utiliser la continuation automatique du shell ou utiliser des arguments distincts:

%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop

Cela donne une configuration de

x = range(1000)
y = range(100)

et les temps

sum(x)
min(y)

Si vous souhaitez avoir des scripts plus longs, vous pourriez être tenté de passer à timeit dans un script Python. Je suggère d'éviter cela parce que l'analyse et le timing sont simplement meilleurs en ligne de commande. Au lieu de cela, j'ai tendance à faire des scripts Shell:

 SETUP="

 ... # lots of stuff

 "

 echo Minmod arr1
 python -m timeit -s "$SETUP" "Minmod(arr1)"

 echo pure_minmod arr1
 python -m timeit -s "$SETUP" "pure_minmod(arr1)"

 echo better_minmod arr1
 python -m timeit -s "$SETUP" "better_minmod(arr1)"

 ... etc

Cela peut prendre un peu plus de temps en raison des multiples initialisations, mais normalement, ce n'est pas grave.


Mais que se passe-t-il si vous voulez _ utiliser timeit dans votre module?

Eh bien, le moyen le plus simple est de faire:

def function(...):
    ...

timeit.Timer(function).timeit(number=NUMBER)

et cela vous donne le temps cumulé (pas minimum!) d'exécuter ce nombre de fois.

Pour obtenir une bonne analyse, utilisez .repeat et prenez le minimum:

min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))

Vous devriez normalement combiner ceci avec functools.partial au lieu de lambda: ... pour réduire les frais généraux. Ainsi, vous pourriez avoir quelque chose comme:

from functools import partial

def to_time(items):
    ...

test_items = [1, 2, 3] * 100
times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000)

# Divide by the number of repeats
time_taken = min(times) / 1000

Vous pouvez aussi faire:

timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)

ce qui vous donnerait quelque chose de plus proche de la interface de la ligne de commande, mais de manière beaucoup moins cool. Le "from __main__ import ..." vous permet d'utiliser le code de votre module principal dans l'environnement artificiel créé par timeit.

Il convient de noter qu'il s'agit d'un wrapper de commodité pour Timer(...).timeit(...) et qu'il n'est donc pas particulièrement bon en termes de timing. Personnellement, je préfère de loin utiliser Timer(...).repeat(...) comme je l’ai montré ci-dessus.


Avertissements

Il y a quelques mises en garde avec timeit qui tiennent partout.

  • Les frais généraux ne sont pas comptabilisés. Supposons que vous souhaitiez enregistrer x += 1 pour savoir combien de temps dure l'ajout:

    >>> python -m timeit -s "x = 0" "x += 1"
    10000000 loops, best of 3: 0.0476 usec per loop
    

    Eh bien, c’est pas 0,0476 µs. Vous savez seulement que c'est moins que cela. Toute erreur est positive.

    Alors essayez de trouver pure overhead:

    >>> python -m timeit -s "x = 0" ""      
    100000000 loops, best of 3: 0.014 usec per loop
    

    C'est un bon 30% frais généraux juste du timing! Cela peut fausser considérablement les timings relatifs. Mais vous ne vous souciez que des ajouter timings; les horaires de recherche pour x doivent également être inclus dans les frais généraux:

    >>> python -m timeit -s "x = 0" "x"
    100000000 loops, best of 3: 0.0166 usec per loop
    

    La différence n'est pas beaucoup plus grande, mais c'est là.

  • Les méthodes de mutation sont dangereuses.

    >>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
    10000000 loops, best of 3: 0.0436 usec per loop
    

    Mais c'est complètement faux!x est la liste vide après la première itération. Vous aurez besoin de réinitialiser:

    >>> python -m timeit "x = [0]*100000" "while x: x.pop()"
    100 loops, best of 3: 9.79 msec per loop
    

    Mais alors vous avez beaucoup de frais généraux. Compte pour cela séparément.

    >>> python -m timeit "x = [0]*100000"                   
    1000 loops, best of 3: 261 usec per loop
    

    Notez que soustraire le temps système est raisonnable ici uniquement parce que le temps système est une fraction infime du temps.

    Pour votre exemple, il convient de noter que les deux Tri d'insertion et Tri horaire ont complètement inhabituel comportements de synchronisation pour les listes déjà triées. Cela signifie que vous aurez besoin d'un random.shuffle entre les tris si vous voulez éviter de gâcher vos timings.

112
Veedrac

Si vous voulez comparer rapidement deux blocs de code/fonctions, vous pouvez faire:

import timeit

start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func2()
print(timeit.default_timer() - start_time)
76
zzart

Je trouve le moyen le plus simple d'utiliser timeit depuis la ligne de commande:

Étant donné test.py :

def InsertionSort(): ...
def TimSort(): ...

run timeit comme ceci:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'
41
unutbu

pour moi, c'est le moyen le plus rapide:

import timeit
def foo():
    print("here is my code to time...")


timeit.timeit(stmt=foo, number=1234567)
10
Rodrigo Laguna
# Генерация целых чисел

def gen_prime(x):
    multiples = []
    results = []
    for i in range(2, x+1):
        if i not in multiples:
            results.append(i)
            for j in range(i*i, x+1, i):
                multiples.append(j)

    return results


import timeit

# Засекаем время

start_time = timeit.default_timer()
gen_prime(3000)
print(timeit.default_timer() - start_time)

# start_time = timeit.default_timer()
# gen_prime(1001)
# print(timeit.default_timer() - start_time)
9
David Webb

Cela fonctionne très bien: 

  python -m timeit -c "$(cat file_name.py)"
7
Ohad Rubin

permet de configurer le même dictionnaire dans chacun des éléments suivants et de tester le temps d'exécution.

L'argument de configuration est essentiellement la configuration du dictionnaire

Nombre est d'exécuter le code 1000000 fois. Pas la configuration mais la stmt

Lorsque vous exécutez ceci, vous pouvez voir que l'index est bien plus rapide que get. Vous pouvez l'exécuter plusieurs fois pour voir.

Le code essaie essentiellement d’obtenir la valeur de c dans le dictionnaire.

import timeit

print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))
print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))

Voici mes résultats, le vôtre différera.

par index: 0.20900007452246427

par get: 0.54841166886888

3
Stryker

passez simplement votre code entier comme argument de timeit:

import timeit

print(timeit.timeit("""

limit = 10000
prime_list = [i for i in range(2, limit+1)]

for prime in prime_list:
    for elem in range(prime*2, max(prime_list)+1, prime):
        if elem in prime_list:
            prime_list.remove(elem)"""

, number=10))
2
import timeit


def oct(x):
   return x*x


timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()
0
Yagmur SAHIN

Vous créez deux fonctions puis exécutez quelque chose de similaire à ceci .. Remarque: vous souhaitez choisir le même nombre d'exécution/exécution pour comparer Apple à Apple.
Ceci a été testé sous Python 3.7.

 enter image description here Voici le code pour le copier facilement 

!/usr/local/bin/python3
import timeit

def fibonacci(n):
    """
    Returns the n-th Fibonacci number.
    """
    if(n == 0):
        result = 0
    Elif(n == 1):
        result = 1
    else:
        result = fibonacci(n-1) + fibonacci(n-2)
    return result

if __== '__main__':
    import timeit
    t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
    print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")
0
grepit

Le module timeit intégré fonctionne mieux à partir de la ligne de commande IPython.

Pour chronométrer des fonctions depuis un module:

from timeit import default_timer as timer
import sys

def timefunc(func, *args, **kwargs):
    """Time a function. 

    args:
        iterations=3

    Usage example:
        timeit(myfunc, 1, b=2)
    """
    try:
        iterations = kwargs.pop('iterations')
    except KeyError:
        iterations = 3
    elapsed = sys.maxsize
    for _ in range(iterations):
        start = timer()
        result = func(*args, **kwargs)
        elapsed = min(timer() - start, elapsed)
    print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed)))
    return result
0
ChaimG

Exemple d'utilisation de l'interpréteur Python REPL avec une fonction acceptant les paramètres.

>>> import timeit                                                                                         

>>> def naive_func(x):                                                                                    
...     a = 0                                                                                             
...     for i in range(a):                                                                                
...         a += i                                                                                        
...     return a                                                                                          

>>> def wrapper(func, *args, **kwargs):                                                                   
...     def wrapper():                                                                                    
...         return func(*args, **kwargs)                                                                  
...     return wrapper                                                                                    

>>> wrapped = wrapper(naive_func, 1_000)                                                                  

>>> timeit.timeit(wrapped, number=1_000_000)                                                              
0.4458435332577161                                                                                        
0
Vlad Bezden