C++ 缓存(lru结合lfu)

1. ARC (Adaptive Replacement Cache)算法的核心思想

LRU(最近最久未使用)算法的主要不足在于它只考虑时间局部性,当遇到突发性的冷数据访问时,可能会将热点数据挤出缓存,造成缓存污染。例如,如果缓存大小为4,当前缓存中有热点数据A、B、C、D,突然有大量冷数据E、F、G、H访问,这些冷数据会依次替换掉热点数据,导致缓存命中率急剧下降。而LFU(最近最少使用)算法虽然考虑了访问频率,但它只关注历史访问次数,无法及时响应访问模式的变化,比如一个曾经频繁访问的数据如果最近不再访问,它仍然会占用缓存空间,而且新加入的数据由于访问次数少,容易被频繁访问的数据挤出缓存。ARC(自适应替换缓存)算法通过结合LRU和LFU的优点,并引入幽灵缓存机制来解决这些问题。ARC维护四个列表:T1(最近一次访问的数据)、B1(T1的幽灵缓存)、T2(多次访问的数据)和B2(T2的幽灵缓存)。

  1. 在缓存中查找客户端需要访问的数据,如果没有命中,表示缓存穿透,将需要访问的数据从从磁盘中取出,从LRU对应链表的尾部插入,这里尾部表示新数据。
  2. 如果命中且LFU链表中没有则判断该数据的访问次数是否大于transformTime_(自定义的变量,我这里设置为3,代表访问次数超过3次),若大于则将该数据插入LFU对应链表中(一个块至少需要读取transformTime_次,并且要是最近请求的数据,才会被存储到LFU中)。于是,该数据块不仅仅只保存在LRU的缓存目录中,也将保存到LFU中。如果命中且LFU链表中存在,则将数据重新放入LFU链表中对应位置(访问频次计数+1),这样,那些真正被频繁访问的页面将一直呆在缓存中,不会被冷数据的加入而误淘汰,不经常访问的数据会向链表头部移动,最终被淘汰出去。
  3. 如果此时LRU缓存满了,则从LRU链表中淘汰表头部的数据,将淘汰数据的key放入LRU对应的ghost list。然后在LRU的链表尾部添加新数据。如果ghost list的元素满了,按照先进先出的方式淘汰ghost list中的元素头部元素,然后再从尾部插入元素。
  4. 如未命中缓存的数据根据key发现在LRU对应的ghost list中,则表示幽灵命中,缓存系统就可以知道,这是一个刚刚淘汰的页面,而不是第一次读取或者说很久之前读取的一个页面。于是根据这个信息来调整内部的partition分割指针以适应当下的访问模式。上述迹象说明当前的LRU缓存太小了,于是将partition分割指针右移一位(也就是LRU缓存空间+1,LFU缓存空间-1),并将命中的key数据从ghost的中移除,将新数据从LRU链表尾部插入。
  5. 反之如果未命中缓存的数据根据key发现在LFU对应的ghost中,则说明当前访问模式偏向于频繁访问经常被访问的那一些数据,说明当下LFU缓存空间太小了,对应partition分割指针左移一位,并将命中的key数据从ghost的中移除,将新数据从LRU链表尾部插入。
  • T1:最近一次访问的数据
  • B1:T1 的幽灵缓存(记录从 T1 淘汰的数据)
  • T2:多次访问的数据
  • B2:T2 的幽灵缓存(记录从 T2 淘汰的数据)

数据结构选择:

哈希表 + 双向链表(LRU部分)

哈希表提供 O(1) 的查找性能; 双向链表维护访问顺序; 智能指针(shared_ptr)自动管理内存,避免内存泄漏; 链表节点包含前后指针,方便快速移动和删除。

频率映射表 + 哈希表(LFU部分)

map 按频率排序,方便找到最小频率; list 存储同频率节点,支持快速插入删除; 哈希表提供 O(1) 的节点查找; 分离频率管理和节点存储,提高效率。

幽灵缓存:

保护共享资源访问; RAII 方式自动管理锁; 避免死锁和竞态条件; 保证数据一致性

智能指针:

shared_ptr 实现引用计数; 自动管理节点生命周期; 避免内存泄漏; 支持多线程安全

2.代码部分

(1)CachePolicy.h基本接口
#pragma once

#include "../KICachePolicy.h"
#include "KArcLruPart.h"
#include "KArcLfuPart.h"
#include 

