#cache #lru #bloom #lfu

memory-cache-rust

memory-cache是一个以性能和正确性为重点构建的快速、并发缓存库。构建Ristretto的动机来自于在

1个不稳定版本

0.1.0-alpha2022年11月30日

#197 in 缓存

MIT 协议

115KB
2.5K SLoC

memory-cache-rust

memory-cache是一个以性能和正确性为重点构建的快速、并发缓存库。

构建memory-cache的动机来自于在Dgraph中需要无竞争的缓存。

功能

  • 高命中率 - 使用我们独特的准入/淘汰策略配对,Ristretto的性能在同类中最佳。
    • 淘汰:SampledLFU - 与精确LRU相当,在搜索和数据库跟踪上性能更好。
    • 准入:TinyLFU - 有一点内存开销(每个计数器12位),但性能提升。
  • 高速吞吐量 - 我们使用各种技术来管理竞争,结果是卓越的吞吐量。
  • 基于成本的淘汰 - 任何被认为有价值的大项可以淘汰多个较小的项(成本可以是任何东西)。
  • 完全并发 - 你可以使用尽可能多的线程,吞吐量下降很小。
  • 指标 - 可选的性能指标,包括吞吐量、HIT比率和其他统计数据。
  • 简单API - 只需确定你的理想Config值,然后就可以开始运行。

状态

Ristretto可用于生产,但仍在积极开发中。我们预计它将在不久的将来准备好用于生产。

用法

示例

use memory_cache_rust::bloom::hasher::{key_to_hash, cast_mut, value_to_int};
fn main() {
  let cache = Cache::with_config(
    Config {
      numb_counters: 1e7 as i64, // maximum cost of cache (1GB).
      max_cost: 1 << 30,// maximum cost of cache (1GB).
      buffer_items: 64,// number of keys per Get buffer.
      metrics: false,
      key_to_hash: key_to_hash,
      on_evict: None,
      cost: None,
    }
  );

  let guard = cache.guard();
  cache.set("key", "value1", 1, &guard);
  cache.set("key2", "value2", 1, &guard);
  cache.set("key3", "value3", 1, &guard);
  cache.set("key4", "value4", 1, &guard);
  thread::sleep(Duration::from_millis(50));

  assert_eq!(cache.get(&"key", &guard), Some("value1"));
  assert_eq!(cache.get(&"key2", &guard), Some("value2"));
  assert_eq!(cache.get(&"key3", &guard), Some("value3"));

  cache.del("key", &guard);
  thread::sleep(Duration::from_millis(10));
  let v = cache.get(&"key", &guard);
  assert_eq!(v, None);
}

配置

在创建Ristretto实例时,将Config结构传递给NewCache(如上例所示)。

NumCounters int64

NumCounters是要保留的4位访问计数器的数量,用于准入和淘汰。当缓存满时,我们将此设置为预期保留项数的10倍时看到良好的性能。

例如,如果每个项的成本为1且MaxCost为100,则将NumCounters设置为1,000。或者,如果您使用可变成本值但预计缓存满时将保留约10,000个项,则将NumCounters设置为100,000。重要的是在满的缓存中的唯一项数,而不一定是MaxCost值。

MaxCost int64

MaxCost决定了驱逐决策。例如,如果MaxCost是100,并且一个成本为1的新项目将总缓存成本增加到101,则会驱逐1个项目。

MaxCost还可以用来表示字节的最大大小。例如,如果MaxCost是1,000,000(1MB),且缓存已满,有1,000个1KB的项目,一个新接受的项目将导致5个1KB的项目被驱逐。

MaxCost可以是任何值,只要它与调用Set时使用的成本值匹配。

BufferItems int64

BufferItems是Get缓冲区的大小。我们找到的最佳值是64。

如果在某些原因下你看到Get性能随着大量竞争(你不应该)而下降,尝试以64的倍数增加此值。这是一个微调机制,你可能不必接触它。

Metrics bool

当你想要实时记录各种统计信息时,Metrics为true。这是因为这是一个配置标志,存在10%的吞吐量性能开销。

OnEvict func(hashes [2]uint64, value interface{}, cost int64)

对于每次驱逐,都会调用OnEvict。

KeyToHash func(key interface{}) [2]uint64

KeyToHash是用于每个键的哈希算法。如果这是nil,

注意,如果你想使用128位哈希,你应该使用完整的[2]uint64,否则只需在uint640位置填充,它将像任何64位哈希一样运行。

Cost func(value interface{}) int64

Cost是一个可选函数,你可以将其传递给Config,以在运行时评估项目成本,并且仅用于不丢弃的Set调用(如果计算项目成本特别昂贵,并且你不希望浪费时间在将被丢弃的项目上,这很有用)。

要通知Ristretto你想要使用此Cost函数

  1. 将Cost字段设置为一个非nil函数。
  2. 在调用Set为新项目或项目更新时,使用cost为0。

依赖项

~6–13MB
~127K SLoC