Réponses:
La façon dont timeit fonctionne consiste à exécuter une fois le code d'installation, puis à effectuer des appels répétés à une série d'instructions. Donc, si vous voulez tester le tri, une certaine prudence est requise afin qu'un passage à un tri sur place n'affecte pas le prochain passage avec des données déjà triées (ce qui, bien sûr, ferait vraiment briller le Timsort car il fonctionne mieux lorsque les données sont déjà partiellement commandées).
Voici un exemple de configuration d'un test de 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 moment - cela peut vraiment aider à réduire les distorsions de mesure dues à d'autres processus en cours d'exécution sur votre système.
Ce sont mes conseils pour utiliser correctement timeit. J'espère que cela t'aides :-)
.repeat(7,1000)
déjà (en utilisant la même graine)! Votre solution est donc parfaite IMO.
.repeat(7, 1000)
vs .repeat(2, 3500)
vs .repeat(35, 200
) devrait dépendre de la façon dont l'erreur due à la charge du système se compare à l'erreur due à la variabilité des entrées. Dans le cas extrême, si votre système est toujours sous forte charge et que vous voyez une longue queue mince à gauche de la distribution du temps d'exécution (lorsque vous l'attrapez dans un état inactif rare), vous pourriez même trouver .repeat(7000,1)
plus utile que .repeat(7,1000)
si vous ne peut pas budgéter plus de 7 000 exécutions.
Si vous souhaitez utiliser timeit
dans une session Python interactive, il existe deux options pratiques:
Utilisez le shell IPython . Il dispose de la %timeit
fonction spéciale pratique :
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 autres noms que vous avez définis plus tôt au cours de la session interactive en les important __main__
dans l'instruction setup:
>>> 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]
from __main__ import f
technique. Je ne pense pas que ce soit aussi largement connu qu'il devrait l'être. Il est utile dans des cas comme celui-ci où un appel de fonction ou de méthode est synchronisé. Dans d'autres cas (chronométrage d'une série d'étapes), cela est moins utile car il introduit une surcharge d'appel de fonction.
%timeit f(x)
sys._getframe(N).f_globals
) aient été par défaut depuis le début.
Je vais vous révéler un secret: la meilleure façon de l'utiliser timeit
est en ligne de commande.
Sur la ligne de commande, timeit
fait une analyse statistique appropriée: il vous indique combien de temps a duré le plus court. C'est bien parce que toute erreur de synchronisation est positive. Ainsi, le temps le plus court contient le moins d'erreurs. 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!
Ainsi, 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 voulez plusieurs lignes, vous pouvez utiliser la continuation automatique du shell ou utiliser des arguments séparés:
%~> 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 fois
sum(x)
min(y)
Si vous souhaitez avoir des scripts plus longs, vous pourriez être tenté de passer à l' timeit
intérieur d'un script Python. Je suggère d'éviter cela parce que l'analyse et le timing sont tout simplement meilleurs sur la 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 un gros problème.
Mais que faire si vous souhaitez utiliser l' timeit
intérieur de votre module?
Eh bien, le moyen le plus simple est de faire:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
et cela vous donne un temps cumulé ( pas minimum!) pour 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 devez normalement combiner cela 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 également faire:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
ce qui vous donnerait quelque chose de plus proche de l' interface depuis la ligne de commande, mais d'une manière beaucoup moins cool. Le "from __main__ import ..."
vous permet d'utiliser le code de votre module principal à l'intérieur de l'environnement artificiel créé par timeit
.
Il convient de noter qu'il s'agit d'un emballage pratique Timer(...).timeit(...)
et qu'il n'est donc pas particulièrement bon pour le 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 timeit
qui tiennent partout.
Les frais généraux ne sont pas pris en compte. Dites que vous voulez chronométrer x += 1
, pour savoir combien de temps prend l'addition:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Eh bien, ce n'est pas 0,0476 µs. Vous savez seulement que c'est moins que ça. Toute erreur est positive.
Essayez donc de trouver des frais généraux purs :
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
C'est un bon 30% de frais généraux rien que du timing! Cela peut fausser considérablement les synchronisations relatives. Mais vous ne vous souciez que de l' ajout de timings; les horaires de recherche 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 elle 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 devrez 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. Tenez-en compte séparément.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Notez que la soustraction de la surcharge n'est raisonnable ici que parce que la surcharge est une petite fraction du temps.
Pour votre exemple, il convient de noter que le tri par insertion et le tri temporel ont tous deux des comportements temporels inhabituels pour les listes déjà triées. Cela signifie que vous aurez besoin d'un random.shuffle
tri entre si vous voulez éviter de perturber vos horaires.
timeit
partir d'un programme mais fonctionnant de la même manière que la ligne de commande? .
timeit
exécute une pass
instruction lorsque aucun argument n'est donné, ce qui, bien sûr, prend un certain temps. Si des arguments sont fournis, pass
ils ne seront pas exécutés, donc soustraire certains 0.014
usecs de chaque synchronisation serait incorrect.
Je trouve que le moyen le plus simple d'utiliser timeit est à partir de la ligne de commande:
Étant donné test.py :
def InsertionSort(): ...
def TimSort(): ...
exécutez 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 consiste essentiellement à configurer le dictionnaire
Le numéro est d'exécuter le code 1000000 fois. Pas la configuration mais le stmt
Lorsque vous exécutez cela, 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, les vôtres seront différents.
par indice: 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()
gc.enable()
?
Le module timeit intégré fonctionne mieux à partir de la ligne de commande IPython.
Pour chronométrer des fonctions à partir d'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 qui accepte des 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
Vous devez créer deux fonctions, puis exécuter quelque chose de similaire à ceci. Remarquez que vous souhaitez choisir le même nombre d'exécutions / exécutions pour comparer pomme à pomme.
Cela 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 __name__ == '__main__':
import timeit
t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")
timsort(a)
et prenez la différence :-)