Wat is het Python-equivalent van Matlab’s tic- en toc-functies?

Wat is het Python-equivalent van Matlab’s tic- en toc-functies?


Antwoord 1, autoriteit 100%

Afgezien van timeitdie ThiefMaster noemde, is een eenvoudige manier om het te doen gewoon (na het importeren van time):

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

Ik heb een helperklas die ik graag gebruik:

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

Het kan worden gebruikt als contextmanager:

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

Soms vind ik deze techniek handiger dan timeit– het hangt allemaal af van wat je wilt meten.


Antwoord 2, autoriteit 20%

Ik had dezelfde vraag toen ik vanuit Matlab naar Python migreerde. Met behulp van deze thread was ik in staat om een exacteanaloog van de Matlab tic()en toc()functies te construeren. Voeg eenvoudig de volgende code bovenaan uw script in.

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)

Dat is het! Nu zijn we klaar om tic()en toc()volledig te gebruiken, net als in Matlab. Bijvoorbeeld

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

Eigenlijk is dit veelzijdiger dan de ingebouwde Matlab-functies. Hier zou je een andere instantie van de TicTocGeneratorkunnen maken om meerdere bewerkingen bij te houden, of gewoon om dingen anders te timen. Als we bijvoorbeeld een script timen, kunnen we nu elk stuk van het script afzonderlijk timen, evenals het hele script. (Ik zal een concreet voorbeeld geven)

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)

Je zou nu twee afzonderlijke dingen moeten kunnen timen: in het volgende voorbeeld timen we het totale script en delen van een script afzonderlijk.

tic()
time.sleep(5)
tic2()
time.sleep(3)
toc2() # returns "Elapsed time 2: 5.00 seconds."
toc() # returns "Elapsed time: 8.00 seconds."

Eigenlijk hoef je niet eens elke keer tic()te gebruiken. Als je een reeks commando’s hebt die je wilt timen, dan kun je schrijven

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

Ik hoop dat dit nuttig is.


Antwoord 3, autoriteit 12%

De absoluut beste analogie van tic en toc zou zijn om ze eenvoudig in python te definiëren.

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"

Dan kun je ze gebruiken als:

tic()
# do stuff
toc()

Antwoord 4, autoriteit 9%

Meestal IPython’s %time, %timeit, %prunen %lprun(als men line_profilergeïnstalleerd) voldoen redelijk goed aan mijn profileringsbehoeften. Er ontstond echter een use case voor tic-toc-achtige functionaliteit toen ik probeerde om berekeningen te profileren die interactief werden aangestuurd, d.w.z. door de muisbeweging van de gebruiker in een GUI. Ik had het gevoel dat het spammen van tics en tocs in de bronnen, terwijl interactief testen de snelste manier zou zijn om de knelpunten aan het licht te brengen. Ik ging met Eli Bendersky’s Timer-les mee, maar was niet helemaal tevreden, omdat ik de inspringing van mijn code moest veranderen, wat in sommige editors onhandig kan zijn en het versiebeheersysteem in de war brengt. Bovendien kan het nodig zijn om de tijd tussen punten in verschillende functies te meten, wat niet zou werken met de with-instructie. Na veel Python-slimheid te hebben uitgeprobeerd, is hier de eenvoudige oplossing die volgens mij het beste werkte:

from time import time
_tstart_stack = []
def tic():
    _tstart_stack.append(time())
def toc(fmt="Elapsed: %s s"):
    print fmt % (time() - _tstart_stack.pop())

Omdat dit werkt door de starttijden op een stapel te duwen, werkt het correct voor meerdere niveaus van tics en tocs. Het maakt het ook mogelijk om de opmaakreeks van de toc-instructie te wijzigen om aanvullende informatie weer te geven, wat ik leuk vond aan Eli’s Timer-klasse.

