Python-Zeitmessfunktion

121

Ich möchte eine Python-Funktion erstellen, um die in jeder Funktion verbrachte Zeit zu testen und ihren Namen mit ihrer Zeit zu drucken, wie ich den Funktionsnamen drucken kann und ob es eine andere Möglichkeit gibt, dies zu sagen

def measureTime(a):
    start = time.clock() 
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print "Time spent in (function name) is: ", elapsed
Wazery
quelle
Python-Profiling-Tools können Ihnen Funktionsnamen und die jeweils aufgewendete Zeit anzeigen. Lesen Sie hier: docs.python.org/library/profile.html
Roadmaster
Besser timeitfür die Messung verwenden. Es ist nicht perfekt, aber es schlägt Ihren Stich bei weitem und es ist viel einfacher zu bedienen, timeitals selbst etwas Besseres zu zaubern.

Antworten:

240

In erster Linie empfehle ich dringend, einen Profiler zu verwenden oder zumindest timeit zu verwenden .

Wenn Sie jedoch Ihre eigene Timing-Methode ausschließlich zum Lernen schreiben möchten, finden Sie hier einen Einstieg in die Verwendung eines Dekorateurs.

Python 2:

def timing(f):
    def wrap(*args):
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0)
        return ret
    return wrap

Und die Verwendung ist sehr einfach, verwenden Sie einfach den @ timing-Dekorator:

@timing
def do_work():
  #code

Python 3:

def timing(f):
    def wrap(*args, **kwargs):
        time1 = time.time()
        ret = f(*args, **kwargs)
        time2 = time.time()
        print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))

        return ret
    return wrap

Hinweis Ich rufe f.func_nameauf, um den Funktionsnamen als Zeichenfolge (in Python 2) oder f.__name__ in Python 3 abzurufen.