namespace KamaCache 
{

template
class KArcCache : public KICachePolicy 
{
public:
    explicit KArcCache(size_t capacity = 10, size_t transformThreshold = 2)
        : capacity_(capacity)
        , transformThreshold_(transformThreshold)
        , lruPart_(std::make_unique>(capacity, transformThreshold))
        , lfuPart_(std::make_unique>(capacity, transformThreshold))
    {}

    ~KArcCache() override = default;

    void put(Key key, Value value) override 
    {
        bool inGhost = checkGhostCaches(key);
        
        if (!inGhost) 
        {
            if (lruPart_->put(key, value)) 
            {
                lfuPart_->put(key, value);
            }
        } else 
        {
            lruPart_->put(key, value);
        }
    }

    bool get(Key key, Value& value) override 
    {
        checkGhostCaches(key);
        bool shouldTransform = false;
        //检查LRU的幽灵缓存
        if (lruPart_->get(key, value, shouldTransform)) 
        {
            if (shouldTransform) 
            {
                lfuPart_->put(key, value);
            }
            return true;
        }
        //检查LFU的幽灵缓存
        return lfuPart_->get(key, value);
    }

    Value get(Key key) override 
    {
        Value value{};
        get(key, value);
        return value;
    }

private:
    bool checkGhostCaches(Key key) 
    {
        bool inGhost = false;
        if (lruPart_->checkGhost(key)) 
        {
            if (lfuPart_->decreaseCapacity()) 
            {
                lruPart_->increaseCapacity();
            }
            inGhost = true;
        } 
        else if (lfuPart_->checkGhost(key)) 
        {
            if (lruPart_->decreaseCapacity()) 
            {
                lfuPart_->increaseCapacity();
            }
            inGhost = true;
        }
        return inGhost;
    }

private:
    size_t capacity_;
    size_t transformThreshold_;
    std::unique_ptr> lruPart_;
    std::unique_ptr> lfuPart_;
};

} // namespace KamaCache

(2)ArcCacheNode.h相关数据结构

#pragma once

#include 

namespace KamaCache 
{

template
class ArcNode 
{
private:
    Key key_;
    Value value_;
    size_t accessCount_;
    std::shared_ptr prev_;
    std::shared_ptr next_;

public:
    ArcNode() : accessCount_(1), prev_(nullptr), next_(nullptr) {}
    
    ArcNode(Key key, Value value) 
        : key_(key)
        , value_(value)
        , accessCount_(1)
        , prev_(nullptr)
        , next_(nullptr) 
    {}

    // Getters
    Key getKey() const { return key_; }
    Value getValue() const { return value_; }
    size_t getAccessCount() const { return accessCount_; }
    
    // Setters
    void setValue(const Value& value) { value_ = value; }
    void incrementAccessCount() { ++accessCount_; }

    template friend class ArcLruPart;
    template friend class ArcLfuPart;
};

} // namespace KamaCache

(3)两种算法具体实现:

lru

#pragma once

#include "KArcCacheNode.h"
#include 
#include 

namespace KamaCache 
{

template
class ArcLruPart 
{
public:
    using NodeType = ArcNode;
    using NodePtr = std::shared_ptr;
    using NodeMap = std::unordered_map;

    explicit ArcLruPart(size_t capacity, size_t transformThreshold)
        : capacity_(capacity)
        , ghostCapacity_(capacity)
        , transformThreshold_(transformThreshold)
    {
        initializeLists();
    }

    bool put(Key key, Value value) 
    {
        if (capacity_ == 0) return false;
        
        std::lock_guard lock(mutex_);
        auto it = mainCache_.find(key);
        if (it != mainCache_.end()) 
        {
            return updateExistingNode(it->second, value);
        }
        return addNewNode(key, value);
    }

    bool get(Key key, Value& value, bool& shouldTransform) 
    {
        std::lock_guard lock(mutex_);  //加锁保护
        auto it = mainCache_.find(key);          //在哈希表中查找
        if (it != mainCache_.end()) 
        {
            shouldTransform = updateNodeAccess(it->second);  //更新节点访问次数
            value = it->second->getValue();                  //获取节点值
            return true;
        }
        return false;
    }

