Jaki jest odpowiednik w Pythonie funkcji tic i toc Matlaba?


112

Jaki jest odpowiednik w Pythonie funkcji tic i toc Matlaba ?


7
Jeśli naprawdę chcesz mieć bezpośredni odpowiednik, po prostu zadzwoń, tic = time.time()a toc = time.time(), print toc-tic, 'sec Elapsed'jak powiedzieli ludzie poniżej, timeitjest jednak bardziej wytrzymały.
Joe Kington

Wydaje się, że lepsze wyniki uzyskuję stosując podejście @ JoeKington w połączeniu z timeit.default_timer (), jak na przykład:, tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer()then print toc-tic.
littleO

1
Biblioteka pytictoc wydaje się najdobitniejsza, składnia jest nawet nieco schludniejsza niż poniżej ttictoc. pypi.org/project/pytictoc
FlorianH

Odpowiedzi:


172

Poza timeittym, o czym wspomniał ThiefMaster, prostym sposobem na to jest po prostu (po imporcie time):

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

Mam klasę pomocniczą, której lubię używać:

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

Może być używany jako menedżer kontekstu:

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

Czasami uważam tę technikę za wygodniejszą niż timeit- wszystko zależy od tego, co chcesz zmierzyć.


25
@eat: Z całym szacunkiem się nie zgadzam. Ludzie od zawsze używali timepolecenia unix do mierzenia czasu wykonywania programów, a ta metoda powiela to w kodzie Pythona. Nie widzę w tym nic złego, o ile jest to właściwe narzędzie do pracy. timeitnie zawsze tak jest, a profiler jest znacznie cięższym rozwiązaniem dla większości potrzeb
Eli Bendersky

4
Na ostatnią linię zasugerowałbym print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'. Trudno to zrozumieć bez% .2f. Dzięki za świetny pomysł.
Can Kavaklıoğlu

4
Na pierwszy rzut oka wygląda to bardzo wygodnie, ale w praktyce wymaga wcięcia bloku kodu, który chcemy w czasie, co może być dość niewygodne w zależności od długości bloku kodu i wybranego edytora. Wciąż eleganckie rozwiązanie, które zachowuje się poprawnie w przypadku użytkowania zagnieżdżonego.
Stefan

1
Myślę, że chcesz elapsed = t - time.time(), zamiast elapsed = time.time() - t. W tym ostatnim upłynął będzie ujemny. Zaproponowałem tę zmianę jako edycję.
rysqui

3
@rysqui - Czy aktualna godzina nie jest zawsze większa niż poprzednia ? Myślę, że elapsed = time.time() - tjest to forma, która zawsze daje wartość dodatnią.
Scott Smith

32

Miałem to samo pytanie, kiedy migrowałem do Pythona z Matlab. Z pomocą tego wątku udało mi się skonstruować dokładny odpowiednik Matlaba tic()i toc()funkcji. Po prostu wstaw następujący kod u góry skryptu.

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)

Otóż ​​to! Teraz jesteśmy gotowi do pełnego wykorzystania tic()i toc()tak jak w Matlabie. Na przykład

tic()

time.sleep(5)

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

W rzeczywistości jest to bardziej wszechstronne niż wbudowane funkcje Matlab. Tutaj możesz utworzyć kolejną instancję, TicTocGeneratoraby śledzić wiele operacji lub po prostu inaczej mierzyć czas. Na przykład, podczas odmierzania czasu skryptu, możemy teraz określić czas dla każdego fragmentu skryptu oddzielnie, a także dla całego skryptu. (Podam konkretny przykład)

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)

Teraz powinieneś być w stanie zmierzyć czas w dwóch osobnych rzeczach: W poniższym przykładzie oddzielnie mierzymy czas dla całego skryptu i części skryptu.

tic()

time.sleep(5)

tic2()

time.sleep(3)

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

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

Właściwie nie musisz nawet używać za tic()każdym razem. Jeśli masz serię poleceń, które chcesz mierzyć czas, możesz napisać

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