Mike Lewis
quelle
4
genau das, was ich will :) ... aber ihr habt mich überzeugt, den Python-Profiler zu verwenden
Wazery
3
Sieht so aus, als würde davon ausgegangen, dass time.time () die Zeit in Mikrosekunden seit der Epoche angibt. In der Dokumentation wird angegeben, dass die Zeit in Sekunden angegeben wird . Docs.python.org/2/library/time.html#time.time .
Rahul Jha
Dies kann nicht wirksam werden, nachdem Yield in func verwendet wurde. Wie kann ich diese Methode noch anwenden und die Ausbeute verwenden?
Jiamo
def Timing (f): def wrap (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () print '% s Funktion hat% 0.3 benötigt f ms '% (f.func_name, (time2-time1) * 1000) return ret return wrap
Vivek Bagaria
1
Was ist der Nachteil, wenn Sie es selbst schreiben? Ist es nicht einfach genug, eine Liste der verstrichenen Zeiten zu speichern und ihre Verteilung zu untersuchen?
3pitt
51

Nach dem Spielen mit dem timeitModul gefällt mir die Benutzeroberfläche nicht, die im Vergleich zu den folgenden beiden Methoden nicht so elegant ist.

Der folgende Code befindet sich in Python 3.

Die Dekorationsmethode

Dies ist fast das gleiche mit @ Mikes Methode. Hier füge ich hinzu kwargsund functoolswickle es ein, um es besser zu machen.

def timeit(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
    return newfunc

@timeit
def foobar():
    mike = Person()
    mike.think(30)

Die Kontextmanager-Methode

from contextlib import contextmanager

@contextmanager
def timeit_context(name):
    startTime = time.time()
    yield
    elapsedTime = time.time() - startTime
    print('[{}] finished in {} ms'.format(name, int(elapsedTime * 1000)))

Zum Beispiel können Sie es wie folgt verwenden:

with timeit_context('My profiling code'):
    mike = Person()
    mike.think()

Und der Code innerhalb des withBlocks wird zeitlich festgelegt.

Fazit

Mit der ersten Methode können Sie den Dekorateur leicht auskommentieren, um den normalen Code zu erhalten. Es kann jedoch nur eine Funktion zeitlich festgelegt werden. Wenn Sie einen Teil des Codes haben, den Sie nicht als Funktion verwenden können, können Sie die zweite Methode auswählen.

Zum Beispiel haben Sie jetzt

images = get_images()
bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

Jetzt möchten Sie die bigImage = ...Linie zeitlich festlegen. Wenn Sie es in eine Funktion ändern, ist es:

images = get_images()
bitImage = None
@timeit
def foobar():
    nonlocal bigImage
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

Sieht nicht so gut aus ... Was ist, wenn Sie in Python 2 sind, das kein nonlocalSchlüsselwort hat?

Stattdessen passt die Verwendung der zweiten Methode hier sehr gut:

images = get_images()
with timeit_context('foobar'):
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)
Strahl
quelle
Interessanter Beitrag, aber ich finde es nutzlos, dass Sie bei der von Ihnen erwähnten Dekorationsmethode die timeitSchnittstelle ändern und die wraps()Funktion des functoolsModuls verwenden mussten. Ich meine, all dieser zusätzliche Code ist nicht notwendig.
Billal Begueradj
1
Needsimport functools
Guillaume Chevalier
1
Beachten Sie, dass Ihr Dekorateur den Rückgabewert der ursprünglichen Funktion
Marc Van Daele
11

Ich sehe nicht, wo das Problem mit dem timeitModul liegt. Dies ist wahrscheinlich der einfachste Weg, dies zu tun.

import timeit
timeit.timeit(a, number=1)

Es ist auch möglich, Argumente an die Funktionen zu senden. Alles, was Sie brauchen, ist, Ihre Funktion mit Dekorateuren abzuschließen. Weitere Erklärungen hier: http://www.pythoncentral.io/time-a-python-function/

Der einzige Fall, in dem Sie möglicherweise daran interessiert sind, Ihre eigenen Timing-Anweisungen zu schreiben, besteht darin, dass Sie eine Funktion nur einmal ausführen und auch ihren Rückgabewert erhalten möchten.

Der Vorteil der Verwendung des timeitModuls besteht darin, dass Sie die Anzahl der Ausführungen wiederholen können . Dies kann erforderlich sein, da andere Prozesse Ihre Timing-Genauigkeit beeinträchtigen können. Sie sollten es also mehrmals ausführen und sich den niedrigsten Wert ansehen.

Phani
quelle
3
Argumente mit Wrappern und Dekoratoren an die Funktion senden? Warum nicht timeit.timeit(lambda: func(a,b,c), number=1)? Ich benutze dies, wenn ich Tests an einer hypothetischen Lösung in einem Terminal durchführe.
Jack
10

Timeit hat zwei große Fehler: Es gibt nicht den Rückgabewert der Funktion zurück und verwendet eval, was die Übergabe von zusätzlichem Setup-Code für Importe erfordert. Dies löst beide Probleme einfach und elegant:

def timed(f):
  start = time.time()
  ret = f()
  elapsed = time.time() - start
  return ret, elapsed

timed(lambda: database.foo.execute('select count(*) from source.apachelog'))
(<sqlalchemy.engine.result.ResultProxy object at 0x7fd6c20fc690>, 4.07547402381897)
Jonathan Ray
quelle
Vielen Dank! timeit funktioniert nicht gut mit Apache Spark, weil Sie alle Spark-Abhängigkeiten importieren müssen, und wer möchte einen großen alten String erstellen, der das tut? Diese Lösung ist viel einfacher und flexibler.
Paul
4

Es gibt ein einfaches Werkzeug für das Timing. https://github.com/RalphMao/PyTimer

Es kann wie ein Dekorateur arbeiten :

from pytimer import Timer
@Timer(average=False)      
def matmul(a,b, times=100):
    for i in range(times):
        np.dot(a,b)        

Ausgabe:

matmul:0.368434
matmul:2.839355

Es kann auch wie ein Plug-In-Timer mit Namespace-Steuerung funktionieren (hilfreich, wenn Sie ihn in eine Funktion einfügen, die viele Codes enthält und an einer anderen Stelle aufgerufen werden kann).

timer = Timer()                                           
def any_function():                                       
    timer.start()                                         

    for i in range(10):                                   

        timer.reset()                                     
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block1')                        

        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block2')                        
        np.dot(np.ones((100,1000)), np.zeros((1000,1000)))

    for j in range(20):                                   
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
    timer.summary()                                       

for i in range(2):                                        
    any_function()                                        

Ausgabe:

========Timing Summary of Default Timer========
block2:0.065062
block1:0.032529
========Timing Summary of Default Timer========
block2:0.065838
block1:0.032891

Hoffe es wird helfen

ralphmao95
quelle
3

Decorator-Methode mit der Decorator-Python-Bibliothek:

import decorator

@decorator
def timing(func, *args, **kwargs):
    '''Function timing wrapper
        Example of using:
        ``@timing()``
    '''

    fn = '%s.%s' % (func.__module__, func.__name__)

    timer = Timer()
    with timer:
        ret = func(*args, **kwargs)

    log.info(u'%s - %0.3f sec' % (fn, timer.duration_in_seconds()))
    return ret

Siehe Beitrag in meinem Blog:

Beitrag auf mobilepro.pl Blog

Mein Beitrag bei Google Plus

MobilePro.pl
quelle
1

Meine Art es zu tun:

from time import time

def printTime(start):
    end = time()
    duration = end - start
    if duration < 60:
        return "used: " + str(round(duration, 2)) + "s."
    else:
        mins = int(duration / 60)
        secs = round(duration % 60, 2)
        if mins < 60:
            return "used: " + str(mins) + "m " + str(secs) + "s."
        else:
            hours = int(duration / 3600)
            mins = mins % 60
            return "used: " + str(hours) + "h " + str(mins) + "m " + str(secs) + "s."

Setzen Sie eine Variable wie start = time()zuvor, führen Sie die Funktion / Schleifen aus und printTime(start)direkt nach dem Block.

und du hast die Antwort.

Adam Liu
quelle