Что такое Python-эквивалент функций tic и toc в Matlab?


113

Что такое Python-эквивалент функций tic и toc в Matlab ?


8
Если вы действительно хотите прямой эквивалент, просто позвоните 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()then print toc-tic.
littleO

1
Библиотека pytictoc кажется наиболее подходящей, синтаксис даже немного лучше, чем ttictoc ниже. pypi.org/project/pytictoc
FlorianH

Ответы:


174

Помимо 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. Спасибо за отличную идею.
Джан Каваклыоглу

4
На первый взгляд это выглядит очень удобным, но на практике требуется, чтобы кто-то сделал отступ в кодовом блоке, который нужно рассчитать, что может быть довольно неудобным в зависимости от длины кодового блока и выбранного редактора. По-прежнему элегантное решение, которое корректно ведет себя в случае вложенного использования.
Стефан

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

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

32

У меня был такой же вопрос, когда я перешел на Python с Matlab. С помощью этой ветки я смог построить точный аналог Matlab tic()и toc()functions. Просто вставьте следующий код в начало вашего скрипта.

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, сделайте глобальный список и позвольте ticpush к нему и выталкивать tocиз него.
Ахмед Фасих

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

@AhmedFasih Это мой ответ, хотя можно было бы улучшить и другие вещи.
antonimmo

15

Обычно IPython - х %time, %timeit, %prunи %lprun(если таковой line_profilerустановлен) удовлетворяет мои потребности профилирующие достаточно хорошо. Однако вариант использования tic-toc-подобной функциональности возник, когда я попытался профилировать вычисления, которые выполнялись интерактивно, то есть движением мыши пользователя в графическом интерфейсе. Я чувствовал , как спам tics и tocS в источниках во время тестирования в интерактивном режиме будет самым быстрым способом выявить узкие места. Я пошел с 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-toc-подхода практически ограничена блоками кода, выполнение которых занимает более 10 микросекунд. Ниже этого уровня timeitтребуются стратегии усреднения, подобные приведенному выше, для получения точных измерений.


1
Чрезвычайно элегантный, @Stefan - не могу поверить, что у него такой низкий рейтинг. Спасибо!
thclark 08

10

Можно использовать ticи tocот ttictoc. Установите его с помощью

pip install ttictoc

И просто импортируйте их в свой скрипт, как показано ниже

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

8

Я только что создал модуль [tictoc.py] для создания вложенных тик-тактов, что и делает 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()

Функция-оболочка timethis называется декоратором. Немного более подробное объяснение здесь: medium.com/pythonhive/…
Мирча

1

Я немного изменил ответ @Eli __init__()Bendersky, чтобы использовать 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)

Чтобы использовать, просто поместите Timer ("blahblah") в начало некоторой локальной области видимости. Истекшее время будет напечатано в конце области:

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 05

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)

Как и у Эли, его можно использовать как диспетчер контекста:

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

Вывод:

[Count] Elapsed: 0.27 seconds

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

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модуле, и я использую его с

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.