Який еквівалент Python функцій tic та toc Matlab?


112

Який еквівалент Python функцій tic та toc Matlab ?


7
Якщо ви дійсно хочете прямого еквівалента, просто зателефонуйте tic = time.time()і toc = time.time(), тоді, print toc-tic, 'sec Elapsed'як люди сказали нижче, хоч і timeitнадійніше.
Джо Кінгтон

Я , здається , щоб отримати кращі результати , використовуючи @ підхід JoeKington в поєднанні з timeit.default_timer (), як це, наприклад: tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer(), то print toc-tic.
маленькийО

1
Пібіток бібліотеки здається найбільш схильним, синтаксис навіть трохи акуратніше, ніж ttictoc внизу. pypi.org/project/pytictoc
FlorianH

Відповіді:


172

Крім того, про timeitякий згадував ThiefMaster, простий спосіб зробити це просто (після імпорту time):

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

У мене є клас помічників, який я люблю використовувати:

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

Його можна використовувати як менеджер контексту:

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

Іноді мені ця техніка зручніша, ніж timeit- все залежить від того, що ти хочеш виміряти.


25
@eat: Я з повагою не згоден. Люди використовують команду unix timeдля вимірювання часу виконання програм назавжди, і цей метод копіює це всередині коду Python. Я не бачу в цьому нічого поганого, якщо це правильний інструмент для роботи. timeitне завжди це, і профайлер - це набагато більш важке рішення для більшості потреб
Елі Бендерський

4
Для останнього рядка я б запропонував print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'. Важко зрозуміти без% .2f. Дякую за чудову ідею.
Can Kavaklıoğlu

4
На перший погляд це виглядає дуже зручно, але на практиці вимагає відрізати блок коду, який хочеться вчасно, що може бути досить незручно в залежності від довжини блоку коду та редактора, що вибирається. Все-таки елегантне рішення, яке правильно поводиться у разі вкладеного використання.
Стефан

1
Я думаю, що ти хочеш elapsed = t - time.time()замість цього elapsed = time.time() - t. В останньому минулий буде негативним. Я запропонував цю зміну змінити.
rysqui

3
@rysqui - Чи не завжди поточний час є більшою кількістю, ніж попередній час ? Я думаю, що elapsed = time.time() - tце форма, яка завжди дає позитивне значення.
Скотт Сміт

32

У мене було те саме питання, коли я перейшов на пітон з Матлаба. За допомогою цієї нитки мені вдалося побудувати точний аналог Matlab tic()та toc()функцій. Просто вставте наступний код у верхній частині сценарію.

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)

Це воно! Тепер ми готові до повного використання tic()і так toc()само, як у Matlab. Наприклад

tic()

time.sleep(5)

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

Насправді це більш універсально, ніж вбудовані функції Matlab. Тут ви можете створити інший примірник TicTocGeneratorдля відстеження кількох операцій або просто провести час по-різному. Наприклад, під час вибору сценарію ми тепер можемо окремо розраховувати кожен фрагмент сценарію окремо, як і весь сценарій. (Я надам конкретний приклад)

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)

Тепер ви маєте змогу обміняти дві окремі речі: У наступному прикладі ми загадуємо загальний сценарій та частини сценарію окремо.

tic()

time.sleep(5)

tic2()

time.sleep(3)

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

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

Власне, вам навіть не потрібно tic()кожен раз користуватися. Якщо у вас є ряд команд, які ви хочете вчасно, тоді ви можете написати

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

Я сподіваюся, що це корисно.


22

Абсолютним найкращим аналогом tic і toc було б просто визначити їх у python.

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"

Тоді ви можете використовувати їх як:

tic()
# do stuff
toc()

6
Це не буде правильно вести себе у випадку використання вкладених ticі toc, що підтримує Matlab. Потрібно трохи більше вишуканості.
Стефан

2
Я реалізував подібні функції у власному коді, коли мені потрібні були основні терміни. Однак я б видалив import timeзовнішні функції обох функцій, оскільки це може зайняти досить багато часу.
Bas Swinckels

Якщо ви наполягаєте на використанні цієї методики, і вам вона потрібна для обробки вкладених tic / toc, складіть глобальний список і дозвольте ticнатиснути на нього та вийти tocз нього.
Ахмед Фасіх

1
Також я читав в інших місцях, що timeit.default_timer()краще, ніж time.time()тому, що time.clock()може бути більш підходящим в залежності від ОС
Мігель,

@AhmedFasih Ось що робить моя відповідь, хоча можна змінити більше речей.
antonimmo

15

Зазвичай IPython - х %time, %timeit, %prunі %lprun(якщо такий line_profilerвстановлено) задовольняє мої потреби профілюючі досить добре. Однак випадок використання для tic-toc-подібної функціональності виник, коли я намагався профілювати обчислення, які керувались інтерактивно, тобто рухом миші користувача в графічному інтерфейсі. Мені здалося, що спам- ticі та tocджерела в джерелах, а інтерактивне тестування - це найшвидший спосіб виявити вузькі місця. Я ходив з Timerкласом Елі Бендерського , але не був цілком задоволений, оскільки це вимагало від мене змінити відступ мого коду, що може бути незручним для деяких редакторів і заплутати систему контролю версій. Крім того, може виникнути потреба в вимірюванні часу між точками в різних функціях, які не працюватимуть зwithзаява. Спробувавши багато кмітливості Python, ось просте рішення, яке я знайшов найкращим:

