Published: 2016-11-08

计算运行时间的timeit

Table of Contents

1 timeit的功能和用法

timeit 模块提供了测试一小段代码运行时间的功能。我前面有一篇文章用它来测试定义 __slots__ 对对象访问速度的提升情况,参见这里官方文档 上有下面这样的使用例子:

# 从命令行调用
python -m timeit '"-".join([str(n) for n in range(100)])'

# 从REPL调用
>>> import timeit
>>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
0.7288308143615723

使用 timeit 的时候可以直接调用其定义好的 timeit.timeit , timeit.repeat , timeit.default_timer 方法,或者定义一个类 timeit.Timer ,使用本身的方法。

# API

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000)

timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=3, number=1000000)

class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>)
  • timeit() 接受的第一个参数stmt为要计算时间的表达式,第二个参数setup为初始化的表达式。
  • timer参数为计时器,在不同平台默认不一样,参照这里 ,但不管是Unix还是Windows测得的时间都是系统经过的时间,所以是有可能被正在运行的其它程序影响的。
  • number参数是表达式执行的次数,所以这里返回的时间是执行1000000次总和的时间。
  • timeit.repeat() 中的repeat参数是测试重复执行的次数,相当于调用number次的timeit(),number默认为3,返回3次运行时间的列表,通常可以取min()来评估程序的执行时间。
  • timeit.repeat() 中除了最小值其他的值得参考意义可能不大,因为有可能是受到系统中其它程序影响导致的
  • 注意python版本不同导致的接口参数不同,这里默认采用python2.7版本

接下来看一些复杂点的示例

# 多行表达式的调用
>>> import timeit
>>> attribute is missing
>>> s = """\
... try:
...     str.__nonzero__
... except AttributeError:
...     pass
... """
>>> timeit.timeit(stmt=s, number=100000)
0.9138244460009446


# 测试函数调用时间
def test():
    """Stupid test function"""
    L = []
    for i in range(100):
        L.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

2 其它

当我在网上浏览timeit相关资源时看到了这篇文章, 上面提到了通过上下文管理器(context manager)实现一个计时器,利用了变量的生命周期相关特性(#TODO),虽然就像作者所说,可以加上其它的特性比如循环次数等以及更优雅的实现一个计时器,代码如下,可供参考:

# Source code: https://gist.github.com/pengmeng/78a25663c20ab8890b81
__author__ = 'mengpeng'
import time


class timeme(object):
    __unitfactor = {'s': 1,
                    'ms': 1000,
                    'us': 1000000}

    def __init__(self, unit='s', precision=4):
        self.start = None
        self.end = None
        self.total = 0
        self.unit = unit
        self.precision = precision

    def __enter__(self):
        if self.unit not in timeme.__unitfactor:
            raise KeyError('Unsupported time unit.')
        self.start = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.time()
        self.total = (self.end - self.start) * timeme.__unitfactor[self.unit]
        self.total = round(self.total, self.precision)

    def __str__(self):
        return 'Running time is {0}{1}'.format(self.total, self.unit)

运行示例:

from timeme import timeme
with timeme('ms', 6) as t:
    result = sum(range(100000))

print(t) # Running time is 5.2948ms
print(t.total) # 5.2948

3 参考资料

Author: Nisen

Email: imnisenATgmailDOTcom

Emacs 25.2.1 (Org mode 8.2.10)