如何使用STL实现LFU缓存?

时间:2012-07-10 08:41:02

标签: c++ algorithm caching stl

我正在尝试使用纯STL实现LFU(Least Frequently Used)缓存(我不想使用Boost!)。

要求是:

  • 使用与Key类似的std::map对任何元素进行关联访问。
  • 能够释放优先级最低的项目(使用其UsesCount属性)。
  • 能够更新任何项目的优先级(UsesCount)。

问题是:

  • 如果我使用std::vector作为项目容器(KeyValueUsesCount),std::map作为关联向量的迭代器容器访问和std::make_heapstd::push_heapstd::pop_heap作为向量中的优先级队列实现,映射中的迭代器在堆操作后无效。
  • 如果我在之前的配置中使用std::list(或std::map)而不是std::vector,则std::make_heap等无法编译,因为它们的迭代器不支持aritmetic
  • 如果我想使用std::priority_queue,我无法更新项目优先级。

问题是:

  • 我是否遗漏了一些明显可以解决这个问题的方法?
  • 您能否指出一些符合以前要求的LFU缓存的纯C ++ / STL实现?

感谢您的见解。

2 个答案:

答案 0 :(得分:3)

使用*_heap函数和向量进行make实现似乎非常合适。虽然它会导致更新缓慢。您遇到的迭代器失效问题对于使用向量作为基础数据结构的每个容器都是正常的。这是boost::heap::priority_queue也采用的方法,但由于上述原因,它没有提供可变接口。其他boost::heap data-structures提供更新堆的功能。

似乎有些奇怪:即使您能够使用std::priority_queue,您仍将面临迭代器失效问题。

直接回答你的问题:你不会错过一些明显的东西。 std::priority_queue没有它应该有用的那么有用。最好的方法是编写自己的支持更新的堆实现。使其完全兼容STL(特别是分配器识别)是相当棘手的,而不是一个简单的任务。最重要的是,实现LFU缓存。

第一步,查看Boost实现以了解工作量。我不知道第二个参考实现。

要解决迭代器失效问题,您可以随时选择间接到另一个容器,尽管您应该尽量避免它,因为它会产生额外的成本并且会变得非常混乱。

答案 1 :(得分:1)

比保留两个数据结构更简单的方法:

  • 只需保留一张地图,该地图会将您的密钥映射到其值/使用次数对。
  • 当缓存已满:
    • 创建地图元素(O(n)
    • 的迭代器向量
    • 使用std::nth_element查找最差的10%(O(n)
    • 将其全部从地图中删除(O(n log n)

因此,向缓存添加新元素的常见情况为O(log n),最差情况为O(n log n),并且已摊销O(log n)

删除最差的10%可能在LFU缓存中有点激烈,因为新条目必须占据前90%或者它们被削减。然后,如果你只删除一个元素,那么新条目仍然需要在下一个新条目之前离开底部,或者它们被删除,并且它们没有时间这样做。所以取决于为什么LFU对你来说是正确的缓存策略,我对它的改变可能是错误的策略,或者它可能仍然没问题。