Python timeit.timeit在语句之间执行不定时的操作

时间:2017-04-03 20:49:14

标签: python performance caching time

我有一个递归函数,上面有一个functools.lru_cache()装饰器。我想多次从空缓存执行它。一种方法是每次清除缓存,但这包括缓存清除时间。

这就是我目前所拥有的:

@functools.lru_cache(maxsize=None)
def recursive_function(n):
    # ...
    return sum(recursive_function(n - i) * b for i, b in enumerate(other_list))

def time(number, n):
    return timeit.timeit(
        'f({}); f.cache_clear()'.format(n),
        'from {} import recursive_function as f'.format(__name__),
        number=number
    )

我仍然希望用 缓存来计时,因为缓存会以指数方式减少时间,但是在调用一次之后再次调用它需要〜0ms,因为它只是获取缓存值

有没有办法在timeit.timeit中的陈述之间做某事而不计算时间?或者在cache_clear之前暂停计时器?

1 个答案:

答案 0 :(得分:1)

您可以生成该函数的number个副本,每个副本都带有独立的 lru_cache包装器:

setup = '''\
    from {name} import recursive_function as f
    f = iter([
        functools.lru_cache(maxsize=None)(recursive_function.__wrapped__)
        for _ in range({number})])
    n = {n}
    next_ = next
'''.format(name=__name__, number=number, n=n)
test = '''\
    recursive_function = next_(f)
    recursive_funcion.__globals__['recursive_funcion'] = recursive_funcion
    recursive_function(n)
'''
return timeit.timeit(test, setup, number=number)

安装程序创建number单独修饰的函数对象预先,每个函数对象都有一个不同的LRU缓存,并为此创建一个迭代器。然后,测试使用next()函数获取下一个可用的函数对象,并将其用于测试。

每次都必须替换当前的全局名称recursive_function,否则递归调用将无法找到新的装饰版本。这有点不利,不要运行计时器,并期望缓存在之后是空的(它将包含最后一次测试运行的结果)。

这是因为:

  1. 原始的未缓存功能仍可用作recursive_function.__wrapped__
  2. 装饰器语法只是调用装饰器对象以产生新函数对象的语法糖。
  3. 这为每个单独的测试提供了一个干净的缓存,只需要很少的开销(只有一个next()调用,它已绑定到本地以避免全局名称查找惩罚)。