Was ist das Python-Äquivalent zu Matlabs Tic- und Toc-Funktionen?


110

Was ist das Python-Äquivalent zu Matlabs Tic- und Toc-Funktionen ?


7
Wenn Sie wirklich das direkte Äquivalent wollen, rufen Sie einfach an tic = time.time()und toc = time.time()dann, print toc-tic, 'sec Elapsed'wie die Leute unten gesagt haben, timeitist es jedoch robuster.
Joe Kington

Ich scheine bessere Ergebnisse zu erzielen, wenn ich den Ansatz von @ JoeKington in Verbindung mit timeit.default_timer () verwende, wie zum Beispiel : tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer(), dann print toc-tic.
littleO

1
Die Bibliothek pytictoc scheint am konvektivsten zu sein, die Syntax ist sogar etwas ordentlicher als die unten stehende ttictoc. pypi.org/project/pytictoc
FlorianH

Antworten:


171

Abgesehen von dem, timeitwas ThiefMaster erwähnt hat, ist eine einfache Möglichkeit, dies zu tun, nur (nach dem Importieren time):

t = time.time()
# do stuff
elapsed = time.time() - t

Ich habe eine Hilfsklasse, die ich gerne benutze:

class Timer(object):
    def __init__(self, name=None):
        self.name = name

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        if self.name:
            print('[%s]' % self.name,)
        print('Elapsed: %s' % (time.time() - self.tstart))

Es kann als Kontextmanager verwendet werden:

with Timer('foo_stuff'):
   # do some foo
   # do some stuff

Manchmal finde ich diese Technik bequemer als timeit- alles hängt davon ab, was Sie messen möchten.


25
@eat: Ich bin respektvoll anderer Meinung. Die Leute haben den Unix- timeBefehl verwendet, um die Laufzeit von Programmen für immer zu messen, und diese Methode repliziert dies in Python-Code. Ich sehe nichts falsch daran, solange es das richtige Werkzeug für den Job ist. timeitist nicht immer so, und ein Profiler ist eine viel schwerere Lösung für die meisten Anforderungen
Eli Bendersky

4
Für die letzte Zeile würde ich vorschlagen print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'. Ohne% .2f ist es schwer zu verstehen. Danke für die tolle Idee.
Kann Kavaklıoğlu

4
Dies sieht auf den ersten Blick sehr praktisch aus, erfordert jedoch in der Praxis das Einrücken des gewünschten Codeblocks, was je nach Länge des Codeblocks und dem Editor Ihrer Wahl recht unpraktisch sein kann. Immer noch eine elegante Lösung, die sich bei verschachtelter Verwendung korrekt verhält.
Stefan

1
Ich denke du willst elapsed = t - time.time()statt elapsed = time.time() - t. In letzterem ist verstrichen negativ. Ich habe diese Änderung als Bearbeitung vorgeschlagen.
Rysqui

3
@rysqui - Ist die aktuelle Zeit nicht immer eine größere Zahl als die vorherige ? Ich würde denken, dass dies elapsed = time.time() - tdie Form ist, die immer einen positiven Wert ergibt.
Scott Smith

31

Ich hatte die gleiche Frage, als ich von Matlab nach Python migrierte. Mit Hilfe dieses Threads konnte ich ein genaues Analogon des Matlab tic()und seiner toc()Funktionen erstellen . Fügen Sie einfach den folgenden Code oben in Ihr Skript ein.

import time

def TicTocGenerator():
    # Generator that returns time differences
    ti = 0           # initial time
    tf = time.time() # final time
    while True:
        ti = tf
        tf = time.time()
        yield tf-ti # returns the time difference

TicToc = TicTocGenerator() # create an instance of the TicTocGen generator

# This will be the main function through which we define both tic() and toc()
def toc(tempBool=True):
    # Prints the time difference yielded by generator instance TicToc
    tempTimeInterval = next(TicToc)
    if tempBool:
        print( "Elapsed time: %f seconds.\n" %tempTimeInterval )

def tic():
    # Records a time in TicToc, marks the beginning of a time interval
    toc(False)

Das ist es! Jetzt sind wir voll einsatzbereit tic()und toc()genau wie in Matlab. Beispielsweise

tic()

time.sleep(5)

toc() # returns "Elapsed time: 5.00 seconds."

Tatsächlich ist dies vielseitiger als die integrierten Matlab-Funktionen. Hier können Sie eine weitere Instanz von erstellen TicTocGenerator, um mehrere Vorgänge zu verfolgen oder um die Dinge anders zu planen. Während wir beispielsweise ein Skript zeitlich festlegen, können wir jetzt jedes Teil des Skripts sowie das gesamte Skript separat zeitlich festlegen. (Ich werde ein konkretes Beispiel geben)

