Что такое Python-эквивалент функций tic и toc в Matlab? - PullRequest
80 голосов
/ 01 мая 2011

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

Ответы [ 11 ]

135 голосов
/ 01 мая 2011

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

23 голосов
/ 02 ноября 2014

У меня был тот же вопрос, когда я перешел на python из Matlab. С помощью этого потока я смог построить точный аналог функций 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...

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

18 голосов
/ 19 сентября 2013

Абсолютно лучшим аналогом 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()
11 голосов
/ 15 ноября 2013

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

5 голосов
/ 27 июля 2018

На всякий случай, если кому-то интересно.Основываясь на всех остальных ответах, я написал класс tictoc, который имеет лучшие из них.

Ссылка на github здесь .

Вы также можете использовать pip дляполучить его.

pip install ttictoc

Как его использовать:

Импортировать его

from ttictoc import TicToc

Использование оператора 'with'

Без созданиялюбой объект, который вы можете синхронизировать код следующим образом.

with TicToc('name'):
  some code...

# Prints the elapsed time

Или, создав объект, вы можете сделать то же самое.

t = TicToc('name')
with t:
  some code...

# Prints the elapsed time

Явный вызов Tic Toc

Youтакже можно явно вызывать тик-ток, как показано ниже.

t = TicToc('name')
t.tic()
some code...
t.toc()
print(t.elapsed)
With indentation

Если вы хотите синхронизировать несколько уровней вашего кода, вы также можете сделать это, установив для параметра «отступ» значение True.

t = TicToc(,indentation=True)
t.tic()
some code1...
t.tic()
some code2...
t.tic()
some code3...
t.toc()
print('time for code 3 ',t.elapsed)
t.toc()
print('time for code 2 with code 3 ',t.elapsed)
t.toc()
print('time for code 1 with code 2 and 3 ',t.elapsed)

Аргументы

У класса есть 3 аргумента: имя, метод и отступ.

  • name: Это имя объекта.Это не обязательно.
  • метод: Указывает, какой метод следует использовать для получения времени.
  • Отступ: позволяет использовать один и тот же объект несколько раз с разными отступами во времени.

Аргумент метода может быть либо int, str, либо по вашему выбору.Если это строка, допустимыми значениями являются time, perf_counter и process_time.Если это целое число, допустимыми значениями являются 0, 1 и 2.

  • time или 0: time.time
  • perf_counter или 1: time.perf_counter
  • process_time или 2: time.process_time

Если версия Python> = 3.7: - time_ns или 3: time.time_ns - perf_counter_ns или 4: time.perf_counter_ns - process_time_ns или 5: time.process_time_ns

В случае, если вы предпочитаете использовать другой метод, который вы просто используете (например, time.clock:

TicToc(method=time.clock) 

Класс следующий:

import sys
import time

class TicToc(object):
  """
  Counts the elapsed time.
  """
  def __init__(self,name='',method='time',indentation=False):
    """
    Args:
    name (str): Just informative, not needed
    method (int|str|ftn|clss): Still trying to understand the default
        options. 'time' uses the 'real wold' clock, while the other
        two use the cpu clock. If you want to use your own method, do it
        through this argument

        Valid int values:
          0: time.time  |  1: time.perf_counter  |  2: time.proces_time

          if python version >= 3.7:
          3: time.time_ns  |  4: time.perf_counter_ns  |  5: time.proces_time_ns

        Valid str values:
          'time': time.time  |  'perf_counter': time.perf_counter
          'process_time': time.proces_time

          if python version >= 3.7:
          'time_ns': time.time_ns  |  'perf_counter_ns': time.perf_counter_ns  
          'proces_time_ns': time.proces_time_ns

        Others:
          Whatever you want to use as time.time
    indentation (bool): Allows to do tic toc with indentation with a single object.
        If True, you can put several tics using the same object, and each toc will 
        correspond to the respective tic.
        If False, it will only register one single tic, and return the respective 
        elapsed time of the future tocs.
    """
    self.name = name
    self.indentation = indentation
    if self.indentation:
      self.tstart = []

    self.__measure = 's' # seconds

    self.__vsys = sys.version_info

    if self.__vsys[0]>2 and self.__vsys[1]>=7:
      # If python version is greater or equal than 3.7
      if type(method) is int:
        if method==0: method = 'time'
        elif method==1: method = 'perf_counter'
        elif method==2: method = 'process_time'
        elif method==3: method = 'time_ns'
        elif method==3: method = 'perf_counter_ns'
        elif method==4: method = 'process_time_ns'
        else: 
          import warnings
          msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
          warnings.warn(msg,Warning)
          method = 'time'

      if type(method) is str:
        if method=='time': self.get_time = time.time
        elif method=='perf_counter': self.get_time = time.perf_counter
        elif method=='process_time': self.get_time = time.process_time
        elif method=='time_ns': self.get_time = time.time_ns, self.__measure = 'ns' # nanoseconds
        elif method=='perf_counter_ns': self.get_time = time.perf_counter_ns, self.__measure = 'ns' # nanoseconds
        elif method=='process_time_ns': self.get_time = time.process_time_ns, self.__measure = 'ns' # nanoseconds
        else: 
          import warnings
          msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
          warnings.warn(msg,Warning)
          self.get_time = time.time

      else:
        self.get_time = method
    else:
      # If python vesion is lower than 3.7
      if type(method) is int:
        if method==0: method = 'time'
        elif method==1: method = 'perf_counter'
        elif method==2: method = 'process_time'
        else: 
          import warnings
          msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
          warnings.warn(msg,Warning)
          method = 'time'

      if type(method) is str:
        if method=='time': self.get_time = time.time
        elif method=='perf_counter': self.get_time = time.perf_counter
        elif method=='process_time': self.get_time = time.process_time
        else: 
          import warnings
          msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
          warnings.warn(msg,Warning)
          self.get_time = time.time

      else:
        self.get_time = method

  def __enter__(self):
    if self.indentation:
      self.tstart.append(self.get_time())
    else:
      self.tstart = self.get_time()

  def __exit__(self,type,value,traceback):
    self.tend = self.get_time()
    if self.indentation:
      self.elapsed = self.tend - self.tstart.pop()
    else:
      self.elapsed = self.tend - self.tstart

    if self.name!='': name = '[{}] '.format(self.name)
    else: name = self.name

    print('{0}Elapsed time: {1} ({2})'.format(name,self.elapsed,self.__measure))

  def tic(self):
    if self.indentation:
      self.tstart.append(self.get_time())
    else:
      self.tstart = self.get_time()

  def toc(self):
    self.tend = self.get_time()
    if self.indentation:
      if len(self.tstart)>0:
        self.elapsed = self.tend - self.tstart.pop()
      else:
        self.elapsed = None
    else:
      self.elapsed = self.tend - self.tstart
5 голосов
/ 18 мая 2017

Я только что создал модуль [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()

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

3 голосов
/ 01 мая 2011

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

1 голос
/ 21 июня 2018

Обновление Ответ Илии на 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, а также с возможностью добавления в файл журнала. Вы должны импортировать дату и время, чтобы использовать функцию регистрации:

import time
import datetime 
with Timer('Count', 'log.txt'):    
    for i in range(0,10_000_000):
        pass
1 голос
/ 27 июля 2016

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

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

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()
1 голос
/ 27 мая 2015

Я немного изменил ответ @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)

Чтобы использовать, просто поместите 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...