【问题标题】:What is the Python equivalent of Matlab's tic and toc functions?Matlab 的 tic 和 toc 函数的 Python 等价物是什么?
【发布时间】:2011-08-16 12:39:48
【问题描述】:

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

【问题讨论】:

  • 如果你真的想要直接等效,只需调用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
  • pytictoc 库似乎最方便,语法甚至比下面的 ttictoc 更简洁。 pypi.org/project/pytictoc

标签: python matlab timing


【解决方案1】:

您可以使用来自ttictoctictoc。安装它

pip install ttictoc

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

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

【讨论】:

    【解决方案2】:
    pip install easy-tictoc
    

    在代码中:

    from tictoc import tic, toc
    
    tic()
    
    #Some code
    
    toc()
    

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

    【讨论】:

    【解决方案3】:

    除了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 更方便 - 这完全取决于您要测量的内容。

    【讨论】:

    • @eat:我不同意。人们一直在使用 unix time 命令来测量程序的运行时间,这种方法在 Python 代码中复制了这一点。我认为它没有任何问题,只要它是适合这项工作的工具。 timeit 并不总是这样,探查器是满足大多数需求的重量级解决方案
    • 对于最后一行,我建议print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'。没有 %.2f 就很难理解。谢谢你的好主意。
    • 这乍一看看起来很方便,但实际上需要缩进想要时间的代码块,根据代码块的长度和选择的编辑器,这可能会很不方便。仍然是一个优雅的解决方案,在嵌套使用的情况下表现正确。
    • @rysqui - 当前时间不是总是比上一个时间大吗?我认为elapsed = time.time() - t 是始终产生正值的形式。
    • @ScottSmith 嗯。是的,我不知道两年前我写那条评论时在想什么。这似乎超级错误,而你似乎超级正确。不知道我在想什么。
    【解决方案4】:

    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
    

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        我稍微改了一下@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
        

        【讨论】:

        • 这个实现的一个问题是,timer 在最后一次调用后不会被删除,如果在for 循环之后有任何其他代码。要获得最后一个计时器值,应该在 for 循环之后删除或覆盖 timer,例如通过timer = None.
        • @bastelflp 刚刚意识到我误解了你的意思......你的建议现在已被纳入代码中。谢谢。
        【解决方案7】:

        我刚刚创建了一个模块 [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!
        • 我必须警告您,当由多个模块同时使用时,这可能不会按预期运行,因为(AFAIK)模块的行为类似于单例。
        【解决方案8】:

        这也可以使用包装器来完成。非常通用的计时方式。

        此示例代码中的包装器包装任何函数并打印执行该函数所需的时间量:

        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】:

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

        我希望这会有所帮助。

        【讨论】:

        • 我觉得这个答案被严重低估了。非常感谢您的分享!
        【解决方案10】:

        通常,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())
        

        由于这是通过将开始时间推入堆栈来实现的,因此它可以在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-方法的用处实际上仅限于执行时间超过 10 微秒的代码块。在此之下,需要像timeit 这样的平均策略才能获得忠实的测量结果。

        【讨论】:

        • 非常优雅,@Stefan - 不敢相信这是这么低的评价。谢谢!
        【解决方案11】:

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

        【讨论】:

        • 这在 Matlab 支持的 tictoc 的嵌套使用的情况下将无法正常运行。需要更复杂一点。
        • 当我需要一些基本时间时,我已经在自己的代码中实现了类似的功能。但是,我会在这两个函数之外删除 import time,因为它可能需要相当长的时间。
        • 如果你坚持使用这种技术,并且需要它来处理嵌套的 tic/toc,请将全局设为一个列表,然后让 tic 推送到它并让 toc 从中弹出。跨度>
        • 另外我在别处读到timeit.default_timer()time.time() 更好,因为time.clock() 可能更合适,具体取决于操作系统
        • @AhmedFasih 这就是我的回答,虽然还有更多的地方可以改进。
        【解决方案12】:

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

        【讨论】:

        • 是的,timeit 最适合基准测试。它甚至不必是单个函数,您可以传递任意复杂的语句。
        • 嗯,将不是极其简单的函数调用的代码作为字符串传递是非常难看的。
        • 它也适用于这样的 lambdas(直接来自文档):timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000)
        猜你喜欢
        • 1970-01-01
        • 2014-01-22
        • 2011-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-15
        • 2022-11-11
        • 2014-07-03
        相关资源
        最近更新 更多