什么是Matlab的tic和toc函数的Python等价物?

时间:2011-05-01 16:46:35

标签: python matlab timing

Matlab的tic and toc functions的等价物是什么?

12 个答案:

答案 0 :(得分:133)

除了ThiefMaster提到的timeit之外,一个简单的方法就是(在导入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更方便 - 这一切都取决于你想要衡量的内容。

答案 1 :(得分:23)

当我从Matlab迁移到python时,我遇到了同样的问题。在这个线程的帮助下,我能够构建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)

就是这样!现在我们已准备好像在Matlab中一样充分使用tic()toc()。例如

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

我希望这有用。

答案 2 :(得分:18)

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()

答案 3 :(得分:11)

通常,IPython的%time%timeit%prun%lprun(如果安装了line_profiler)可以很好地满足我的分析需求。但是,当我尝试剖析交互式驱动的计算时,即用户在GUI中的鼠标移动时,就出现了tic-toc类似功能的用例。我觉得在源代码中发送tictoc垃圾邮件,而交互式测试则是揭示瓶颈的最快方法。我选择了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())

由于这可以通过在堆栈上按下开始时间来实现,因此它可以在tictoc的多个级别上正常工作。它还允许我们更改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 - 方法的用处实际上仅限于执行时间超过10微秒的代码块。在此之下,需要像timeit这样的平均策略才能获得忠实的衡量标准。

答案 4 :(得分:5)

我刚刚创建了一个模块[tictoc.py]来实现嵌套的tic tocs,这就是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()

我希望它有所帮助。

答案 5 :(得分:4)

以防万一有人感兴趣。根据所有其他答案,我编写了一个tictoc类,该类具有所有优点。

github上的链接为here.

您也可以使用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。

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

如果您想对代码的多个级别进行计时,也可以通过将'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: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

答案 6 :(得分:3)

查看timeit模块。 它并不是真正等效的,但如果您想要的时间代码在函数内,您可以轻松使用它。

答案 7 :(得分: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)

使用,简单地将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

答案 8 :(得分: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()

答案 9 :(得分:0)

在Stefan和antonimmo的答案的基础上,我最终放了

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不会打印已用时间,但会返回它。)

答案 10 :(得分:0)

Eli's answer更新为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的建议修整位数,并可以选择附加到日志文件中。您必须导入日期时间才能使用日志记录功能:

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

答案 11 :(得分:0)

pip install easy-tictoc

在代码中:

from tictoc import tic, toc

tic()

#Some code

toc()

免责声明:我是该库的作者。