我有一个递归函数,上面有一个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之前暂停计时器?
答案 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
,否则递归调用将无法找到新的装饰版本。这有点不利,不要运行计时器,并期望缓存在之后是空的(它将包含最后一次测试运行的结果)。
这是因为:
recursive_function.__wrapped__
这为每个单独的测试提供了一个干净的缓存,只需要很少的开销(只有一个next()
调用,它已绑定到本地以避免全局名称查找惩罚)。