5个不稳定版本
0.4.3 | 2024年1月2日 |
---|---|
0.4.0 | 2023年1月18日 |
0.3.2 | 2022年12月2日 |
0.2.1 | 2022年1月1日 |
0.2.0 | 2021年12月24日 |
#347 在 缓存
11,978 每月下载量
在 15 个Crates中使用(通过 memoize)
21KB
365 代码行
memoize
为相对简单的Rust函数提供一个#[memoize]
属性:也就是说,具有一个或多个可Clone
的参数,以及一个可Clone
的返回类型的函数。就这么简单。
新闻:该库已更新,因此您无需单独导入lru
、lazy_static
和其他依赖项。现在应该可以自动工作。请记住启用full
功能以使用LRU缓存和其他附加功能。
阅读文档(cargo doc --open
)以了解详细信息,或查看examples/
,如果您想了解更多信息
// From examples/test2.rs
use memoize::memoize;
#[memoize]
fn hello(arg: String, arg2: usize) -> bool {
arg.len()%2 == arg2
}
fn main() {
// `hello` is only called once here.
assert!(! hello("World".to_string(), 0));
assert!(! hello("World".to_string(), 0));
// Sometimes one might need the original function.
assert!(! memoized_original_hello("World".to_string(), 0));
}
这可以扩展为(经过一些简化)
std::thread_local! {
static MEMOIZED_MAPPING_HELLO : RefCell<HashMap<(String, usize), bool>> = RefCell::new(HashMap::new());
}
pub fn memoized_original_hello(arg: String, arg2: usize) -> bool {
arg.len() % 2 == arg2
}
#[allow(unused_variables)]
fn hello(arg: String, arg2: usize) -> bool {
let ATTR_MEMOIZE_RETURN__ = MEMOIZED_MAPPING_HELLO.with(|ATTR_MEMOIZE_HM__| {
let mut ATTR_MEMOIZE_HM__ = ATTR_MEMOIZE_HM__.borrow_mut();
ATTR_MEMOIZE_HM__.get(&(arg.clone(), arg2.clone())).cloned()
});
if let Some(ATTR_MEMOIZE_RETURN__) = ATTR_MEMOIZE_RETURN__ {
return ATTR_MEMOIZE_RETURN__;
}
let ATTR_MEMOIZE_RETURN__ = memoized_original_hello(arg.clone(), arg2.clone());
MEMOIZED_MAPPING_HELLO.with(|ATTR_MEMOIZE_HM__| {
let mut ATTR_MEMOIZE_HM__ = ATTR_MEMOIZE_HM__.borrow_mut();
ATTR_MEMOIZE_HM__.insert((arg, arg2), ATTR_MEMOIZE_RETURN__.clone());
});
r
}
附加功能
如上例所示,每个线程默认都有自己的缓存。如果您希望每个线程共享相同的缓存,您可以像下面这样指定SharedCache
选项,以便将缓存包装在std::sync::Mutex
中。例如
#[memoize(SharedCache)]
fn hello(key: String) -> ComplexStruct {
// ...
}
您可以选择使用LRU缓存。事实上,如果您知道被缓存的函数有无限多的不同输入,您应该这样做!在这种情况下,使用如下属性
// From examples/test1.rs
// Compile with --features=full
use memoize::memoize;
#[derive(Debug, Clone)]
struct ComplexStruct {
// ...
}
#[memoize(Capacity: 123)]
fn hello(key: String) -> ComplexStruct {
// ...
}
增加更多缓存和配置选项相对简单,只需解析属性参数。目前,如果您未启用具有 full
功能,使用例如 Capacity
这样的参数进行编译将会失败。
另一个参数是 TimeToLive,指定缓存值允许存活的时间。
#[memoize(Capacity: 123, TimeToLive: Duration::from_secs(2))]
还可以使用 chrono::Duration
,但需要首先将其转换为 std::time::Duration
。
#[memoize(TimeToLive: chrono::Duration::hours(3).to_std().unwrap())]
缓存值永远不会超过提供的持续时间,而是在下一个请求时重新计算。
您还可以指定一个自定义哈希器,例如使用 CustomHasher
的 AHash。
#[memoize(CustomHasher: ahash::HashMap)]
由于某些哈希器的初始化函数不是 new()
,您可以通过指定一个 HasherInit
函数调用。
#[memoize(CustomHasher: FxHashMap, HasherInit: FxHashMap::default())]
有时,您不能或不想将数据作为缓存的一部分存储。在这些情况下,您可以使用 #[memoize]
宏中的 Ignore
参数来忽略一个参数。任何被 Ignore
的参数不再需要是可 Clone
的,因为它们不是作为参数集的一部分存储的,并且更改一个被 Ignore
的参数不会触发再次调用函数。您可以通过多次指定 Ignore
参数来忽略多个参数。
// `Ignore: count_calls` lets our function take a `&mut u32` argument, which is normally not
// possible because it is not `Clone`-able.
#[memoize(Ignore: count_calls)]
fn add(a: u32, b: u32, count_calls: &mut u32) -> u32 {
// Keep track of the number of times the underlying function is called.
*count_calls += 1;
a + b
}
刷新
如果您对函数 f
进行了记忆化,将会有一个名为 memoized_flush_f()
的函数,允许您清除记忆化缓存。
贡献
...总是受欢迎!这将是我的第一个过程宏 crate,我非常感激对功能和风格的改进。请发送一个 pull request,如果需要一段时间才能审查,请不要气馁;我有时在这里会慢一些 :) -- Lewin
依赖
~1.5MB
~36K SLoC