    bool checkGhost(Key key) 
    {
        auto it = ghostCache_.find(key);
        if (it != ghostCache_.end()) {
            removeFromGhost(it->second);
            ghostCache_.erase(it);
            return true;
        }
        return false;
    }

    void increaseCapacity() { ++capacity_; }
    
    bool decreaseCapacity() 
    {
        if (capacity_ <= 0) return false;
        if (mainCache_.size() == capacity_) {
            evictLeastRecent();
        }
        --capacity_;
        return true;
    }

private:
    void initializeLists() 
    {
        mainHead_ = std::make_shared();
        mainTail_ = std::make_shared();
        mainHead_->next_ = mainTail_;
        mainTail_->prev_ = mainHead_;

        ghostHead_ = std::make_shared();
        ghostTail_ = std::make_shared();
        ghostHead_->next_ = ghostTail_;
        ghostTail_->prev_ = ghostHead_;
    }

    bool updateExistingNode(NodePtr node, const Value& value) 
    {
        node->setValue(value);
        moveToFront(node);
        return true;
    }

    bool addNewNode(const Key& key, const Value& value) 
    {
        if (mainCache_.size() >= capacity_) 
        {   
            evictLeastRecent(); // 驱逐最近最少访问
        }

        NodePtr newNode = std::make_shared(key, value);
        mainCache_[key] = newNode;
        addToFront(newNode);
        return true;
    }

    bool updateNodeAccess(NodePtr node) 
    {
        moveToFront(node);     //移动到链表前端
        node->incrementAccessCount();    //增加访问次数
        return node->getAccessCount() >= transformThreshold_; //判断是否需要调整阈值
    }

    void moveToFront(NodePtr node) 
    {
        // 先从当前位置移除 
        node->prev_->next_ = node->next_;
        node->next_->prev_ = node->prev_;
        
        // 添加到头部
        addToFront(node);
    }

    void addToFront(NodePtr node) 
    {
        node->next_ = mainHead_->next_;
        node->prev_ = mainHead_;
        mainHead_->next_->prev_ = node;
        mainHead_->next_ = node;
    }

    void evictLeastRecent() 
    {
        NodePtr leastRecent = mainTail_->prev_;
        if (leastRecent == mainHead_) 
            return;

        // 从主链表中移除
        removeFromMain(leastRecent);

        // 添加到幽灵缓存
        if (ghostCache_.size() >= ghostCapacity_) 
        {
            removeOldestGhost();
        }
        addToGhost(leastRecent);

        // 从主缓存映射中移除
        mainCache_.erase(leastRecent->getKey());
    }

    void removeFromMain(NodePtr node) 
    {
        node->prev_->next_ = node->next_;
        node->next_->prev_ = node->prev_;
    }

    void removeFromGhost(NodePtr node) 
    {
        node->prev_->next_ = node->next_;
        node->next_->prev_ = node->prev_;
    }

    void addToGhost(NodePtr node) 
    {
        // 重置节点的访问计数
        node->accessCount_ = 1;
        
        // 添加到幽灵缓存的头部
        node->next_ = ghostHead_->next_;
        node->prev_ = ghostHead_;
        ghostHead_->next_->prev_ = node;
        ghostHead_->next_ = node;
        
        // 添加到幽灵缓存映射
        ghostCache_[node->getKey()] = node;
    }

    void removeOldestGhost() 
    {
        NodePtr oldestGhost = ghostTail_->prev_;
        if (oldestGhost == ghostHead_) 
            return;

        removeFromGhost(oldestGhost);
        ghostCache_.erase(oldestGhost->getKey());
    }
    

private:
    size_t capacity_;
    size_t ghostCapacity_;
    size_t transformThreshold_; // 转换门槛值
    std::mutex mutex_;

    NodeMap mainCache_; // key -> ArcNode
    NodeMap ghostCache_;
    
    // 主链表
    NodePtr mainHead_;
    NodePtr mainTail_;
    // 淘汰链表
    NodePtr ghostHead_;
    NodePtr ghostTail_;
};

} // namespace KamaCache

lfu:

#pragma once

#include "KArcCacheNode.h"
#include 
#include 
#include 

namespace KamaCache 
{

template
class ArcLfuPart 
{
public:
    using NodeType = ArcNode;
    using NodePtr = std::shared_ptr;
    using NodeMap = std::unordered_map;
    using FreqMap = std::map>;