from time import time
_tstart_stack = []

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

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

Оскільки це працює, натискаючи на стек час початку, він буде працювати коректно для декількох рівнів tics і tocs. Це також дозволяє змінювати рядок формату tocзаяви, щоб відображати додаткову інформацію, що мені сподобалось про Timerклас Елі .

Чомусь я переймався витратами на чисту реалізацію Python, тому я протестував модуль розширення 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);
}

Це для MacOSX, і я пропустив код, щоб перевірити, чи lvlнемає меж для стислості. Хоча tictoc.res()в моїй системі дається роздільна здатність близько 50 наносекунд, я виявив, що тремтіння вимірювання будь-якого оператора Python легко знаходиться в діапазоні мікросекунд (і набагато більше при використанні з IPython). З цього моменту видатки на реалізацію Python стають незначними, так що його можна використовувати з тією ж впевненістю, що і реалізацію C.

Я виявив, що корисність- tic-tocAssach практично обмежена кодовими блоками, на виконання яких потрібно більше 10 мікросекунд. Нижче, timeitдля досягнення вірного вимірювання, потрібні стратегії усереднення, подібні до in .


1
Надзвичайно елегантний, @Stefan - не можу повірити, що це так низько оцінено. Дякую!
thclark

10

Ви можете використовувати ticі tocвід ttictoc. Встановіть його за допомогою

pip install ttictoc

І просто імпортуйте їх у свій сценарій, як слід

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

8

Я щойно створив модуль [tictoc.py] для досягнення вкладених tic tocs, що і робить Matlab.

from time import time

tics = []

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

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

І це працює так:

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

Я сподіваюся, що це допомагає.


Приємна реплікація tic / toc від MATLAB!
Метт

1
Мушу попередити, що це може не так, як хочеться, коли використовується одночасно більш ніж 1 модулем, оскільки (AFAIK) модулі поводяться як одиночні.
antonimmo

3

Погляньте на timeitмодуль. Це насправді не рівнозначно, але якщо код, який ви хочете ввести, знаходиться всередині функції, ви можете легко використовувати його.


Так, timeitнайкраще для орієнтирів. Це навіть не повинно бути однією функцією, ви можете передавати рядно складні заяви.

10
Ну, передавати код, який не є надзвичайно простим викликом функції, оскільки рядок дуже некрасивий.
ThiefMaster


1

Це також можна зробити за допомогою обгортки. Дуже загальний спосіб зберігати час.

Обгортка в цьому прикладі коду завершує будь-яку функцію і друкує кількість часу, необхідного для виконання функції:

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

Функція обгортки, тиметис, називається декоратором. Трохи детальніше пояснення тут: medium.com/pythonhive/…
Mircea

1

Я трохи змінив відповідь @ Елі Бендерського, щоб використати __init__()ctor та dtor, __del__()щоб зробити час, щоб зручніше було користуватися без відступу оригінального коду:

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)

Для використання простий поставте Таймер ("блабла") на початок деякої локальної області. Пройдений час буде надруковано в кінці області:

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

Він виводить:

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
Проблема з цією реалізацією полягає в тому, що timerвона не видаляється після останнього дзвінка, якщо після forциклу слідує якийсь інший код . Щоб отримати останнє значення таймера, слід видалити або перезаписати цикл timerпісля forциклу, наприклад через timer = None.
bastelflp

1
@bastelflp Щойно зрозумів, що я неправильно зрозумів, що ти маєш на увазі ... Ваша пропозиція тепер включена в код. Дякую.
Шаохуа Лі

1

Оновлення відповіді Елі на 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)

Як і у Eli, він може використовуватися як менеджер контексту:

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

Вихід:

[Count] Elapsed: 0.27 seconds

Я також оновив його, щоб надрукувати одиниці часу, про які повідомляється (секунд), і обрізати кількість цифр, як запропонував Can, і з можливістю також додавати до журналу файл. Ви повинні імпортувати час, щоб скористатися функцією ведення журналу:

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

0

Спираючись на відповіді Стефана та Антонімо, я закінчився

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

в utils.pyмодулі, і я використовую його з a

from utils import Tictoc
tic, toc = Tictoc()

Сюди

  • ви можете просто використовувати tic(), toc()і вкладати їх як в Matlab
  • в якості альтернативи, ви можете назвати їх: tic(1), toc(1)або tic('very-important-block'), toc('very-important-block')і таймери з різними назвами не заважатимуть
  • імпорт їх таким чином запобігає втручанню між ним модулями.

(тут toc не друкує минулий час, але повертає його.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.