Om de een of andere reden maakte ik me zorgen over de overhead van een pure Python-implementatie, dus heb ik ook een C-uitbreidingsmodule getest:

#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);
}

Dit is voor MacOSX en ik heb code weggelaten om te controleren of lvlvoor de beknoptheid buiten de grenzen valt. Terwijl tictoc.res()een resolutie van ongeveer 50 nanoseconden op mijn systeem oplevert, ontdekte ik dat de jitter van het meten van een Python-statement gemakkelijk in het microsecondebereik ligt (en nog veel meer bij gebruik vanaf IPython). Op dit punt wordt de overhead van de Python-implementatie te verwaarlozen, zodat deze met hetzelfde vertrouwen kan worden gebruikt als de C-implementatie.

Ik ontdekte dat het nut van de tic-toc-aanpak praktisch beperkt is tot codeblokken die meer dan 10 microseconden nodig hebben om uit te voeren. Daaronder zijn middelingsstrategieën zoals in timeitvereist om een betrouwbare meting te krijgen.


Antwoord 5, autoriteit 5%

Je kunt ticen tocvan ttictocgebruiken. Installeer het met

pip install ttictoc

En importeer ze gewoon als volgt in uw script

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

Antwoord 6, autoriteit 4%

Ik heb zojuist een module [tictoc.py] gemaakt om geneste tic toc’s te bereiken, wat Matlab doet.

from time import time
tics = []
def tic():
    tics.append(time())
def toc():
    if len(tics)==0:
        return None
    else:
        return time()-tics.pop()

En het werkt zo:

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

Ik hoop dat het helpt.


Antwoord 7, autoriteit 2%

Bekijk de module timeit.
Het is niet echt equivalent, maar als de code die u wilt timen zich in een functie bevindt, kunt u deze gemakkelijk gebruiken.


Antwoord 8

pip install easy-tictoc

In de code:

from tictoc import tic, toc
tic()
#Some code
toc()

Disclaimer: ik ben de auteur van deze bibliotheek.


Antwoord 9

Dit kan ook met een wrapper. Zeer algemene manier om de tijd bij te houden.

De wrapper in deze voorbeeldcode wikkelt elke functie en drukt de hoeveelheid tijd af die nodig is om de functie uit te voeren:

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

Antwoord 10

Ik heb het antwoord van @Eli Bendersky een klein beetje gewijzigd om de ctor __init__()en dtor __del__()te gebruiken om de timing te doen, zodat het meer kan worden gebruikt handig zonder de originele code te laten inspringen:

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)

Om te gebruiken, plaatst u Timer(“blahblah”) aan het begin van een lokaal bereik. Verstreken tijd wordt afgedrukt aan het einde van de scoop:

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

Er wordt afgedrukt:

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

Antwoord 11

Voortbouwend op de antwoorden van Stefan en antonimmo, heb ik uiteindelijk

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 een utils.pymodule, en ik gebruik het met een

from utils import Tictoc
tic, toc = Tictoc()

Op deze manier

  • je kunt gewoon tic(), toc()gebruiken en ze nesten zoals in Matlab
  • je kunt ze ook een naam geven: tic(1), toc(1)of tic('very-important-block'), toc('very-important-block')en timers met verschillende namen storen niet
  • door ze op deze manier te importeren, voorkomt u interferentie tussen modules die het gebruiken.

(hier drukt toc de verstreken tijd niet af, maar geeft deze terug.)


Antwoord 12

Eli’s antwoordbijwerken naar 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)

Net als die van Eli, kan het worden gebruikt als contextmanager:

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

Uitgang:

[Count] Elapsed: 0.27 seconds

Ik heb het ook bijgewerkt om de gemelde tijdseenheden (seconden) af te drukken en het aantal cijfers te trimmen zoals gesuggereerd door CAN en met de mogelijkheid om ook toe te voegen aan een logbestand. U moet DATETIME importeren om de functie Logging te gebruiken:

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

Other episodes