一尘不染

使用C ++的最近最少使用的缓存

algorithm

我正在尝试使用C ++实现LRU
Cache。我想知道什么是实现它们的最佳设计。我知道LRU应该提供find(),添加一个元素并删除一个元素。删除操作应删除LRU元素。最好的ADT是什么来实现的?例如:如果我使用一个元素为值且时间计数器为键的映射,则可以搜索O(logn)时间,插入为O(n),删除为O(logn)。


阅读 167

收藏
2020-07-28

共1个答案

一尘不染

LRU高速缓存的一个主要问题是很少有“ const”操作,大多数操作都会更改基础表示(如果仅因为它们改变了所访问的元素)。

这当然很不方便,因为这意味着它不是传统的STL容器,因此任何展示迭代器的想法都非常复杂:取消引用迭代器时,这是一个访问权限,应修改我们要迭代的列表…天啊。

并且在速度和内存消耗方面都有性能方面的考虑。

不幸的是,但是您需要某种方式来将数据组织到队列(LRU)中(可以从中间删除元素),这意味着您的元素必须彼此独立。一std::list配合,当然,但比你需要更多。在这里,单链接列表就足够了,因为您不需要向后迭代列表(毕竟,您只需要一个队列)。

但是,这些方法的一个主要缺点是它们的引用局部性差,如果需要更高的速度,则需要为节点提供自己的自定义(pool?)分配器,以使它们尽可能地靠近。这也将在某种程度上减轻堆碎片。

接下来,您显然需要一个索引结构(用于缓存位)。最自然的是转向哈希表。std::tr1::unordered_mapstd::unordered_map或者boost::unordered_map通常是高质量的实施方案,因此您应该可以使用某些方案。他们还为哈希冲突处理分配了额外的节点,您可能更喜欢其他种类的哈希图,查阅Wikipedia上有关该主题的文章,并了解各种实现技术的特征。

继续,有(显而易见的)线程支持。如果不需要线程支持,那很好,但是如果需要,它会稍微复杂一些:

  • 就像我说的那样,const对这种结构的操作很少,因此您实际上不需要区分读/写访问
  • 内部锁定很好,但是您可能会发现它与您的使用效果不佳。内部锁定的问题在于它不支持“事务”的概念,因为它放弃了每次调用之间的锁定。如果是这种情况,请将您的对象转换为互斥体并提供一个std::unique_ptr<Lock> lock()方法(在调试中,您可以断言在每个方法的入口处都将获得锁定)
  • (在锁定策略中)存在重入问题,即能够从同一线程内“重新锁定”互斥锁,请检查Boost.Thread以获取有关可用的各种锁和互斥锁的更多信息。

最后,还有错误报告的问题。由于预期缓存可能无法检索您放入的数据,因此我将考虑使用“味道差”的异常。考虑指针(Value*)或Boost.Optionalboost::optional<Value&>)。我更喜欢Boost.Optional,因为它的语义很明确。

2020-07-28