Mam nadzieję, że jest to pomocne.


22

Absolutnie najlepszym analogiem tic i toc byłoby po prostu zdefiniowanie ich w Pythonie.

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"

Następnie możesz ich użyć jako:

tic()
# do stuff
toc()

6
Nie będzie to działać poprawnie w przypadku zagnieżdżonego użycia tici toc, które obsługuje Matlab. Wymagane byłoby trochę więcej wyrafinowania.
Stefan

2
Zaimplementowałem podobne funkcje we własnym kodzie, gdy potrzebowałem podstawowego timingu. Chciałbym jednak usunąć import timezewnętrzną stronę obu funkcji, ponieważ może to zająć trochę czasu.
Bas Swinckels

Jeśli upierasz się przy używaniu tej techniki i potrzebujesz jej do obsługi zagnieżdżonych tic / toc, zrób globalną listę i pozwól jej ticpushować i tocwyskakiwać z niej.
Ahmed Fasih

1
Czytałem też gdzie indziej, że timeit.default_timer()jest to lepsze niż time.time()ponieważ time.clock()może być bardziej odpowiednie w zależności od systemu operacyjnego
Miguel,

@AhmedFasih Tak właśnie działa moja odpowiedź, chociaż można by poprawić więcej.
antonimmo

15

Zazwyczaj ipython na %time, %timeit, %prunoraz %lprun(jeśli jest line_profilerzainstalowany) spełniają moje potrzeby profilowania całkiem dobrze. Jednak przypadek użycia tic-tocfunkcji-like pojawił się, gdy próbowałem profilować obliczenia, które były interaktywnie sterowane, tj. Przez ruch myszy użytkownika w GUI. Czułem, że spamowanie tici spamowanie tocw źródłach podczas interaktywnego testowania byłoby najszybszym sposobem na ujawnienie wąskich gardeł. Poszedłem z Timerklasą Eli Bendersky'ego , ale nie byłem w pełni zadowolony, ponieważ wymagało to zmiany wcięcia mojego kodu, co może być niewygodne w niektórych edytorach i dezorientuje system kontroli wersji. Ponadto może zaistnieć potrzeba pomiaru czasu między punktami w różnych funkcjach, które nie działałyby zwithkomunikat. Po wypróbowaniu dużej ilości sprytu w Pythonie, oto proste rozwiązanie, które według mnie działa najlepiej:

from time import time
_tstart_stack = []

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

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

Ponieważ działa to poprzez przesuwanie czasów rozpoczęcia na stosie, będzie działać poprawnie na wielu poziomach ticsi tocs. Pozwala również zmienić ciąg formatu tocinstrukcji, aby wyświetlić dodatkowe informacje, które podobały mi się w Timerklasie Eli .

Z jakiegoś powodu martwiłem się o narzuty związane z czystą implementacją Pythona, więc przetestowałem również moduł rozszerzenia C:

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

To jest dla MacOSX i pominąłem kod, aby sprawdzić, czy lvljest poza zakresem dla zwięzłości. Chociaż tictoc.res()zapewnia rozdzielczość około 50 nanosekund w moim systemie, odkryłem, że jitter pomiaru dowolnej instrukcji Pythona z łatwością mieści się w zakresie mikrosekund (i znacznie więcej, gdy jest używany z IPythona). W tym momencie narzut implementacji Pythona staje się pomijalny, dzięki czemu można go używać z takim samym zaufaniem jak implementacja C.

Okazało się, że użyteczność metody tic-toc-podejścia jest praktycznie ograniczona do bloków kodu, których wykonanie zajmuje więcej niż 10 mikrosekund. Poniżej, timeitaby uzyskać wierny pomiar, wymagane są strategie uśredniania, takie jak w .


1
Niezwykle elegancki, @Stefan - nie mogę uwierzyć, że jest tak nisko oceniany. Dzięki!
thclark

