Antworten:
Die Funktionsweise von timeit besteht darin, den Setup-Code einmal auszuführen und dann eine Reihe von Anweisungen wiederholt aufzurufen . Wenn Sie also die Sortierung testen möchten, ist einige Sorgfalt erforderlich, damit ein Durchgang bei einer direkten Sortierung den nächsten Durchgang mit bereits sortierten Daten nicht beeinflusst (dies würde den Timsort natürlich wirklich zum Leuchten bringen, da er die beste Leistung erbringt wenn die Daten bereits teilweise bestellt sind).
Hier ist ein Beispiel für das Einrichten eines Sortiertests:
>>> 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 Anweisungsreihe bei jedem Durchgang eine neue Kopie der unsortierten Daten erstellt.
Beachten Sie auch die Timing-Technik, bei der die Messsuite sieben Mal ausgeführt wird und nur die beste Zeit eingehalten wird. Dies kann wirklich dazu beitragen, Messverzerrungen aufgrund anderer Prozesse auf Ihrem System zu reduzieren.
Das sind meine Tipps für den richtigen Umgang mit timeit. Hoffe das hilft :-)
.repeat(7,1000)
bereits tun (indem Sie den gleichen Samen verwenden)! Ihre Lösung ist also IMO perfekt.
.repeat(7, 1000)
vs. .repeat(2, 3500)
vs .repeat(35, 200
) davon abhängen sollte, wie der Fehler aufgrund der Systemlast mit dem Fehler aufgrund der Eingabevariabilität verglichen wird. Im Extremfall, wenn Ihr System immer stark ausgelastet ist und Sie links von der Verteilung der Ausführungszeit einen langen, dünnen Schwanz sehen (wenn Sie es in einem seltenen Leerlaufzustand abfangen), sind Sie möglicherweise sogar .repeat(7000,1)
nützlicher als .repeat(7,1000)
wenn Sie kann nicht mehr als 7000 Läufe budgetieren.
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 praktische %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__
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]
from __main__ import f
Technik. Ich denke nicht, dass dies so bekannt ist, wie es sein sollte. Dies ist in solchen Fällen hilfreich, in denen ein Funktions- oder Methodenaufruf zeitgesteuert wird. In anderen Fällen (zeitliche Abstimmung einer Reihe von Schritten) ist dies weniger hilfreich, da dadurch der Funktionsaufruf-Overhead eingeführt wird.
%timeit f(x)
sys._getframe(N).f_globals
) von Anfang an die Standardeinstellung gewesen sein.
Ich werde Ihnen ein Geheimnis verraten: Der beste Weg, dies zu tun, timeit
ist die Befehlszeile.
Führt in der Befehlszeile eine timeit
ordnungsgemäße statistische Analyse durch: Hier erfahren Sie, wie lange der kürzeste Lauf gedauert hat. Dies ist gut, da alle Zeitfehler positiv sind. Die kürzeste Zeit hat also den geringsten Fehler. Es gibt keine Möglichkeit, einen negativen Fehler zu erhalten, da ein Computer niemals schneller rechnen kann als er!
Also die Kommandozeilenschnittstelle:
%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
Das ist ganz einfach, oder?
Sie können Folgendes einrichten:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
das ist auch nützlich!
Wenn Sie mehrere Zeilen möchten, können Sie entweder die automatische Fortsetzung der Shell verwenden 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 Zeiten
sum(x)
min(y)
Wenn Sie längere Skripte haben möchten, könnten Sie versucht sein, in timeit
ein Python-Skript zu wechseln . Ich schlage vor, dies zu vermeiden, da die Analyse und das Timing in der Befehlszeile einfach besser sind. Stattdessen neige ich dazu, Shell-Skripte zu erstellen:
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.
Aber was , wenn Sie möchten , verwenden timeit
in Ihrem Modul?
Nun, der einfache Weg ist:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
und das gibt Ihnen kumulative ( nicht minimale!) Zeit, um diese Anzahl von Malen auszuführen.
Verwenden Sie, um eine gute Analyse zu erhalten .repeat
und nehmen Sie das Minimum, :
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Normalerweise sollten Sie dies mit kombinieren, functools.partial
anstatt lambda: ...
den Overhead zu senken. So könnte man so etwas haben wie:
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:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
das würde Ihnen etwas näher an der Schnittstelle geben von der Kommandozeile geben, aber auf eine viel weniger coole Art und Weise. Mit "from __main__ import ..."
können Sie Code aus Ihrem Hauptmodul in der von erstellten künstlichen Umgebung verwenden timeit
.
Es ist erwähnenswert, dass dies ein Convenience-Wrapper für ist Timer(...).timeit(...)
und daher nicht besonders gut im Timing ist. Ich persönlich bevorzuge es bei weitem, Timer(...).repeat(...)
wie ich oben gezeigt habe.
Es gibt ein paar Einschränkungen timeit
, die überall gelten.
Gemeinkosten werden nicht berücksichtigt. Angenommen, Sie möchten Zeit 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, reinen Overhead zu finden:
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
Das sind gut 30% Overhead, nur vom Timing! Dies kann die relativen Timings massiv verzerren. Aber Sie haben sich nur wirklich um das Hinzufügen von Timings gekümmert ; Die Nachschlagezeiten für müssen x
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 er 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. Berücksichtigen Sie dies separat.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Beachten Sie, dass das Subtrahieren des Overheads hier nur deshalb sinnvoll ist der Overhead nur einen kleinen Bruchteil der Zeit ausmacht.
In Ihrem Beispiel ist anzumerken, dass sowohl Insertion Sort als auch Tim Sort ein völlig ungewöhnliches Timing-Verhalten für bereits sortierte Listen aufweisen. Dies bedeutet, dass Sie eine random.shuffle
Zwischensortierung benötigen, wenn Sie vermeiden möchten, dass Ihre Timings zerstört werden.
timeit
aus einem Programm zu verwenden, aber genauso wie die Befehlszeile zu funktionieren? .
timeit
wird eine pass
Anweisung ausgeführt, wenn keine Argumente angegeben werden, was natürlich einige Zeit in Anspruch nimmt. Wenn Argumente angegeben werden, pass
werden diese nicht ausgeführt, daher 0.014
wäre es falsch , einige Usecs von jedem Timing zu subtrahieren .
Ich finde, die einfachste Möglichkeit, timeit zu verwenden, ist über die Befehlszeile:
Gegeben test.py :
def InsertionSort(): ...
def TimSort(): ...
Führen Sie die Zeit so aus:
% 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 super:
python -m timeit -c "$(cat file_name.py)"
Lassen Sie uns in jedem der folgenden Wörter das gleiche Wörterbuch einrichten und die Ausführungszeit testen.
Das Setup-Argument richtet im Grunde das Wörterbuch ein
Nummer ist, den Code 1000000 mal auszuführen. Nicht das Setup, sondern das stmt
Wenn Sie dies ausführen, können Sie sehen, 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 abzurufen.
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.
nach Index: 0,20900007452246427
von get: 0.54841166886888
Übergeben Sie einfach Ihren gesamten Code als Argument der Zeit:
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()
?
Das integrierte Timeit-Modul funktioniert am besten über die IPython-Befehlszeile.
So steuern Sie Funktionen 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 REPL-Interpreters 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
Sie würden zwei Funktionen erstellen und dann etwas Ähnliches ausführen. Beachten Sie, dass Sie die gleiche Anzahl von Ausführungen / Ausführungen auswählen möchten, um Apfel mit Apfel zu vergleichen.
Dies wurde unter Python 3.7 getestet.
Hier ist der Code zum einfachen 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 __name__ == '__main__':
import timeit
t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")
timsort(a)
und nehmen Sie den Unterschied :-)