Tic, toc функции аналоговые в Python
Что является лучшим аналогом программы MATLAB ТИЦ и функции оглавление ( http://www.mathworks.com/help/techdoc/ref/tic.html) в Python?
11 ответов:
помимо
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- все зависит от того что вы хотите измерить.
у меня был тот же вопрос, когда я перешел на 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...Я надеюсь, что это полезно.
абсолютным лучшим аналогом 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()
как правило, оболочкой 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заявление для отображения дополнительной информации, которая мне понравилась о класса.по какой-то причине меня беспокоили накладные расходы на чистую реализацию 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необходимы, что получают точное измерение.
Я только что создал модуль [tictoc.py] для достижения вложенных tic TOC, что и делает 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()Я надеюсь, что это помогает.
посмотреть
timeitмодуль. Это не совсем эквивалентно, но если код, который вы хотите раз внутри функции вы можете легко использовать его.
на всякий случай, если кто-то заинтересован. На основе всех других ответов я написал класс 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 явно
вы также можете вызвать tic toc explicitply, как показано ниже.
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 аргумента: имя, метод и отступ.
- имя: это имя объекта. Это не требуемый.
- метод: указывает, какой метод следует использовать для получения времени.
- отступ: позволяет использовать один и тот же объект несколько раз, в разных вмятины на время.
аргумент метода может быть либо 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: Время.perf_counter_ns - process_time_ns или 5: Время.process_time_ns
в случае, если вы предпочитаете использовать другой метод, который вы просто делаете (используя в качестве примера время.часы:
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
Это также можно сделать с помощью оболочки. Очень общий способ ведения времени.
обертка в этом примере кода обертывает любую функцию и выводит количество времени, необходимое для выполнения функции:
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()
Я немного изменил ответ @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)чтобы использовать, простой таймер put ("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
основываясь на ответах Стефана и антониммо, я закончил тем, что поставил
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 не печатает прошедшее время, но возвращает его.)
обновление ответ Эли на 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
Comments