10

Możesz używać tici tocod ttictoc. Zainstaluj go z

pip install ttictoc

I po prostu zaimportuj je do swojego skryptu w następujący sposób

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

8

Właśnie utworzyłem moduł [tictoc.py] do tworzenia zagnieżdżonych tików, co robi Matlab.

from time import time

tics = []

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

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

I to działa w ten sposób:

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

Mam nadzieję, że to pomoże.


Niezła replika tic / toc z MATLAB!
Matt,

1
Muszę cię ostrzec, że może to nie zachowywać się zgodnie z oczekiwaniami, gdy jest używane jednocześnie przez więcej niż 1 moduł, ponieważ moduły (AFAIK) zachowują się jak pojedyncze.
antonimmo

3

Przyjrzyj się timeitmodułowi. Nie jest to tak naprawdę równoważne, ale jeśli kod, który chcesz mierzyć czas, znajduje się wewnątrz funkcji, możesz go łatwo użyć.


Tak, timeitnajlepiej sprawdza się w testach porównawczych. Nie musi to być nawet pojedyncza funkcja, możesz przekazywać bardzo złożone instrukcje.

10
Cóż, przekazywanie kodu, który nie jest niezwykle prostym wywołaniem funkcji jako ciąg znaków, jest bardzo brzydkie.
ThiefMaster


1

Można to również zrobić za pomocą opakowania. Bardzo ogólny sposób na trzymanie czasu.

Opakowanie w tym przykładowym kodzie otacza dowolną funkcję i wyświetla ilość czasu potrzebną do wykonania funkcji:

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

Funkcja owijania, timethis, nazywana jest dekoratorem. Trochę bardziej szczegółowe wyjaśnienie, tutaj: medium.com/pythonhive/…
Mircea

1

Zmieniłem trochę odpowiedź @Eli Bendersky, aby użyć ctor __init__()i dtor __del__()do pomiaru czasu, aby można było z niego wygodniej korzystać bez wcięcia oryginalnego kodu:

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)

Aby użyć, po prostu umieść Timer („blahblah”) na początku jakiegoś lokalnego zakresu. Upływający czas zostanie wydrukowany na końcu zakresu:

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

Drukuje:

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
Problem z tą implementacją polega na tym, że timernie jest ona usuwana po ostatnim wywołaniu, jeśli po forpętli występuje inny kod . Aby uzyskać ostatnią wartość timera, należy usunąć lub nadpisać timerpo forpętli, na przykład poprzez timer = None.
bastelflp

1
@bastelflp Właśnie sobie uświadomiłem, że źle zrozumiałem, co masz na myśli ... Twoja sugestia została teraz uwzględniona w kodzie. Dzięki.
Shaohua Li

1

Aktualizacja odpowiedzi Eli do Pythona 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)

Podobnie jak Eli, może być używany jako menedżer kontekstu:

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

Wynik:

[Count] Elapsed: 0.27 seconds

Zaktualizowałem go również, aby drukować zgłaszane jednostki czasu (sekundy) i zmniejszać liczbę cyfr zgodnie z sugestią Can, a także z opcją dołączania do pliku dziennika. Aby korzystać z funkcji rejestrowania, musisz zaimportować datę i godzinę:

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

0

Opierając się na odpowiedziach Stefana i antonimmo, skończyło się umieszczaniem

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

w utils.pymodule i używam go z plikiem

from utils import Tictoc
tic, toc = Tictoc()

Tą drogą

  • możesz po prostu użyć tic(), toc()a gniazdo nich jak w Matlab
  • Alternatywnie, można je nazwać: tic(1), toc(1)lub tic('very-important-block'), toc('very-important-block')i timery z różnymi nazwami nie będzie kolidować
  • zaimportowanie ich w ten sposób zapobiega zakłóceniom między modułami z nich korzystającymi.

(tutaj toc nie drukuje czasu, który upłynął, ale zwraca go).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.