TicToc2 = TicTocGenerator() # create another instance of the TicTocGen generator

def toc2(tempBool=True):
    # Prints the time difference yielded by generator instance TicToc2
    tempTimeInterval = next(TicToc2)
    if tempBool:
    print( "Elapsed time 2: %f seconds.\n" %tempTimeInterval )

def tic2():
    # Records a time in TicToc2, marks the beginning of a time interval
    toc2(False)

Jetzt sollten Sie in der Lage sein, zwei verschiedene Dinge zeitlich zu steuern: Im folgenden Beispiel werden das gesamte Skript und Teile eines Skripts separat zeitlich festgelegt.

tic()

time.sleep(5)

tic2()

time.sleep(3)

toc2() # returns "Elapsed time 2: 5.00 seconds."

toc() # returns "Elapsed time: 8.00 seconds."

Eigentlich müssen Sie nicht einmal tic()jedes Mal verwenden. Wenn Sie eine Reihe von Befehlen haben, die Sie zeitlich festlegen möchten, können Sie schreiben

tic()

time.sleep(1)

toc() # returns "Elapsed time: 1.00 seconds."

time.sleep(2)

toc() # returns "Elapsed time: 2.00 seconds."

time.sleep(3)

toc() # returns "Elapsed time: 3.00 seconds."

# and so on...

Ich hoffe das ist hilfreich.


22

Das absolut beste Analogon von Tic und Toc wäre, sie einfach in Python zu definieren.

def tic():
    #Homemade version of matlab tic and toc functions
    import time
    global startTime_for_tictoc
    startTime_for_tictoc = time.time()

def toc():
    import time
    if 'startTime_for_tictoc' in globals():
        print "Elapsed time is " + str(time.time() - startTime_for_tictoc) + " seconds."
    else:
        print "Toc: start time not set"

Dann können Sie sie verwenden als:

tic()
# do stuff
toc()

6
Dies verhält sich bei verschachtelter Verwendung von ticund toc, die Matlab unterstützt, nicht korrekt . Ein wenig mehr Raffinesse wäre erforderlich.
Stefan

2
Ich habe ähnliche Funktionen in meinem eigenen Code implementiert, als ich ein grundlegendes Timing benötigte. Ich würde jedoch die import timeAußenseite beider Funktionen entfernen , da dies möglicherweise einige Zeit dauern kann.
Bas Swinckels

Wenn Sie darauf bestehen, diese Technik zu verwenden, und Sie sie benötigen, um verschachtelte Tics / Tocs zu verarbeiten, erstellen Sie die globale Liste und lassen Sie sie darauf ticdrücken und tocaus ihr herausspringen .
Ahmed Fasih

1
Ich habe auch an anderer Stelle gelesen, dass dies timeit.default_timer()besser ist als time.time()weil time.clock()es je nach Betriebssystem besser geeignet sein könnte
Miguel

@AhmedFasih Das ist, was meine Antwort tut, obwohl mehr Dinge verbessert werden könnten.
Antonimmo

15

Normalerweise IPython die %time, %timeit, %prunund %lprun(wenn man hat line_profilerinstalliert) erfüllt meine Profilierungs Bedürfnisse ganz gut. Ein Anwendungsfall für tic-tocähnliche Funktionen ergab sich jedoch, als ich versuchte, Berechnungen zu profilieren, die interaktiv gesteuert wurden, dh durch die Mausbewegung des Benutzers in einer GUI. Ich hatte das Gefühl, tics und tocs in den Quellen zu spammen, während interaktives Testen der schnellste Weg wäre, um die Engpässe aufzudecken. Ich habe mich für Eli Benderskys Kurs entschieden Timer, war aber nicht ganz glücklich, da ich die Einrückung meines Codes ändern musste, was in einigen Editoren unpraktisch sein kann und das Versionskontrollsystem verwirrt. Darüber hinaus besteht möglicherweise die Notwendigkeit, die Zeit zwischen Punkten in verschiedenen Funktionen zu messen, was mit dem nicht funktionieren würdewithAussage. Nachdem ich viel Python-Cleverness ausprobiert habe, ist hier die einfache Lösung, die meiner Meinung nach am besten funktioniert hat:

from time import time
_tstart_stack = []

def tic():
    _tstart_stack.append(time())

def toc(fmt="Elapsed: %s s"):
    print fmt % (time() - _tstart_stack.pop())

