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
?
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 :-)
Si vous souhaitez utiliser timeit
dans une session Python interactive, deux options pratiques vous sont proposées:
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
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]
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.
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.
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)
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()'
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)
# Генерация целых чисел
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)
Cela fonctionne très bien:
python -m timeit -c "$(cat file_name.py)"
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
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))
import timeit
def oct(x):
return x*x
timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()
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.
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")
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
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