Matlab的tic和toc函数的Python等效项是什么?


111

Matlab的tic和toc函数的Python等效项是什么?


7
如果您真的想要直接等效项,只需调用tic = time.time()toc = time.time(),那么print toc-tic, 'sec Elapsed'正如下面的人们所说的那样,timeit它更强大。
乔·肯顿

使用@JoeKington的方法结合timeit.default_timer()似乎可以得到更好的结果,例如: tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer(),然后print toc-tic
littleO

1
pytictoc库似乎最方便,语法甚至比下面的ttictoc整洁。pypi.org/project/pytictoc
FlorianH

Answers:


172

除了timeitThiefMaster提到的以外,一个简单的方法就是(导入后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它更方便-取决于您要测量的内容。


25
@eat:敬请谅解。人们一直在使用unix time命令来测量程序的运行时间,这种方法在Python代码内部复制了此信息。只要它是完成这项工作的正确工具,我就不会觉得有什么毛病。timeit并非总是如此,探查器是满足大多数需求的重量级解决方案
Eli Bendersky

4
对于最后一行,我建议print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'。没有%.2f,很难理解。谢谢你的好主意。
CanKavaklıoğlu2013年

4
乍一看,这看起来非常方便,但是实际上需要缩进一个人想要的时间,这取决于代码块的长度和所选择的编辑器,这可能非常不便。仍然是一种优雅的解决方案,在嵌套使用的情况下可以正确运行。
Stefan

1
我想你要elapsed = t - time.time()而不是elapsed = time.time() - t。在后者中消逝将是负面的。我建议将此更改作为修改。
rysqui 2015年

3
@rysqui- 当前时间是否总是比以前的时间大?我认为这elapsed = time.time() - t是始终产生正值的形式。
Scott Smith,

32

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

而已!现在,我们已经准备好充分利用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...

我希望这会有所帮助。


22

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

6
在Matlab支持的嵌套使用tic和的情况下,这将无法正常工作toc。需要更多的复杂性。
Stefan

2
需要一些基本时间时,我已经在自己的代码中实现了类似的功能。但是,我将删除这import time两个函数的外部,因为这可能会花费一些时间。
Bas Swinckels

如果您坚持使用此技术,并且需要使用它来处理嵌套的tic / toc,请将全局列表tic设为列表,然后将其推入列表并从中toc弹出。
艾哈迈德·法西

1
我在其他地方也读过timeit.default_timer()比其更好的书,time.time()因为它time.clock()可能更适合于操作系统
Miguel

@AhmedFasih这就是我的回答,尽管还有更多事情可以改进。
antonimmo

15

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

由于这是通过将堆栈中的开始时间推入来进行的,因此对于tics和tocs的多个级别它都可以正常工作。它还允许更改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需要进行诸如in的平均策略才能获得忠实的度量。


1
@Stefan非常优雅,不能相信这是如此低的评分。谢谢!
thclark

10

您可以使用tictocttictoc。用安装

pip install ttictoc

然后按照以下步骤将它们导入您的脚本中

from ttictoc import tic,toc
tic()
# Some code
print(toc())

8

我刚刚创建了一个模块[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()

希望对您有所帮助。


可以很好地从MATLAB复制tic / toc!
马特

1
我必须警告您,当由多个模块同时使用时,这可能无法达到预期的效果,因为(AFAIK)模块的行为类似于单例。
antonimmo

3

看一下timeit模块。它不是真正等效的,但是如果您要计时的代码在函数内部,则可以轻松使用它。


是的,timeit最适合基准测试。它甚至不必是单个函数,您可以传递复杂的语句。

10
好吧,将不是一个极其简单的函数调用的代码作为字符串传递是非常丑陋的。
ThiefMaster


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

包装函数time称为装饰器。更详细的解释,在这里: medium.com/pythonhive/…–
Mircea

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

3
这种实现的问题是timer,如果在for循环后跟随其他任何代码,则不会在上一次调用之后将其删除。要获取最后一个计时器值,应删除或覆盖循环timer后的计时器for,例如通过timer = None
bastelflp

1
@bastelflp刚刚意识到我误解了您的意思...您的建议现在已包含在代码中。谢谢。
李少华

1

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

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

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不打印经过的时间,而是返回经过的时间。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.