如何在非结构体方法上实现缓存的惯用方式是什么?

9

我有一个昂贵的函数,像这样:

pub fn get_expensive_value(n: u64): u64 {
   let ret = 0;
   for 0 .. n {
       // expensive stuff
   }
   ret
}

这个函数会频繁地使用相同的参数进行调用。它是纯函数,这意味着它将返回相同的结果并且可以使用缓存。

如果这是一个结构体方法,我会添加一个充当缓存的成员到结构体中,但它不是。所以我的选择似乎是使用静态变量:

static mut LAST_VAL: Option<(u64, u64)> = None;

pub fn cached_expensive(n: u64) -> u64 {
   unsafe {
       LAST_VAL = LAST_VAL.and_then(|(k, v)| {
           if k == n {
              Some((n,v))
           } else {
              None
           }
       }).or_else(|| {
           Some((n, get_expensive_value(n)))
       });
       let (_, v) = LAST_VAL.unwrap();
       v
   }
}

现在,我不得不使用unsafe。我可以在const中放置一个RefCell,而不是static mut。但我并不认为这样更安全-它只是避免了使用unsafe块。我考虑过使用Mutex,但我认为这也无法保证线程安全。
重新设计代码以使用结构体进行存储不是一个真正的选择。

重复的问题:如何创建一个全局可变单例? - Shepmaster
我不认为这是一个重复的问题。我的问题特别涉及缓存,虽然我的起点是一个全局的、可变的单例,但这只是偶然的,一个好的解决方案可能会解释如何(例如)使用RefCellMutex可以使其更好,或者提供完全不同的结构替代方案。 - Peter Hall
我不同意,但在其他人同意之前我不会使用魔法重复锤。简而言之,没有任何其他地方可以存储数据。要么你传入一个存储位置(通过明确的参数或隐式地通过self),要么它必须存储在全局存储中。后者有一个限制,就是你需要处理对缓存的并发访问。 - Shepmaster
虽然我现在还无法推理出来,但我有一种感觉,即问题中提出的“unsafe”块没有维护其所要求的安全性保证。例如,我非常确定对该函数的并发调用可能会对存储进行部分读取/写入(因为没有相互排斥),从而在任意情况下导致奇怪的行为。 - Shepmaster
@Shepmaster 是的,这正是我担心的问题,这也促使我发布了这个问题。我已经阅读了你链接的重复问题,并且它非常有帮助。Arc<Mutex<..>> 的构造看起来就是我需要的。但我会重新考虑是否可以用不同的方式实现。由于我正在处理一个现有的代码库,所以情况并不那么简单。 - Peter Hall
显示剩余2条评论
2个回答

8

我认为最好的替代方案是使用带有互斥锁的全局变量。使用lazy_static可以使其更加便捷,并允许在函数内部进行“全局”声明。

pub fn cached_expensive(n: u64) -> u64 {
    use std::sync::Mutex;
    lazy_static! {
        static ref LAST_VAL: Mutex<Option<(u64, u64)>> = Mutex::new(None);
    }
    let mut last = LAST_VAL.lock().unwrap();
    let r = last.and_then(|(k, v)| {
        if k == n {
            Some((n, v))
        } else {
            None
        }
    }).or_else(|| Some((n, get_expensive_value(n))));
    let (_, v) = r.unwrap();
    *last = r;
    v
}

你同意这是一个重复的问题,与评论中建议的如何创建全局、可变单例?相同吗? - Shepmaster
1
是的。但我决定写下这个答案来展示全局变量可以在函数内部声明,因此其作用域是局部的。 - malbarbo

7

您还可以查看cached项目/箱。它使用简单的宏将函数缓存起来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接