it-swarm.com.de

So verwenden Sie das Timeit-Modul

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?

275
Neemaximo

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 :-)

230

Wenn Sie timeit in einer interaktiven Python-Sitzung verwenden möchten, gibt es zwei praktische Optionen:

  1. 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
    
  2. 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]
    
229
Sven Marnach

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.


Warnungen

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.

112
Veedrac

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)
76
zzart

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()'
41
unutbu

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)
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

Das funktioniert großartig: 

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

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

3
Stryker

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()
0
Yagmur SAHIN

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.

 enter image description here 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")
0
grepit

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
0
ChaimG

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                                                                                        
0
Vlad Bezden