【问题标题】:LRU implementation in production code生产代码中的 LRU 实现
【发布时间】:2011-01-04 16:07:50
【问题描述】:

我有一些 C++ 代码需要使用 LRU 技术实现缓存替换。
到目前为止,我知道两种实现 LRU 缓存替换的方法:

  1. 每次访问缓存数据时使用timeStamp,最后比较替换时的timeStamp。
  2. 使用一堆缓存项并将它们移动到顶部,如果它们最近被访问过,那么最后底部将包含 LRU 候选。

那么,哪些更适合用于生产代码?
他们还有其他更好的方法吗?

【问题讨论】:

  • 你所说的“更好”是什么意思 - 你必须指定你的标准是什么。另外,看看这个问题stackoverflow.com/questions/1935777/…
  • 您可以使用整数来代替时间戳记。当一个元素被访问时,将其设为 0 并增加其他轨道
  • @user 这将是一个糟糕的设计,因为它会使访问成本O(n)

标签: c++ algorithm lru


【解决方案1】:

article 描述了使用一对 STL 容器(键值映射加上键访问历史列表)或单个 boost::bimap 的实现。

【讨论】:

【解决方案2】:

这里是一个非常简单的LRU缓存实现

https://github.com/lamerman/cpp-lru-cache

它易于使用并了解其工作原理。代码总大小约为 50 行。

【讨论】:

    【解决方案3】:

    为简单起见,也许您应该考虑使用 Boost 的 MultiIndex 映射。如果我们将键与数据分开,我们支持同一数据上的多组键。

    来自 [http://old.nabble.com/realization-of-Last-Recently-Used-cache-with-boost%3A%3Amulti_index-td22326432.html]:

    "... 使用两个索引:1) 散列用于按键搜索值 2) 顺序用于跟踪最近使用的项目(获取函数将项目作为最后一个项目在序列中。如果我们需要从缓存中删除一些项目,我们可能会从序列的开头删除它们)。”

    请注意,“项目”运算符“允许程序员有效地在同一个 multi_index_container 的不同索引之间移动”。

    【讨论】:

    【解决方案4】:

    在我们的生产环境中,我们使用类似于Linux kernel linked list 的C++ 双链表。它的美妙之处在于您可以将对象添加到任意数量的链表中,并且列表操作快速简单。

    【讨论】:

      【解决方案5】:

      最近,我使用散布在哈希映射上的链表实现了 LRU 缓存。

          /// Typedef for URL/Entry pair
          typedef std::pair< std::string, Entry > EntryPair;
      
          /// Typedef for Cache list
          typedef std::list< EntryPair > CacheList;
      
          /// Typedef for URL-indexed map into the CacheList
          typedef boost::unordered_map< std::string, CacheList::iterator > CacheMap;
      
          /// Cache LRU list
          CacheList mCacheList;
      
          /// Cache map into the list
          CacheMap mCacheMap;
      

      它的优点是所有重要操作都是 O(1)。

      插入算法:

      // create new entry
      Entry iEntry( ... );
      
      // push it to the front;
      mCacheList.push_front( std::make_pair( aURL, iEntry ) );
      
      // add it to the cache map
      mCacheMap[ aURL ] = mCacheList.begin();
      
      // increase count of entries
      mEntries++;
      
      // check if it's time to remove the last element
      if ( mEntries > mMaxEntries )
      {
          // erease from the map the last cache list element
          mCacheMap.erase( mCacheList.back().first );
      
          // erase it from the list
          mCacheList.pop_back();
      
          // decrease count
          mEntries--;
      }
      

      【讨论】:

      • 我也实现了 LRU 缓存并使用了类似的技术。当然,查找不再是 const,因为它会更改状态,因此在多线程环境中,您需要为每次访问甚至多个读取器都使用排他锁。
      • 为什么这是一个有效的答案?考虑一下,你缓存的大小是 5 个元素。输入流是 1,2,3,4,5。此时,链表的顺序为 5->4->3->2->1。现在,当插入一个已经存在的新元素时,比如'1',那么列表变为 1 -> 5-> 4-> 3-> 2-> 1。Hash(key=1) 被覆盖并指向到列表的开头。由于缓存超出限制,您将删除元素 Hash(key=1) 并从列表中弹出。因此,列表将是 1 -> 5 -> 4-> 3-> 2 但哈希将仅包含 4 个元素
      • 这段代码缺少的是,在插入时,如果插入现有元素,则应在插入之前从列表和哈希中删除先前出现的元素
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-03
      • 2015-07-19
      • 1970-01-01
      • 2021-03-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多