Python @lru_缓存装饰器缓存未命中过多
如何配置Python @lru_缓存装饰器缓存未命中过多,python,function,caching,memoization,functools,Python,Function,Caching,Memoization,Functools,如何配置lru\u cache根据接收到的实际值为其缓存设置键,而不是如何调用函数 >>> from functools import lru_cache >>> @lru_cache ... def f(x=2): ... print("reticulating splines...") ... return x ** 2 ... >>> f() reticulating splines... 4 >
lru\u cache
根据接收到的实际值为其缓存设置键,而不是如何调用函数
>>> from functools import lru_cache
>>> @lru_cache
... def f(x=2):
... print("reticulating splines...")
... return x ** 2
...
>>> f()
reticulating splines...
4
>>> f(2)
reticulating splines...
4
>>> f(x=2)
reticulating splines...
4
换句话说,只有上面的第一个调用应该是缓存未命中,另外两个应该是缓存命中。要做到这一点,您必须经历将参数绑定到形式参数的过程。实际的实现过程是在没有公共接口的C代码中实现的,但是在中有一个(慢得多的)重新实现。这比正常使用
functools.lru\u缓存大约慢100倍:
import functools
import inspect
def mycache(f=None, /, **kwargs):
def inner(f):
sig = inspect.signature(f)
f = functools.lru_cache(**kwargs)(f)
@functools.wraps(f)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()
return f(*bound.args, **bound.kwargs)
return wrapper
if f:
return inner(f)
return inner
@mycache
def f(x):
print("reticulating splines...")
return x ** 2
如果该方法的性能损失太大,您可以改为使用以下技巧,这需要更多的代码复制,但运行速度要快得多,仅比正常情况下使用lru_cache
慢2倍左右(有时更快,带有关键字参数):
这使用了更快的C级参数绑定来规范化对已记忆助手函数的调用,但需要复制函数的参数3次:一次在外部函数的签名中,一次在助手的签名中,并且在调用帮助程序时执行一次。do state:Distinct参数模式可能被认为是具有单独缓存项的不同调用。例如,f(a=1,b=2)和f(b=2,a=1)的关键字参数顺序不同,可能有两个单独的缓存项。看起来您可以使用f.cache_info()
方法查看实际的缓存命中/未命中。我知道,但我认为这不是一个合理的默认行为-这些调用在所有实际用途上都是相同的。我希望以某种方式包装(或替换)lru缓存,以避免同一基础调用的所有不同拼写的缓存未命中。@bnaecker:不,因为问题试图实现的行为取决于函数签名,而函数签名是make_key
不知道的。为什么是f()
和f(x=2)
不一样吗?在这两种情况下,args=()
和kwds={'x':2}
不是吗?@mkrieger1:no。默认值不是关键字参数。效果很好。你的C怎么样?是否有兴趣将CPython PR直接添加到lru\u缓存中的“normalize”参数组合在一起?
@functools.lru_cache
def _f(x):
print("reticulating splines...")
return x ** 2
def f(x=2):
return _f(x)