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

Python 等价于 Matlab 的 Tic 和 toc 函数是什么?

207013 次浏览

看看 timeit模块。 它不是真正等价的,但是如果你想计时的代码在一个函数中,你可以很容易地使用它。

除了 ThiefMaster 提到的 timeit之外,一个简单的方法就是(在导入 time之后) :

t = time.time()
# do stuff
elapsed = time.time() - t

我有一个我喜欢使用的 helper 类:

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更方便-这完全取决于你想测量什么。

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的话)很好地满足了我的分析需求。然而,当我试图分析交互驱动的计算时,例如,通过用户在 GUI 中的鼠标运动,就出现了类 tic-toc功能的用例。我觉得在源代码中发送 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())

因为这是通过在堆栈上推送开始时间来工作的,所以对于多个级别的 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这样的平均策略需要得到一个可靠的测量。

当我从 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...

我希望这对你有帮助。

我稍微修改了@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 (“ blahbrah”)放在某个本地作用域的开头。运行时间将打印在范围的最后:

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

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

希望能有所帮助。

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

这边请

  • 你可以像在 Matlab 一样简单地使用 tic()toc()
  • 或者,您可以命名它们: tic(1)toc(1)tic('very-important-block')toc('very-important-block')和不同名称的计时器不会干扰
  • 以这种方式导入它们可以防止使用它的模块之间的干扰。

(这里 toc 不打印运行时间,而是返回它。)

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)

和伊莱的一样,它也可以用作上下文管理器:

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

产出:

[Count] Elapsed: 0.27 seconds

我还更新了它,以打印报告的时间单位(秒)和修剪数字的数字,因为可以建议,并与选项也附加到日志文件。您必须导入 datetime 才能使用日志特性:

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

可以使用 ttictoc中的 tictoc

pip install ttictoc

然后将它们导入到脚本中,如下所示

from ttictoc import tic,toc
tic()
# Some code
print(toc())
pip install easy-tictoc

密码是:

from tictoc import tic, toc


tic()


#Some code


toc()

免责声明: 我是这个图书馆的作者。

这个解决方案可以满足我的侧写需求:

from time import time
import inspect


def tic():
tic.t = time()


def toc(message=None):
time_elapsed = time() - tic.t
if message is None:
message = inspect.currentframe().f_back.f_lineno
print(message, time_elapsed)
tic.t = time()
return time_elapsed

然后,您可以在代码中粘贴大量的 toc(),这样就有了一个非常强大的分析器。(消息默认为文件中调用者的代码行)