Ich verstehe das Konzept dessen, was timeit
tut, aber ich bin nicht sicher, wie ich es in meinen Code implementieren soll.
Wie kann ich zwei Funktionen, zB insertion_sort
und tim_sort
, mit timeit
vergleichen?
Die Funktionsweise von timeit besteht darin, einmal Setup-Code auszuführen und dann wiederholt eine Reihe von Anweisungen aufzurufen. Wenn Sie die Sortierung testen möchten, ist einige Sorgfalt geboten, damit ein Durchlauf bei einer In-Place-Sortierung den nächsten Durchlauf mit bereits sortierten Daten nicht beeinflusst (dies würde natürlich den Timsort wirklich zum Leuchten bringen weil es am besten funktioniert, wenn die Daten bereits teilweise geordnet sind).
Hier ein Beispiel, wie Sie einen Test für die Sortierung einrichten:
>>> 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
Beachten Sie, dass die Reihe von Anweisungen bei jedem Durchlauf eine neue Kopie der unsortierten Daten erstellt.
Beachten Sie auch das Timing-Verfahren, bei dem Sie die Messreihe sieben Mal ausführen und nur die beste Zeit einhalten. Dies kann wirklich dazu beitragen, Messverzerrungen aufgrund anderer Prozesse in Ihrem System zu reduzieren.
Das sind meine Tipps für die korrekte Verwendung von Timeit. Hoffe das hilft :-)
Wenn Sie timeit
in einer interaktiven Python-Sitzung verwenden möchten, gibt es zwei praktische Optionen:
Verwenden Sie die IPython Shell. Es verfügt über die komfortable %timeit
-Sonderfunktion:
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
In einem Standard-Python-Interpreter können Sie auf Funktionen und andere Namen zugreifen, die Sie zuvor während der interaktiven Sitzung definiert haben, indem Sie sie aus __main__
in der Setup-Anweisung importieren:
>>> 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]
Ich gebe Ihnen ein Geheimnis: Die beste Möglichkeit, timeit
zu verwenden, ist die Befehlszeile.
In der Befehlszeile führt timeit
eine ordnungsgemäße statistische Analyse durch: Sie sagt Ihnen, wie lange der kürzeste Lauf gedauert hat. Das ist gut, weil alle Fehler im Timing positiv sind. Die kürzeste Zeit hat also den geringsten Fehler. Es gibt keine Möglichkeit, negative Fehler zu erhalten, da ein Computer niemals schneller rechnen kann als er rechnen kann!
Also die Befehlszeilenschnittstelle:
%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
Das ist ganz einfach, oder?
Sie können Sachen einrichten:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
was auch nützlich ist!
Wenn Sie mehrere Zeilen haben möchten, können Sie entweder die automatische Fortsetzung der Shell oder separate Argumente verwenden:
%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop
Das gibt ein Setup von
x = range(1000)
y = range(100)
und mal
sum(x)
min(y)
Wenn Sie längere Skripts wünschen, könnten Sie versucht sein, innerhalb eines Python-Skripts nach timeit
zu wechseln. Ich schlage vor, das zu vermeiden, da die Analyse und das Timing auf der Kommandozeile einfach besser sind. Ich mache eher Shell-Skripte:
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
Dies kann aufgrund der mehrfachen Initialisierungen etwas länger dauern, aber normalerweise ist das keine große Sache.
Was aber, wenn Sie möchten in Ihrem Modul timeit
verwenden möchten?
Nun, der einfache Weg ist zu tun:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
und das gibt Ihnen kumulative (nicht minimum!) Zeit, um diese Anzahl von Malen auszuführen.
Um eine gute Analyse zu erhalten, verwenden Sie .repeat
und nehmen Sie das Minimum vor:
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Sie sollten dies normalerweise mit functools.partial
anstelle von lambda: ...
kombinieren, um den Overhead zu senken. So könnte man so etwas haben:
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
Sie können auch tun:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
dies würde Ihnen etwas näher an der Schnittstelle von der Befehlszeile geben, aber auf viel weniger coole Weise. Mit "from __main__ import ..."
können Sie Code aus Ihrem Hauptmodul in der von timeit
erstellten künstlichen Umgebung verwenden.
Es ist erwähnenswert, dass dies ein praktischer Wrapper für Timer(...).timeit(...)
ist und daher nicht besonders gut zum Timing geeignet ist. Ich persönlich bevorzuge die Verwendung von Timer(...).repeat(...)
, wie ich oben gezeigt habe.
Es gibt einige Vorbehalte mit timeit
, die überall gelten.
Gemeinkosten werden nicht berücksichtigt. Angenommen, Sie möchten zeitlich x += 1
, um herauszufinden, wie lange das Hinzufügen dauert:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Nun, es ist nicht 0,0476 µs. Sie wissen nur, dass es weniger ist. Alle Fehler sind positiv.
Versuchen Sie also, pure Overhead zu finden:
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
Das ist ein guter 30% Overhead, nur vom Timing aus! Dies kann relative Timings massiv verzerren. Aber Sie haben sich wirklich nur um die Hinzufügen-Timings gekümmert. Die Nachschlagzeiten für x
müssen ebenfalls im Overhead enthalten sein:
>>> python -m timeit -s "x = 0" "x"
100000000 loops, best of 3: 0.0166 usec per loop
Der Unterschied ist nicht viel größer, aber es ist da.
Mutationsmethoden sind gefährlich.
>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop
Aber das ist völlig falsch!x
ist die leere Liste nach der ersten Iteration. Sie müssen neu initialisieren:
>>> python -m timeit "x = [0]*100000" "while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop
Aber dann haben Sie viel Aufwand. Konto dafür separat.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Beachten Sie, dass das Subtrahieren des Overheads hier sinnvoll ist nur weil der Overhead ist ein kleiner Bruchteil der Zeit.
Für Ihr Beispiel ist es erwähnenswert, dass beide Insertion Sort und Tim Sort völlig ungewöhnlich Timing-Verhalten für bereits sortierte Listen haben. Das bedeutet, dass Sie zwischen den Sortierungen einen random.shuffle
benötigen, wenn Sie Ihre Timings nicht beschädigen möchten.
Wenn Sie zwei Codeblöcke/Funktionen schnell vergleichen möchten, können Sie Folgendes tun:
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)
Die einfachste Möglichkeit, timeit zu verwenden, ist die Befehlszeile:
Gegeben test.py:
def InsertionSort(): ...
def TimSort(): ...
laufzeit wie folgt ausführen:
% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'
für mich ist dies der schnellste Weg:
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)
Das funktioniert großartig:
python -m timeit -c "$(cat file_name.py)"
können Sie dasselbe Wörterbuch in jedem der folgenden Bereiche einrichten und die Ausführungszeit testen.
Das Setup-Argument besteht im Wesentlichen darin, das Wörterbuch einzurichten
Nummer soll den Code 1000000 mal ausführen. Nicht das Setup, sondern die stmt
Wenn Sie dies ausführen, können Sie feststellen, dass der Index viel schneller ist als get. Sie können es mehrmals ausführen, um zu sehen.
Der Code versucht grundsätzlich, den Wert von c im Wörterbuch zu ermitteln.
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))
Hier sind meine Ergebnisse, Ihre werden sich unterscheiden.
index: 0.20900007452246427
von get: 0.54841166886888
geben Sie einfach Ihren gesamten Code als Argument der Zeit ein:
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()
Sie würden zwei Funktionen erstellen und dann etwas Ähnliches ausführen. Beachten Sie, dass Sie die gleiche Anzahl von Ausführungen wählen möchten, um Apple mit Apple zu vergleichen.
Dies wurde unter Python 3.7 getestet.
Hier ist der Code zum leichteren Kopieren
!/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")
Das integrierte Timeit-Modul funktioniert am besten über die IPython-Befehlszeile.
Zeitfunktionen innerhalb eines Moduls:
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
Beispiel für die Verwendung des Python-Interpreters REPL mit einer Funktion, die Parameter akzeptiert.
>>> 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