Что такое Python-эквивалент функций Matlab tic и toc?
Что такое Python-эквивалент функций Matlab tic и toc?
Ответ 1
Помимо 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
- все зависит от того, что вы хотите измерить.
Ответ 2
У меня был тот же вопрос, когда я перешел на 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...
Я надеюсь, что это будет полезно.
Ответ 3
Абсолютным лучшим аналогом 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
Обычно 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
, чтобы получить точное измерение.
Ответ 5
Я только что создал модуль [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()
Надеюсь, это поможет.
Ответ 6
На всякий случай кому-то интересно. Основываясь на всех остальных ответах, я написал класс tictoc, в котором есть лучшие из них.
Ссылка на github здесь.
Вы также можете использовать pip, чтобы получить его.
pip install ttictoc
Как использовать это:
Импортировать его
from ttictoc import TicToc
Использование выражения "с"
Без создания какого-либо объекта вы можете рассчитать время своего кода следующим образом.
with TicToc('name'):
some code...
# Prints the elapsed time
Или, создав объект, вы можете сделать то же самое.
t = TicToc('name')
with t:
some code...
# Prints the elapsed time
Вызов Tic Toc явно
Вы также можете явно вызвать Tic Toc, как показано ниже.
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.
- время или 0: время. время
- 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
Ответ 7
Посмотрите модуль timeit
.
Это не совсем эквивалентно, но если код, который вы хотите использовать, находится внутри функции, которую вы можете легко использовать.
Ответ 8
Я изменил @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
Ответ 9
Это также можно сделать с помощью обертки. Очень общий способ удержания времени.
Оболочка в этом примере кода обертывает любую функцию и печатает количество времени, необходимое для выполнения функции:
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()
Ответ 10
Основываясь на ответах Стефана и Антонимо, я закончил тем, что
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 не печатает прошедшее время, а возвращает его.)
Ответ 11
Обновление ответа Eli на 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, а также с возможностью добавления в файл журнала. Вы должны импортировать datetime, чтобы использовать функцию регистрации:
import time
import datetime
with Timer('Count', 'log.txt'):
for i in range(0,10_000_000):
pass