    explicit ArcLfuPart(size_t capacity, size_t transformThreshold)
        : capacity_(capacity)
        , ghostCapacity_(capacity)
        , transformThreshold_(transformThreshold)
        , minFreq_(0)
    {
        initializeLists();
    }

    bool put(Key key, Value value) 
    {
        if (capacity_ == 0) 
            return false;

        std::lock_guard lock(mutex_);
        auto it = mainCache_.find(key);
        if (it != mainCache_.end()) 
        {
            return updateExistingNode(it->second, value);
        }
        return addNewNode(key, value);
    }

    bool get(Key key, Value& value) 
    {
        std::lock_guard lock(mutex_);
        auto it = mainCache_.find(key);
        if (it != mainCache_.end()) 
        {
            updateNodeFrequency(it->second);
            value = it->second->getValue();
            return true;
        }
        return false;
    }

    bool checkGhost(Key key) 
    {
        auto it = ghostCache_.find(key);
        if (it != ghostCache_.end()) 
        {
            removeFromGhost(it->second);
            ghostCache_.erase(it);
            return true;
        }
        return false;
    }

    void increaseCapacity() { ++capacity_; }
    
    bool decreaseCapacity() 
    {
        if (capacity_ <= 0) return false;
        if (mainCache_.size() == capacity_) 
        {
            evictLeastFrequent();
        }
        --capacity_;
        return true;
    }

private:
    void initializeLists() 
    {
        ghostHead_ = std::make_shared();
        ghostTail_ = std::make_shared();
        ghostHead_->next_ = ghostTail_;
        ghostTail_->prev_ = ghostHead_;
    }

    bool updateExistingNode(NodePtr node, const Value& value) 
    {
        node->setValue(value);
        updateNodeFrequency(node);
        return true;
    }

    bool addNewNode(const Key& key, const Value& value) 
    {
        if (mainCache_.size() >= capacity_) 
        {
            evictLeastFrequent();
        }

        NodePtr newNode = std::make_shared(key, value);
        mainCache_[key] = newNode;
        
        // 将新节点添加到频率为1的列表中
        if (freqMap_.find(1) == freqMap_.end()) 
        {
            freqMap_[1] = std::list();
        }
        freqMap_[1].push_back(newNode);
        minFreq_ = 1;
        
        return true;
    }

    void updateNodeFrequency(NodePtr node) 
    {
        size_t oldFreq = node->getAccessCount();
        node->incrementAccessCount();
        size_t newFreq = node->getAccessCount();

        // 从旧频率列表中移除
        auto& oldList = freqMap_[oldFreq];
        oldList.remove(node);
        if (oldList.empty()) 
        {
            freqMap_.erase(oldFreq);
            if (oldFreq == minFreq_) 
            {
                minFreq_ = newFreq;
            }
        }

        // 添加到新频率列表
        if (freqMap_.find(newFreq) == freqMap_.end()) 
        {
            freqMap_[newFreq] = std::list();
        }
        freqMap_[newFreq].push_back(node);
    }

    void evictLeastFrequent() 
    {
        if (freqMap_.empty()) 
            return;

        // 获取最小频率的列表
        auto& minFreqList = freqMap_[minFreq_];
        if (minFreqList.empty()) 
            return;

        // 移除最少使用的节点
        NodePtr leastNode = minFreqList.front();
        minFreqList.pop_front();

        // 如果该频率的列表为空,则删除该频率项
        if (minFreqList.empty()) 
        {
            freqMap_.erase(minFreq_);
            // 更新最小频率
            if (!freqMap_.empty()) 
            {
                minFreq_ = freqMap_.begin()->first;
            }
        }

        // 将节点移到幽灵缓存
        if (ghostCache_.size() >= ghostCapacity_) 
        {
            removeOldestGhost();
        }
        addToGhost(leastNode);
        
        // 从主缓存中移除
        mainCache_.erase(leastNode->getKey());
    }

    void removeFromGhost(NodePtr node) 
    {
        node->prev_->next_ = node->next_;
        node->next_->prev_ = node->prev_;
    }

    void addToGhost(NodePtr node) 
    {
        node->next_ = ghostTail_;
        node->prev_ = ghostTail_->prev_;
        ghostTail_->prev_->next_ = node;
        ghostTail_->prev_ = node;
        ghostCache_[node->getKey()] = node;
    }

