#memoization #cache #lru-cache #proc-macro

memoize

用于自动缓存具有相对简单签名的函数的属性宏

19个版本

0.4.2 2023年12月23日
0.4.1 2023年10月27日
0.4.0 2023年1月18日
0.3.3 2022年12月2日
0.1.4 2020年10月16日

#19缓存

Download history 4026/week @ 2024-04-22 4043/week @ 2024-04-29 4152/week @ 2024-05-06 3946/week @ 2024-05-13 4444/week @ 2024-05-20 4306/week @ 2024-05-27 3922/week @ 2024-06-03 3099/week @ 2024-06-10 2764/week @ 2024-06-17 3105/week @ 2024-06-24 2540/week @ 2024-07-01 3148/week @ 2024-07-08 2847/week @ 2024-07-15 3283/week @ 2024-07-22 3737/week @ 2024-07-29 3773/week @ 2024-08-05

每月下载量 13,869
14 个 Crates 中使用 (7 直接)

MIT 许可证

10KB

memoize

Docs.rs Crates.rs CI

#[memoize] 属性,用于相对简单的 Rust 函数:也就是说,具有一个或多个可 Clone 的参数,以及一个可 Clone 的返回类型。就是这样。

更新日志:该包已更新,因此您不需要单独导入 lrulazy_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 缓存。实际上,如果您知道 memoized 函数具有无界数量的不同输入,您应该这样做!在这种情况下,使用如下属性

// 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())]

缓存值将永远不会超过提供的持续时间,而是在下一次请求时重新计算。

您还可以指定一个 自定义哈希器,例如使用 CustomHasherAHash

#[memoize(CustomHasher: ahash::HashMap)]

由于一些散列函数初始化的不是 new(),您可以指定一个 HasherInit 函数调用。

#[memoize(CustomHasher: FxHashMap, HasherInit: FxHashMap::default())]

有时,您可能无法或不希望将数据作为缓存的一部分存储。在这些情况下,您可以在 #[memoize] 宏中使用 Ignore 参数来忽略一个参数。任何被 Ignore 的参数不再需要是 Clone-able 的,因为它们不是作为参数集的一部分存储的,并且更改一个 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() 的函数,允许您清除记忆化缓存。

贡献

...总是受欢迎的!这是我第一个过程宏存储库,我对功能和风格的改进表示感激。请发送一个拉取请求,如果我的审查需要一段时间,请不要气馁;我有时在这里有点慢 :) -- Lewin

依赖关系

~1.2–1.8MB
~40K SLoC