тик, ток функции аналог в Python

51

Каков наилучший аналог функций tic и toc MATLAB (http://www.mathworks.com/help/techdoc/ref/tic.html) в Python?

  • 4
    Если вы действительно хотите получить прямой эквивалент, просто вызовите tic = time.time() и toc = time.time() , а затем print toc-tic, 'sec Elapsed' Как уже сказали ребята, timeit более устойчив.
  • 0
    Я, кажется, получаю лучшие результаты, используя подход @ JoeKington в сочетании с timeit.default_timer (), например, так: tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer() , затем print toc-tic .
Теги:

9 ответов

87
Лучший ответ

Помимо 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 - все зависит от того, что вы хотите измерить.

  • 0
    Спасибо, класс помощников, как твой, это то, что мне нужно =)
  • 0
    Не используйте эту технику никогда. Если timeit не хватает timeit ( docs.python.org/library/timeit.html ), используйте выполнение кода с помощью приличного профилировщика ( docs.python.org/library/profile.html ). Даже если в Matlab распространена парадигма tic, toc , пожалуйста, не пытайтесь дублировать такую глупость в python . Существуют действительно лучшие инструменты для таких оценок в python . Спасибо
Показать ещё 8 комментариев
16

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

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 для отслеживания нескольких операций или просто по-разному. Например, хотя время a script, мы можем теперь разделить каждую часть script отдельно, а также весь script. (Я приведу конкретный пример)

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)

Теперь вы должны иметь возможность разделить две отдельные вещи: В следующем примере мы разываем общее количество script и частей script отдельно.

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

Я надеюсь, что это будет полезно.

14

Абсолютным лучшим аналогом 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()
  • 4
    Это не будет работать правильно в случае вложенного использования tic и toc , которые поддерживает Matlab. Потребуется немного больше изощренности.
  • 1
    Я реализовал подобные функции в своем собственном коде, когда мне нужно было немного времени. Однако я бы удалил время import time пределы обеих функций, поскольку это может занять довольно много времени.
Показать ещё 2 комментария
7

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

Так как это работает, нажимая время начала стека, оно будет работать правильно для нескольких уровней tic и toc s. Он также позволяет изменить строку формата оператора toc, чтобы отобразить дополнительную информацию, которую мне понравился в классе Eli 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-Approach практически ограничена блоками кода, для выполнения которых требуется более 10 микросекунд. Ниже приведены стратегии усреднения, такие как timeit, чтобы получить точное измерение.

  • 0
    Исключительно элегантный, @Stefan - не могу поверить, что это так низко оценили. Спасибо!
4

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

Надеюсь, это поможет.

  • 0
    Хорошая копия Tic / Toc от MATLAB!
  • 1
    Я должен предупредить вас, что это может работать не так, как хотелось бы, если одновременно используется более чем одним модулем, поскольку (AFAIK) модули ведут себя как одиночные пакеты.
3

Посмотрите модуль timeit. Это не совсем эквивалентно, но если код, который вы хотите использовать, находится внутри функции, которую вы можете легко использовать.

  • 0
    Да, timeit лучше всего подходит для тестов. Это даже не должна быть одна функция, вы можете передавать чрезвычайно сложные операторы.
  • 9
    Ну, передача кода, который не является чрезвычайно простым вызовом функции в виде строки, очень уродлива.
1

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

Чтобы использовать простой таймер ( "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
  • 2
    Проблема с этой реализацией заключается в том, что timer не удаляется после последнего вызова, если любой другой код следует после цикла for . Чтобы получить последнее значение таймера, следует удалить или перезаписать timer после цикла for , например, через timer = None .
  • 1
    @bastelflp Просто понял, что я неправильно понял, что вы имели в виду ... Ваше предложение теперь включено в код. Благодарю.
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 не печатает прошедшее время, а возвращает его.)

0

Это также можно сделать с помощью обертки. Очень общий способ удержания времени.

Оболочка в этом примере кода обертывает любую функцию и печатает количество времени, необходимое для выполнения функции:

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()
  • 0
    Функция-обертка timethis называется декоратором. Немного более подробное объяснение здесь: medium.com/pythonhive/…

Ещё вопросы

Сообщество Overcoder
Наверх
Меню