    void removeOldestGhost() 
    {
        NodePtr oldestGhost = ghostHead_->next_;
        if (oldestGhost != ghostTail_) 
        {
            removeFromGhost(oldestGhost);
            ghostCache_.erase(oldestGhost->getKey());
        }
    }

private:
    size_t capacity_;     //容量
    size_t ghostCapacity_;//幽灵缓存容量
    size_t transformThreshold_;//转换阈值
    size_t minFreq_;      //最小频率
    std::mutex mutex_;    //互斥锁  

    NodeMap mainCache_;    //哈希表
    NodeMap ghostCache_;   //幽灵缓存
    FreqMap freqMap_;      //频率表
    
    NodePtr ghostHead_;
    NodePtr ghostTail_;
};

} // namespace KamaCache

(4)整体协调ArcCache

#pragma once

#include "../KICachePolicy.h"
#include "KArcLruPart.h"
#include "KArcLfuPart.h"
#include 

namespace KamaCache 
{

template
class KArcCache : public KICachePolicy 
{
public:
    explicit KArcCache(size_t capacity = 10, size_t transformThreshold = 2)
        : capacity_(capacity)
        , transformThreshold_(transformThreshold)
        , lruPart_(std::make_unique>(capacity, transformThreshold))
        , lfuPart_(std::make_unique>(capacity, transformThreshold))
    {}

    ~KArcCache() override = default;

    void put(Key key, Value value) override 
    {
        bool inGhost = checkGhostCaches(key);
        
        if (!inGhost) 
        {
            if (lruPart_->put(key, value)) 
            {
                lfuPart_->put(key, value);
            }
        } else 
        {
            lruPart_->put(key, value);
        }
    }

    bool get(Key key, Value& value) override 
    {
        checkGhostCaches(key);
        bool shouldTransform = false;
        //检查LRU的幽灵缓存
        if (lruPart_->get(key, value, shouldTransform)) 
        {
            if (shouldTransform) 
            {
                lfuPart_->put(key, value);
            }
            return true;
        }
        //检查LFU的幽灵缓存
        return lfuPart_->get(key, value);
    }

    Value get(Key key) override 
    {
        Value value{};
        get(key, value);
        return value;
    }

private:
    bool checkGhostCaches(Key key) 
    {
        bool inGhost = false;
        if (lruPart_->checkGhost(key)) 
        {
            if (lfuPart_->decreaseCapacity()) 
            {
                lruPart_->increaseCapacity();
            }
            inGhost = true;
        } 
        else if (lfuPart_->checkGhost(key)) 
        {
            if (lruPart_->decreaseCapacity()) 
            {
                lfuPart_->increaseCapacity();
            }
            inGhost = true;
        }
        return inGhost;
    }

private:
    size_t capacity_;
    size_t transformThreshold_;
    std::unique_ptr> lruPart_;
    std::unique_ptr> lfuPart_;
};

} // namespace KamaCache

3.具体流程

(1)get 操作(取数据)

LRU (Least Recently Used) 部分

LRU 部分通过 ArcLruPart 类实现,维护了一个主缓存(mainCache_)和一个幽灵缓存(ghostCache_)。主缓存使用哈希表(unordered_map)和双向链表相结合的数据结构,哈希表存储 key 到节点的映射实现 O(1) 查找,双向链表维护数据的访问顺序。当调用 get 操作时,首先通过 std::lock_guard 加锁保证线程安全,然后在 mainCache_ 中查找目标 key。如果找到,会触发一系列更新操作:调用 updateNodeAccess 函数增加节点的访问计数(accessCount_),同时通过 moveToFront 函数将节点移动到链表前端(表示最近访问)。moveToFront 操作分两步:先通过调整前后节点的指针将当前节点从原位置移除,然后通过 addToFront 函数将节点插入到链表头部(mainHead_ 之后)。如果节点的访问次数达到转换阈值(transformThreshold_),则标记 shouldTransform 为 true,表示该节点应该转移到 LFU 部分。当缓存满时,通过 evictLeastRecent 函数移除链表尾部的节点(最久未使用),被移除的节点会被放入幽灵缓存中,记录历史访问信息。整个过程通过互斥锁(mutex_)保证线程安全,通过智能指针(shared_ptr)管理节点内存。