Da dies funktioniert, indem die Startzeiten auf einem Stapel verschoben werden, funktioniert es für mehrere Ebenen von tics und tocs korrekt . Es erlaubt auch, die Formatzeichenfolge der tocAnweisung zu ändern , um zusätzliche Informationen anzuzeigen, die mir an Elis TimerKlasse gefallen haben .

Aus irgendeinem Grund habe ich mich mit dem Overhead einer reinen Python-Implementierung befasst und deshalb auch ein C-Erweiterungsmodul getestet:

#include <Python.h>
#include <mach/mach_time.h>
#define MAXDEPTH 100

uint64_t start[MAXDEPTH];
int lvl=0;

static PyObject* tic(PyObject *self, PyObject *args) {
    start[lvl++] = mach_absolute_time();
    Py_RETURN_NONE;
}

static PyObject* toc(PyObject *self, PyObject *args) {
return PyFloat_FromDouble(
        (double)(mach_absolute_time() - start[--lvl]) / 1000000000L);
}

static PyObject* res(PyObject *self, PyObject *args) {
    return tic(NULL, NULL), toc(NULL, NULL);
}

static PyMethodDef methods[] = {
    {"tic", tic, METH_NOARGS, "Start timer"},
    {"toc", toc, METH_NOARGS, "Stop timer"},
    {"res", res, METH_NOARGS, "Test timer resolution"},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
inittictoc(void) {
    Py_InitModule("tictoc", methods);
}

Dies ist für MacOSX, und ich habe Code weggelassen, um zu überprüfen, ob lvler der Kürze halber außerhalb der Grenzen liegt. Während sich tictoc.res()auf meinem System eine Auflösung von etwa 50 Nanosekunden ergibt, stellte ich fest, dass der Jitter beim Messen einer Python-Anweisung leicht im Mikrosekundenbereich liegt (und viel mehr, wenn er von IPython verwendet wird). Zu diesem Zeitpunkt wird der Overhead der Python-Implementierung vernachlässigbar, sodass sie mit der gleichen Sicherheit wie die C-Implementierung verwendet werden kann.

Ich fand heraus, dass die Nützlichkeit des tic-tocAnsatzes praktisch auf Codeblöcke beschränkt ist, deren Ausführung mehr als 10 Mikrosekunden dauert. Darunter sind Mittelungsstrategien wie in timeiterforderlich, um eine genaue Messung zu erhalten.


1
Extrem elegant, @Stefan - kann nicht glauben, dass dies so schlecht bewertet ist. Vielen Dank!
Der

10

Sie können ticund tocvon verwenden ttictoc. Installieren Sie es mit

pip install ttictoc

Und importieren Sie sie einfach wie folgt in Ihr Skript

from ttictoc import tic,toc
tic()
# Some code
print(toc())

8

Ich habe gerade ein Modul [tictoc.py] erstellt, um verschachtelte Tic-Token zu erreichen, was Matlab auch tut.

from time import time

tics = []

def tic():
    tics.append(time())

def toc():
    if len(tics)==0:
        return None
    else:
        return time()-tics.pop()

Und so funktioniert es:

from tictoc import tic, toc

# This keeps track of the whole process
tic()

# Timing a small portion of code (maybe a loop)
tic()

# -- Nested code here --

# End
toc()  # This returns the elapse time (in seconds) since the last invocation of tic()
toc()  # This does the same for the first tic()

Ich hoffe, es hilft.


Schöne Replikation von Tic / Toc von MATLAB!
Matt

1
Ich muss Sie warnen, dass sich dies möglicherweise nicht wie gewünscht verhält, wenn es von mehr als einem Modul gleichzeitig verwendet wird, da sich (AFAIK) -Module wie Singletons verhalten.
Antonimmo

3

Schauen Sie sich das timeitModul an. Es ist nicht wirklich gleichwertig, aber wenn sich der Code, den Sie zeitlich festlegen möchten, in einer Funktion befindet, können Sie ihn problemlos verwenden.


Ja, timeitam besten für Benchmarks. Es muss nicht einmal eine einzelne Funktion sein, Sie können unglaublich komplexe Anweisungen übergeben.

10
Das Übergeben von Code, der kein extrem einfacher Funktionsaufruf als Zeichenfolge ist, ist sehr hässlich.
ThiefMaster


1

Dies kann auch mit einem Wrapper erfolgen. Sehr allgemeine Art, Zeit zu halten.

Der Wrapper in diesem Beispielcode umschließt jede Funktion und gibt die Zeit aus, die zum Ausführen der Funktion benötigt wird:

def timethis(f):
    import time

    def wrapped(*args, **kwargs):
        start = time.time()
        r = f(*args, **kwargs)
        print "Executing {0} took {1} seconds".format(f.func_name,  time.time()-start)
        return r
    return wrapped

@timethis
def thistakestime():
    for x in range(10000000):
        pass

thistakestime()

Die Wrapper-Funktion timethis wird als Dekorateur bezeichnet. Eine etwas ausführlichere Erklärung hier: medium.com/pythonhive/…
Mircea

1

Ich habe die Antwort von @Eli Bendersky ein wenig geändert, um ctor __init__()und dtor __del__()für das Timing zu verwenden, damit es bequemer verwendet werden kann, ohne den ursprünglichen Code einzurücken:

class Timer(object):
    def __init__(self, name=None):
        self.name = name
        self.tstart = time.time()

    def __del__(self):
        if self.name:
            print '%s elapsed: %.2fs' % (self.name, time.time() - self.tstart)
        else:
            print 'Elapsed: %.2fs' % (time.time() - self.tstart)

Setzen Sie den Timer ("blahblah") einfach an den Anfang eines lokalen Bereichs. Die verstrichene Zeit wird am Ende des Bereichs gedruckt:

for i in xrange(5):
    timer = Timer("eigh()")
    x = numpy.random.random((4000,4000));
    x = (x+x.T)/2
    numpy.linalg.eigh(x)
    print i+1
timer = None

Es druckt aus:

1
eigh() elapsed: 10.13s
2
eigh() elapsed: 9.74s
3
eigh() elapsed: 10.70s
4
eigh() elapsed: 10.25s
5
eigh() elapsed: 11.28s

3
Ein Problem bei dieser Implementierung ist die Tatsache, dass timersie nach dem letzten Aufruf nicht gelöscht wird, wenn nach der forSchleife ein anderer Code folgt . Um den letzten Timer-Wert zu erhalten, sollte der timerAfter-the- forLoop gelöscht oder überschrieben werden , z timer = None. B. via .
Bastelflp

1
@bastelflp Ich habe gerade festgestellt, dass ich falsch verstanden habe, was Sie meinten ... Ihr Vorschlag wurde jetzt in den Code aufgenommen. Vielen Dank.
Shaohua Li

1

Aktualisierung von Elis Antwort auf Python 3:

class Timer(object):
    def __init__(self, name=None, filename=None):
        self.name = name
        self.filename = filename

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        message = 'Elapsed: %.2f seconds' % (time.time() - self.tstart)
        if self.name:
            message = '[%s] ' % self.name + message
        print(message)
        if self.filename:
            with open(self.filename,'a') as file:
                print(str(datetime.datetime.now())+": ",message,file=file)

Genau wie bei Eli kann es als Kontextmanager verwendet werden:

import time 
with Timer('Count'):
    for i in range(0,10_000_000):
        pass

Ausgabe:

[Count] Elapsed: 0.27 seconds

Ich habe es auch aktualisiert, um die gemeldeten Zeiteinheiten (Sekunden) zu drucken und die Anzahl der Stellen zu verringern, wie von Can vorgeschlagen, und mit der Option, auch an eine Protokolldatei anzuhängen. Sie müssen datetime importieren, um die Protokollierungsfunktion verwenden zu können:

import time
import datetime 
with Timer('Count', 'log.txt'):    
    for i in range(0,10_000_000):
        pass

0

Aufbauend auf den Antworten von Stefan und Antonimmo habe ich am Ende gesetzt

def Tictoc():
    start_stack = []
    start_named = {}

    def tic(name=None):
        if name is None:
            start_stack.append(time())
        else:
            start_named[name] = time()

    def toc(name=None):
        if name is None:
            start = start_stack.pop()
        else:
            start = start_named.pop(name)
        elapsed = time() - start
        return elapsed
    return tic, toc

in einem utils.pyModul, und ich benutze es mit einem

from utils import Tictoc
tic, toc = Tictoc()

Diesen Weg

  • Sie können einfach verwenden tic(), toc()und nisten sie mögen in Matlab
  • Alternativ können Sie sie benennen : tic(1), toc(1)oder tic('very-important-block'), toc('very-important-block')und Timer mit unterschiedlichen Namen stören nicht
  • Durch den Import auf diese Weise werden Interferenzen zwischen den verwendeten Modulen verhindert.

(Hier druckt toc die verstrichene Zeit nicht aus, sondern gibt sie zurück.)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.