KArcCache::get(key, value)
-> checkGhostCaches(key)
   -> lruPart_->checkGhost(key)
      -> ghostCache_.find(key)
      -> removeFromGhost(node)
      -> ghostCache_.erase(key)
   -> lfuPart_->decreaseCapacity()
   -> lruPart_->increaseCapacity()

-> lruPart_->get(key, value, shouldTransform)
   -> std::lock_guard lock
   -> mainCache_.find(key)
   -> updateNodeAccess(node)
      -> moveToFront(node)
         -> node->prev_->next_ = node->next_
         -> node->next_->prev_ = node->prev_
         -> addToFront(node)
            -> node->next_ = mainHead_->next_
            -> node->prev_ = mainHead_
            -> mainHead_->next_->prev_ = node
            -> mainHead_->next_ = node
      -> node->incrementAccessCount()
      -> return node->getAccessCount() >= transformThreshold_
   -> node->getValue()




相关数据结构:
// 节点结构
template
class ArcNode {
    Key key_;                              // 键
    Value value_;                          // 值
    size_t accessCount_;                   // 访问计数
    std::shared_ptr prev_;        // 前向指针
    std::shared_ptr next_;        // 后向指针
};

// LRU部分主要数据结构
class ArcLruPart {
    size_t capacity_;                      // 缓存容量
    size_t ghostCapacity_;                 // 幽灵缓存容量
    size_t transformThreshold_;            // 转换阈值
    std::mutex mutex_;                     // 互斥锁
    
    NodeMap mainCache_;                    // 主缓存哈希表
    NodeMap ghostCache_;                   // 幽灵缓存哈希表
    
    NodePtr mainHead_;                     // 主链表头节点
    NodePtr mainTail_;                     // 主链表尾节点
    NodePtr ghostHead_;                    // 幽灵链表头节点
    NodePtr ghostTail_;                    // 幽灵链表尾节点
};
LFU (Least Frequently Used) 部分

LFU 部分通过 ArcLfuPart 类实现,同样维护主缓存(mainCache_)和幽灵缓存(ghostCache_),但增加了频率管理机制。除了哈希表存储 key 到节点的映射,还使用 freqMap_ (map>)存储不同访问频率的节点列表。当调用 get 操作时,同样先加锁保护,然后在 mainCache_ 中查找 key。如果找到目标节点,会调用 updateNodeFrequency 函数更新节点频率:首先记录节点当前频率(oldFreq),增加访问计数后得到新频率(newFreq),然后将节点从旧频率列表(freqMap_[oldFreq])移除。如果移除后该频率列表为空,则删除这个频率项,同时更新最小频率(minFreq_)。接着将节点添加到新频率列表(freqMap_[newFreq])中。当缓存满时,通过 evictLeastFrequent 函数移除最小频率列表中的第一个节点(最少使用),被移除的节点同样进入幽灵缓存。LFU 部分的特点是通过 freqMap_ 精确追踪每个节点的访问频率,确保最少使用的数据被优先移除。整个过程也通过互斥锁保证线程安全,通过智能指针管理内存,并通过频率管理机制实现了基于使用频率的缓存替换策略。当节点从 LRU 部分转移到 LFU 部分时,会保持其访问计数,这样可以保证频率统计的连续性。

KArcCache::get(key, value)
-> checkGhostCaches(key)
   -> lfuPart_->checkGhost(key)
      -> ghostCache_.find(key)
      -> removeFromGhost(node)
      -> ghostCache_.erase(key)
   -> lruPart_->decreaseCapacity()
   -> lfuPart_->increaseCapacity()

-> lfuPart_->get(key, value)
   -> std::lock_guard lock
   -> mainCache_.find(key)
   -> updateNodeFrequency(node)
      -> oldFreq = node->getAccessCount()
      -> node->incrementAccessCount()
      -> newFreq = node->getAccessCount()
      -> freqMap_[oldFreq].remove(node)
      -> if (oldList.empty())
         -> freqMap_.erase(oldFreq)
         -> update minFreq_
      -> freqMap_[newFreq].push_back(node)
   -> node->getValue()




相关数据结构:
// 节点结构 (与LRU共用ArcNode)
template
class ArcNode {
    Key key_;                              // 键
    Value value_;                          // 值
    size_t accessCount_;                   // 访问计数
    std::shared_ptr prev_;        // 前向指针
    std::shared_ptr next_;        // 后向指针
};

// LFU部分主要数据结构
class ArcLfuPart {
    size_t capacity_;                      // 缓存容量
    size_t ghostCapacity_;                 // 幽灵缓存容量
    size_t transformThreshold_;            // 转换阈值
    size_t minFreq_;                       // 最小频率
    std::mutex mutex_;                     // 互斥锁
    
    NodeMap mainCache_;                    // 主缓存哈希表
    NodeMap ghostCache_;                   // 幽灵缓存哈希表
    FreqMap freqMap_;                      // 频率映射表
    
    NodePtr ghostHead_;                    // 幽灵链表头节点
    NodePtr ghostTail_;                    // 幽灵链表尾节点
};

// 频率映射表类型定义
using FreqMap = std::map>;

共享的部分:

// 类型定义
using NodeType = ArcNode;
using NodePtr = std::shared_ptr;
using NodeMap = std::unordered_map;

// ArcCache主类数据结构
class KArcCache {
    size_t capacity_;                      // 总缓存容量
    size_t transformThreshold_;            // 转换阈值
    std::unique_ptr> lruPart_;    // LRU部分
    std::unique_ptr> lfuPart_;    // LFU部分
};

(2) put 操作 (放数据)

LRU 部分

LRU 部分的 put 操作通过 ArcLruPart 类实现,当调用 put 操作时,首先通过 std::lock_guard 加锁保证线程安全,然后在 mainCache_ 中查找目标 key。如果找到已存在的节点,调用 updateExistingNode 函数更新节点:先通过 setValue 更新节点的值,然后通过 moveToFront 函数将节点移动到链表前端,表示最近访问。如果节点不存在,则调用 addNewNode 函数添加新节点:首先检查缓存是否已满,如果已满则调用 evictLeastRecent 函数移除最久未使用的节点(链表尾部节点),被移除的节点会被放入幽灵缓存中。然后创建新的 ArcNode 节点,设置其 key、value 和初始访问计数为1,将节点插入到 mainCache_ 哈希表中,最后通过 addToFront 函数将节点添加到链表头部。整个过程通过互斥锁保证线程安全,通过智能指针管理节点内存。当节点被驱逐时,会先调用 removeFromMain 函数从主链表中移除,然后通过 addToGhost 函数将其添加到幽灵缓存中,同时重置其访问计数为1,这样可以记录历史访问信息,帮助系统做出更好的缓存替换决策。

KArcCache::put(key, value)
-> checkGhostCaches(key)
   -> lruPart_->checkGhost(key)
      -> ghostCache_.find(key)
      -> removeFromGhost(node)
      -> ghostCache_.erase(key)
   -> lfuPart_->decreaseCapacity()
   -> lruPart_->increaseCapacity()

-> lruPart_->put(key, value)
   -> std::lock_guard lock
   -> mainCache_.find(key)
   -> updateExistingNode(node, value)
      -> node->setValue(value)
      -> moveToFront(node)
         -> node->prev_->next_ = node->next_
         -> node->next_->prev_ = node->prev_
         -> addToFront(node)
            -> node->next_ = mainHead_->next_
            -> node->prev_ = mainHead_
            -> mainHead_->next_->prev_ = node
            -> mainHead_->next_ = node
   -> addNewNode(key, value)
      -> if (mainCache_.size() >= capacity_)
         -> evictLeastRecent()
            -> NodePtr leastRecent = mainTail_->prev_
            -> removeFromMain(leastRecent)
            -> addToGhost(leastRecent)
            -> mainCache_.erase(leastRecent->getKey())
      -> NodePtr newNode = std::make_shared(key, value)
      -> mainCache_[key] = newNode
      -> addToFront(newNode)



LFU 部分

LFU 部分的 put 操作通过 ArcLfuPart 类实现,同样先加锁保护,然后在 mainCache_ 中查找 key。如果找到已存在的节点,调用 updateExistingNode 函数更新节点:更新节点值并通过 updateNodeFrequency 函数更新其访问频率。如果节点不存在,则调用 addNewNode 函数添加新节点:首先检查缓存是否已满,如果已满则调用 evictLeastFrequent 函数移除访问频率最低的节点。evictLeastFrequent 函数会从 freqMap_ 中找到最小频率(minFreq_)对应的列表,移除该列表中的第一个节点,如果该频率列表为空则删除该频率项并更新最小频率。被移除的节点通过 addToGhost 函数添加到幽灵缓存中。然后创建新的 ArcNode 节点,设置其 key、value 和初始访问计数为1,将节点插入到 mainCache_ 哈希表中,同时将其添加到 freqMap_ 中频率为1的列表中,并更新最小频率为1。整个过程通过互斥锁保证线程安全,通过智能指针管理内存,并通过频率管理机制实现了基于使用频率的缓存替换策略。当节点被驱逐时,会先调用 removeFromGhost 函数从幽灵链表中移除,然后通过 addToGhost 函数将其添加到幽灵缓存的头部,这样可以保持历史访问信息,帮助系统做出更好的缓存替换决策。

KArcCache::put(key, value)
-> checkGhostCaches(key)
   -> lfuPart_->checkGhost(key)
      -> ghostCache_.find(key)
      -> removeFromGhost(node)
      -> ghostCache_.erase(key)
   -> lruPart_->decreaseCapacity()
   -> lfuPart_->increaseCapacity()

-> lfuPart_->put(key, value)
   -> std::lock_guard lock
   -> mainCache_.find(key)
   -> updateExistingNode(node, value)
      -> node->setValue(value)
      -> updateNodeFrequency(node)
         -> oldFreq = node->getAccessCount()
         -> node->incrementAccessCount()
         -> newFreq = node->getAccessCount()
         -> freqMap_[oldFreq].remove(node)
         -> if (oldList.empty())
            -> freqMap_.erase(oldFreq)
            -> update minFreq_
         -> freqMap_[newFreq].push_back(node)
   -> addNewNode(key, value)
      -> if (mainCache_.size() >= capacity_)
         -> evictLeastFrequent()
            -> auto& minFreqList = freqMap_[minFreq_]
            -> NodePtr leastNode = minFreqList.front()
            -> minFreqList.pop_front()
            -> if (minFreqList.empty())
               -> freqMap_.erase(minFreq_)
               -> update minFreq_
            -> addToGhost(leastNode)
            -> mainCache_.erase(leastNode->getKey())
      -> NodePtr newNode = std::make_shared(key, value)
      -> mainCache_[key] = newNode
      -> freqMap_[1].push_back(newNode)
      -> minFreq_ = 1

数据结构部分:

// 节点结构
template
class ArcNode {
    Key key_;                              // 键
    Value value_;                          // 值
    size_t accessCount_;                   // 访问计数
    std::shared_ptr prev_;        // 前向指针
    std::shared_ptr next_;        // 后向指针
};

// LRU部分主要数据结构
class ArcLruPart {
    size_t capacity_;                      // 缓存容量
    size_t ghostCapacity_;                 // 幽灵缓存容量
    size_t transformThreshold_;            // 转换阈值
    std::mutex mutex_;                     // 互斥锁
    
    NodeMap mainCache_;                    // 主缓存哈希表
    NodeMap ghostCache_;                   // 幽灵缓存哈希表
    
    NodePtr mainHead_;                     // 主链表头节点
    NodePtr mainTail_;                     // 主链表尾节点
    NodePtr ghostHead_;                    // 幽灵链表头节点
    NodePtr ghostTail_;                    // 幽灵链表尾节点
};

// LFU部分主要数据结构
class ArcLfuPart {
    size_t capacity_;                      // 缓存容量
    size_t ghostCapacity_;                 // 幽灵缓存容量
    size_t transformThreshold_;            // 转换阈值
    size_t minFreq_;                       // 最小频率
    std::mutex mutex_;                     // 互斥锁
    
    NodeMap mainCache_;                    // 主缓存哈希表
    NodeMap ghostCache_;                   // 幽灵缓存哈希表
    FreqMap freqMap_;                      // 频率映射表
    
    NodePtr ghostHead_;                    // 幽灵链表头节点
    NodePtr ghostTail_;                    // 幽灵链表尾节点
};

// 类型定义
using NodeType = ArcNode;
using NodePtr = std::shared_ptr;
using NodeMap = std::unordered_map;
using FreqMap = std::map>;

// ArcCache主类数据结构
class KArcCache {
    size_t capacity_;                      // 总缓存容量
    size_t transformThreshold_;            // 转换阈值
    std::unique_ptr> lruPart_;    // LRU部分
    std::unique_ptr> lfuPart_;    // LFU部分
};

你可能感兴趣的:(C++,缓存,c++,数据结构,链表)