From 89351183c3cb08382f5618196054f5d545d66420 Mon Sep 17 00:00:00 2001 From: drygrass Date: Tue, 18 Nov 2025 23:41:04 +0800 Subject: [PATCH] first comit --- .gitignore | 28 ++ CMakeLists.txt | 21 ++ KArcCache/KArcCache.h | 92 ++++++ KArcCache/KArcCacheNode.h | 41 +++ KArcCache/KArcLfuPart.h | 231 +++++++++++++ KArcCache/KArcLruPart.h | 222 +++++++++++++ KICachePolicy.h | 22 ++ KLfuCache.h | 379 +++++++++++++++++++++ KLruCache.h | 326 ++++++++++++++++++ LICENSE | 674 ++++++++++++++++++++++++++++++++++++++ README.md | 54 +++ images/hitTest.jpg | Bin 0 -> 128994 bytes testAllCachePolicy.cpp | 286 ++++++++++++++++ 13 files changed, 2376 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 KArcCache/KArcCache.h create mode 100644 KArcCache/KArcCacheNode.h create mode 100644 KArcCache/KArcLfuPart.h create mode 100644 KArcCache/KArcLruPart.h create mode 100644 KICachePolicy.h create mode 100644 KLfuCache.h create mode 100644 KLruCache.h create mode 100644 LICENSE create mode 100644 README.md create mode 100644 images/hitTest.jpg create mode 100644 testAllCachePolicy.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d02fad --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# CMake 生成目录 +build/ +CMakeFiles/ +CMakeCache.txt +.cache/ +.vscode/ +.test + +# CMake 生成的可执行文件和库 +*.exe +*.out +*.a +*.so +*.dylib +*.lib +*.dll + +# CMake 临时文件 +*.cmake +Makefile +CMakeScripts/ + +# 其他常见生成文件 +*.ninja +.ninja_deps +.ninja_log +compile_commands.json +install_manifest.txt \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2695ab2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +# 设置最低 CMake 版本要求 +cmake_minimum_required(VERSION 3.10) + +# 设置项目名称 +project(KCacheSystem) + +# 设置 C++ 标准 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# 指定源文件目录下的所有 .cpp 文件 +file(GLOB SOURCES "*.cpp") + +# 设置目标可执行文件 +add_executable(main ${SOURCES}) + +# 清理中间的 .o 文件 +set_target_properties(main PROPERTIES CLEAN_DIRECT_OUTPUT 1) + +# 额外的编译选项(可根据需要启用) +# target_compile_options(main PRIVATE -Wall -Wextra -O2) \ No newline at end of file diff --git a/KArcCache/KArcCache.h b/KArcCache/KArcCache.h new file mode 100644 index 0000000..c239640 --- /dev/null +++ b/KArcCache/KArcCache.h @@ -0,0 +1,92 @@ +#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 + { + checkGhostCaches(key); + + // 检查 LFU 部分是否存在该键 + bool inLfu = lfuPart_->contain(key); + // 更新 LRU 部分缓存 + lruPart_->put(key, value); + // 如果 LFU 部分存在该键,则更新 LFU 部分 + if (inLfu) + { + lfuPart_->put(key, value); + } + } + + bool get(Key key, Value& value) override + { + checkGhostCaches(key); + + bool shouldTransform = false; + if (lruPart_->get(key, value, shouldTransform)) + { + if (shouldTransform) + { + lfuPart_->put(key, value); + } + return true; + } + 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 \ No newline at end of file diff --git a/KArcCache/KArcCacheNode.h b/KArcCache/KArcCacheNode.h new file mode 100644 index 0000000..f19a1e1 --- /dev/null +++ b/KArcCache/KArcCacheNode.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace KamaCache +{ + +template +class ArcNode +{ +private: + Key key_; + Value value_; + size_t accessCount_; + std::weak_ptr prev_; + std::shared_ptr next_; + +public: + ArcNode() : accessCount_(1), next_(nullptr) {} + + ArcNode(Key key, Value value) + : key_(key) + , value_(value) + , accessCount_(1) + , 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 \ No newline at end of file diff --git a/KArcCache/KArcLfuPart.h b/KArcCache/KArcLfuPart.h new file mode 100644 index 0000000..5387c13 --- /dev/null +++ b/KArcCache/KArcLfuPart.h @@ -0,0 +1,231 @@ +#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 contain(Key key) + { + return mainCache_.find(key) != mainCache_.end(); + } + + 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) + { + if (!node->prev_.expired() && node->next_) { + auto prev = node->prev_.lock(); + prev->next_ = node->next_; + node->next_->prev_ = node->prev_; + node->next_ = nullptr; // 清空指针,防止悬垂引用 + } + } + + void addToGhost(NodePtr node) + { + node->next_ = ghostTail_; + node->prev_ = ghostTail_->prev_; + if (!ghostTail_->prev_.expired()) { + ghostTail_->prev_.lock()->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 \ No newline at end of file diff --git a/KArcCache/KArcLruPart.h b/KArcCache/KArcLruPart.h new file mode 100644 index 0000000..65396e1 --- /dev/null +++ b/KArcCache/KArcLruPart.h @@ -0,0 +1,222 @@ +#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) + { + // 先从当前位置移除 + if (!node->prev_.expired() && node->next_) { + auto prev = node->prev_.lock(); + prev->next_ = node->next_; + node->next_->prev_ = node->prev_; + node->next_ = nullptr; // 清空指针,防止悬垂引用 + } + + // 添加到头部 + 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_.lock(); + if (!leastRecent || leastRecent == mainHead_) + return; + + // 从主链表中移除 + removeFromMain(leastRecent); + + // 添加到幽灵缓存 + if (ghostCache_.size() >= ghostCapacity_) + { + removeOldestGhost(); + } + addToGhost(leastRecent); + + // 从主缓存映射中移除 + mainCache_.erase(leastRecent->getKey()); + } + + void removeFromMain(NodePtr node) + { + if (!node->prev_.expired() && node->next_) { + auto prev = node->prev_.lock(); + prev->next_ = node->next_; + node->next_->prev_ = node->prev_; + node->next_ = nullptr; // 清空指针,防止悬垂引用 + } + } + + void removeFromGhost(NodePtr node) + { + if (!node->prev_.expired() && node->next_) { + auto prev = node->prev_.lock(); + prev->next_ = node->next_; + node->next_->prev_ = node->prev_; + node->next_ = nullptr; // 清空指针,防止悬垂引用 + } + } + + 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() + { + // 使用lock()方法,并添加null检查 + NodePtr oldestGhost = ghostTail_->prev_.lock(); + if (!oldestGhost || 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 \ No newline at end of file diff --git a/KICachePolicy.h b/KICachePolicy.h new file mode 100644 index 0000000..8fbbc82 --- /dev/null +++ b/KICachePolicy.h @@ -0,0 +1,22 @@ +#pragma once + +namespace KamaCache +{ + +template +class KICachePolicy +{ +public: + virtual ~KICachePolicy() {}; + + // 添加缓存接口 + virtual void put(Key key, Value value) = 0; + + // key是传入参数 访问到的值以传出参数的形式返回 | 访问成功返回true + virtual bool get(Key key, Value& value) = 0; + // 如果缓存中能找到key,则直接返回value + virtual Value get(Key key) = 0; + +}; + +} // namespace KamaCache \ No newline at end of file diff --git a/KLfuCache.h b/KLfuCache.h new file mode 100644 index 0000000..023ebab --- /dev/null +++ b/KLfuCache.h @@ -0,0 +1,379 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "KICachePolicy.h" + +namespace KamaCache +{ + +template class KLfuCache; + +template +class FreqList +{ +private: + struct Node + { + int freq; // 访问频次 + Key key; + Value value; + std::weak_ptr pre; // 上一结点改为weak_ptr打破循环引用 + std::shared_ptr next; + + Node() + : freq(1), next(nullptr) {} + Node(Key key, Value value) + : freq(1), key(key), value(value), next(nullptr) {} + }; + + using NodePtr = std::shared_ptr; + int freq_; // 访问频率 + NodePtr head_; // 假头结点 + NodePtr tail_; // 假尾结点 + +public: + explicit FreqList(int n) + : freq_(n) + { + head_ = std::make_shared(); + tail_ = std::make_shared(); + head_->next = tail_; + tail_->pre = head_; + } + + bool isEmpty() const + { + return head_->next == tail_; + } + + // 提那家结点管理方法 + void addNode(NodePtr node) + { + if (!node || !head_ || !tail_) + return; + + node->pre = tail_->pre; + node->next = tail_; + tail_->pre.lock()->next = node; // 使用lock()获取shared_ptr + tail_->pre = node; + } + + void removeNode(NodePtr node) + { + if (!node || !head_ || !tail_) + return; + if (node->pre.expired() || !node->next) + return; + + auto pre = node->pre.lock(); // 使用lock()获取shared_ptr + pre->next = node->next; + node->next->pre = pre; + node->next = nullptr; // 确保显式置空next指针,彻底断开节点与链表的连接 + } + + NodePtr getFirstNode() const { return head_->next; } + + friend class KLfuCache; +}; + +template +class KLfuCache : public KICachePolicy +{ +public: + using Node = typename FreqList::Node; + using NodePtr = std::shared_ptr; + using NodeMap = std::unordered_map; + + KLfuCache(int capacity, int maxAverageNum = 1000000) + : capacity_(capacity), minFreq_(INT8_MAX), maxAverageNum_(maxAverageNum), + curAverageNum_(0), curTotalNum_(0) + {} + + ~KLfuCache() override = default; + + void put(Key key, Value value) override + { + if (capacity_ == 0) + return; + + std::lock_guard lock(mutex_); + auto it = nodeMap_.find(key); + if (it != nodeMap_.end()) + { + // 重置其value值 + it->second->value = value; + // 找到了直接调整就好了,不用再去get中再找一遍,但其实影响不大 + getInternal(it->second, value); + return; + } + + putInternal(key, value); + } + + // value值为传出参数 + bool get(Key key, Value& value) override + { + std::lock_guard lock(mutex_); + auto it = nodeMap_.find(key); + if (it != nodeMap_.end()) + { + getInternal(it->second, value); + return true; + } + + return false; + } + + Value get(Key key) override + { + Value value; + get(key, value); + return value; + } + + // 清空缓存,回收资源 + void purge() + { + nodeMap_.clear(); + freqToFreqList_.clear(); + } + +private: + void putInternal(Key key, Value value); // 添加缓存 + void getInternal(NodePtr node, Value& value); // 获取缓存 + + void kickOut(); // 移除缓存中的过期数据 + + void removeFromFreqList(NodePtr node); // 从频率列表中移除节点 + void addToFreqList(NodePtr node); // 添加到频率列表 + + void addFreqNum(); // 增加平均访问等频率 + void decreaseFreqNum(int num); // 减少平均访问等频率 + void handleOverMaxAverageNum(); // 处理当前平均访问频率超过上限的情况 + void updateMinFreq(); + +private: + int capacity_; // 缓存容量 + int minFreq_; // 最小访问频次(用于找到最小访问频次结点) + int maxAverageNum_; // 最大平均访问频次 + int curAverageNum_; // 当前平均访问频次 + int curTotalNum_; // 当前访问所有缓存次数总数 + std::mutex mutex_; // 互斥锁 + NodeMap nodeMap_; // key 到 缓存节点的映射 + std::unordered_map*> freqToFreqList_;// 访问频次到该频次链表的映射 +}; + +template +void KLfuCache::getInternal(NodePtr node, Value& value) +{ + // 找到之后需要将其从低访问频次的链表中删除,并且添加到+1的访问频次链表中, + // 访问频次+1, 然后把value值返回 + value = node->value; + // 从原有访问频次的链表中删除节点 + removeFromFreqList(node); + node->freq++; + addToFreqList(node); + // 如果当前node的访问频次如果等于minFreq+1,并且其前驱链表为空,则说明 + // freqToFreqList_[node->freq - 1]链表因node的迁移已经空了,需要更新最小访问频次 + if (node->freq - 1 == minFreq_ && freqToFreqList_[node->freq - 1]->isEmpty()) + minFreq_++; + + // 总访问频次和当前平均访问频次都随之增加 + addFreqNum(); +} + +template +void KLfuCache::putInternal(Key key, Value value) +{ + // 如果不在缓存中,则需要判断缓存是否已满 + if (nodeMap_.size() == capacity_) + { + // 缓存已满,删除最不常访问的结点,更新当前平均访问频次和总访问频次 + kickOut(); + } + + // 创建新结点,将新结点添加进入,更新最小访问频次 + NodePtr node = std::make_shared(key, value); + nodeMap_[key] = node; + addToFreqList(node); + addFreqNum(); + minFreq_ = std::min(minFreq_, 1); +} + +template +void KLfuCache::kickOut() +{ + NodePtr node = freqToFreqList_[minFreq_]->getFirstNode(); + removeFromFreqList(node); + nodeMap_.erase(node->key); + decreaseFreqNum(node->freq); +} + +template +void KLfuCache::removeFromFreqList(NodePtr node) +{ + // 检查结点是否为空 + if (!node) + return; + + auto freq = node->freq; + freqToFreqList_[freq]->removeNode(node); +} + +template +void KLfuCache::addToFreqList(NodePtr node) +{ + // 检查结点是否为空 + if (!node) + return; + + // 添加进入相应的频次链表前需要判断该频次链表是否存在 + auto freq = node->freq; + if (freqToFreqList_.find(node->freq) == freqToFreqList_.end()) + { + // 不存在则创建 + freqToFreqList_[node->freq] = new FreqList(node->freq); + } + + freqToFreqList_[freq]->addNode(node); +} + +template +void KLfuCache::addFreqNum() +{ + curTotalNum_++; + if (nodeMap_.empty()) + curAverageNum_ = 0; + else + curAverageNum_ = curTotalNum_ / nodeMap_.size(); + + if (curAverageNum_ > maxAverageNum_) + { + handleOverMaxAverageNum(); + } +} + +template +void KLfuCache::decreaseFreqNum(int num) +{ + // 减少平均访问频次和总访问频次 + curTotalNum_ -= num; + if (nodeMap_.empty()) + curAverageNum_ = 0; + else + curAverageNum_ = curTotalNum_ / nodeMap_.size(); +} + +template +void KLfuCache::handleOverMaxAverageNum() +{ + if (nodeMap_.empty()) + return; + + // 当前平均访问频次已经超过了最大平均访问频次,所有结点的访问频次- (maxAverageNum_ / 2) + for (auto it = nodeMap_.begin(); it != nodeMap_.end(); ++it) + { + // 检查结点是否为空 + if (!it->second) + continue; + + NodePtr node = it->second; + + // 先从当前频率列表中移除 + removeFromFreqList(node); + + // 减少频率 + node->freq -= maxAverageNum_ / 2; + if (node->freq < 1) node->freq = 1; + + // 添加到新的频率列表 + addToFreqList(node); + } + + // 更新最小频率 + updateMinFreq(); +} + +template +void KLfuCache::updateMinFreq() +{ + minFreq_ = INT8_MAX; + for (const auto& pair : freqToFreqList_) + { + if (pair.second && !pair.second->isEmpty()) + { + minFreq_ = std::min(minFreq_, pair.first); + } + } + if (minFreq_ == INT8_MAX) + minFreq_ = 1; +} + +// 并没有牺牲空间换时间,他是把原有缓存大小进行了分片。 +template +class KHashLfuCache +{ +public: + KHashLfuCache(size_t capacity, int sliceNum, int maxAverageNum = 10) + : sliceNum_(sliceNum > 0 ? sliceNum : std::thread::hardware_concurrency()) + , capacity_(capacity) + { + size_t sliceSize = std::ceil(capacity_ / static_cast(sliceNum_)); // 每个lfu分片的容量 + for (int i = 0; i < sliceNum_; ++i) + { + lfuSliceCaches_.emplace_back(new KLfuCache(sliceSize, maxAverageNum)); + } + } + + void put(Key key, Value value) + { + // 根据key找出对应的lfu分片 + size_t sliceIndex = Hash(key) % sliceNum_; + lfuSliceCaches_[sliceIndex]->put(key, value); + } + + bool get(Key key, Value& value) + { + // 根据key找出对应的lfu分片 + size_t sliceIndex = Hash(key) % sliceNum_; + return lfuSliceCaches_[sliceIndex]->get(key, value); + } + + Value get(Key key) + { + Value value; + get(key, value); + return value; + } + + // 清除缓存 + void purge() + { + for (auto& lfuSliceCache : lfuSliceCaches_) + { + lfuSliceCache->purge(); + } + } + +private: + // 将key计算成对应哈希值 + size_t Hash(Key key) + { + std::hash hashFunc; + return hashFunc(key); + } + +private: + size_t capacity_; // 缓存总容量 + int sliceNum_; // 缓存分片数量 + std::vector>> lfuSliceCaches_; // 缓存lfu分片容器 +}; + +} // namespace KamaCache + diff --git a/KLruCache.h b/KLruCache.h new file mode 100644 index 0000000..09c2f89 --- /dev/null +++ b/KLruCache.h @@ -0,0 +1,326 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "KICachePolicy.h" + +namespace KamaCache +{ + +// 前向声明 +template class KLruCache; + +template +class LruNode +{ +private: + Key key_; + Value value_; + size_t accessCount_; // 访问次数 + std::weak_ptr> prev_; // 改为weak_ptr打破循环引用 + std::shared_ptr> next_; + +public: + LruNode(Key key, Value value) + : key_(key) + , value_(value) + , accessCount_(1) + {} + + // 提供必要的访问器 + Key getKey() const { return key_; } + Value getValue() const { return value_; } + void setValue(const Value& value) { value_ = value; } + size_t getAccessCount() const { return accessCount_; } + void incrementAccessCount() { ++accessCount_; } + + friend class KLruCache; +}; + + +template +class KLruCache : public KICachePolicy +{ +public: + using LruNodeType = LruNode; + using NodePtr = std::shared_ptr; + using NodeMap = std::unordered_map; + + KLruCache(int capacity) + : capacity_(capacity) + { + initializeList(); + } + + ~KLruCache() override = default; + + // 添加缓存 + void put(Key key, Value value) override + { + if (capacity_ <= 0) + return; + + std::lock_guard lock(mutex_); + auto it = nodeMap_.find(key); + if (it != nodeMap_.end()) + { + // 如果在当前容器中,则更新value,并调用get方法,代表该数据刚被访问 + updateExistingNode(it->second, value); + return ; + } + + addNewNode(key, value); + } + + bool get(Key key, Value& value) override + { + std::lock_guard lock(mutex_); + auto it = nodeMap_.find(key); + if (it != nodeMap_.end()) + { + moveToMostRecent(it->second); + value = it->second->getValue(); + return true; + } + return false; + } + + Value get(Key key) override + { + Value value{}; + // memset(&value, 0, sizeof(value)); // memset 是按字节设置内存的,对于复杂类型(如 string)使用 memset 可能会破坏对象的内部结构 + get(key, value); + return value; + } + + // 删除指定元素 + void remove(Key key) + { + std::lock_guard lock(mutex_); + auto it = nodeMap_.find(key); + if (it != nodeMap_.end()) + { + removeNode(it->second); + nodeMap_.erase(it); + } + } + +private: + void initializeList() + { + // 创建首尾虚拟节点 + dummyHead_ = std::make_shared(Key(), Value()); + dummyTail_ = std::make_shared(Key(), Value()); + dummyHead_->next_ = dummyTail_; + dummyTail_->prev_ = dummyHead_; + } + + void updateExistingNode(NodePtr node, const Value& value) + { + node->setValue(value); + moveToMostRecent(node); + } + + void addNewNode(const Key& key, const Value& value) + { + if (nodeMap_.size() >= capacity_) + { + evictLeastRecent(); + } + + NodePtr newNode = std::make_shared(key, value); + insertNode(newNode); + nodeMap_[key] = newNode; + } + + // 将该节点移动到最新的位置 + void moveToMostRecent(NodePtr node) + { + removeNode(node); + insertNode(node); + } + + void removeNode(NodePtr node) + { + if(!node->prev_.expired() && node->next_) + { + auto prev = node->prev_.lock(); // 使用lock()获取shared_ptr + prev->next_ = node->next_; + node->next_->prev_ = prev; + node->next_ = nullptr; // 清空next_指针,彻底断开节点与链表的连接 + } + } + + // 从尾部插入结点 + void insertNode(NodePtr node) + { + node->next_ = dummyTail_; + node->prev_ = dummyTail_->prev_; + dummyTail_->prev_.lock()->next_ = node; // 使用lock()获取shared_ptr + dummyTail_->prev_ = node; + } + + // 驱逐最近最少访问 + void evictLeastRecent() + { + NodePtr leastRecent = dummyHead_->next_; + removeNode(leastRecent); + nodeMap_.erase(leastRecent->getKey()); + } + +private: + int capacity_; // 缓存容量 + NodeMap nodeMap_; // key -> Node + std::mutex mutex_; + NodePtr dummyHead_; // 虚拟头结点 + NodePtr dummyTail_; +}; + +// LRU优化:Lru-k版本。 通过继承的方式进行再优化 +template +class KLruKCache : public KLruCache +{ +public: + KLruKCache(int capacity, int historyCapacity, int k) + : KLruCache(capacity) // 调用基类构造 + , historyList_(std::make_unique>(historyCapacity)) + , k_(k) + {} + + Value get(Key key) + { + // 首先尝试从主缓存获取数据 + Value value{}; + bool inMainCache = KLruCache::get(key, value); + + // 获取并更新访问历史计数 + size_t historyCount = historyList_->get(key); + historyCount++; + historyList_->put(key, historyCount); + + // 如果数据在主缓存中,直接返回 + if (inMainCache) + { + return value; + } + + // 如果数据不在主缓存,但访问次数达到了k次 + if (historyCount >= k_) + { + // 检查是否有历史值记录 + auto it = historyValueMap_.find(key); + if (it != historyValueMap_.end()) + { + // 有历史值,将其添加到主缓存 + Value storedValue = it->second; + + // 从历史记录移除 + historyList_->remove(key); + historyValueMap_.erase(it); + + // 添加到主缓存 + KLruCache::put(key, storedValue); + + return storedValue; + } + // 没有历史值记录,无法添加到缓存,返回默认值 + } + + // 数据不在主缓存且不满足添加条件,返回默认值 + return value; + } + + void put(Key key, Value value) + { + // 检查是否已在主缓存 + Value existingValue{}; + bool inMainCache = KLruCache::get(key, existingValue); + + if (inMainCache) + { + // 已在主缓存,直接更新 + KLruCache::put(key, value); + return; + } + + // 获取并更新访问历史 + size_t historyCount = historyList_->get(key); + historyCount++; + historyList_->put(key, historyCount); + + // 保存值到历史记录映射,供后续get操作使用 + historyValueMap_[key] = value; + + // 检查是否达到k次访问阈值 + if (historyCount >= k_) + { + // 达到阈值,添加到主缓存 + historyList_->remove(key); + historyValueMap_.erase(key); + KLruCache::put(key, value); + } + } + +private: + int k_; // 进入缓存队列的评判标准 + std::unique_ptr> historyList_; // 访问数据历史记录(value为访问次数) + std::unordered_map historyValueMap_; // 存储未达到k次访问的数据值 +}; + +// lru优化:对lru进行分片,提高高并发使用的性能 +template +class KHashLruCaches +{ +public: + KHashLruCaches(size_t capacity, int sliceNum) + : capacity_(capacity) + , sliceNum_(sliceNum > 0 ? sliceNum : std::thread::hardware_concurrency()) + { + size_t sliceSize = std::ceil(capacity / static_cast(sliceNum_)); // 获取每个分片的大小 + for (int i = 0; i < sliceNum_; ++i) + { + lruSliceCaches_.emplace_back(new KLruCache(sliceSize)); + } + } + + void put(Key key, Value value) + { + // 获取key的hash值,并计算出对应的分片索引 + size_t sliceIndex = Hash(key) % sliceNum_; + lruSliceCaches_[sliceIndex]->put(key, value); + } + + bool get(Key key, Value& value) + { + // 获取key的hash值,并计算出对应的分片索引 + size_t sliceIndex = Hash(key) % sliceNum_; + return lruSliceCaches_[sliceIndex]->get(key, value); + } + + Value get(Key key) + { + Value value; + memset(&value, 0, sizeof(value)); + get(key, value); + return value; + } + +private: + // 将key转换为对应hash值 + size_t Hash(Key key) + { + std::hash hashFunc; + return hashFunc(key); + } + +private: + size_t capacity_; // 总容量 + int sliceNum_; // 切片数量 + std::vector>> lruSliceCaches_; // 切片LRU缓存 +}; + +} // namespace KamaCache \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f9de5c --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# KamaCache + +> ⭐️ 本项目为[【代码随想录知识星球】](https://programmercarl.com/other/kstar.html) 教学项目 +> ⭐️ 在 [缓存项目文档](https://www.programmercarl.com/other/project_huancun.html) 里详细讲解:**项目前置知识 + 项目细节 + 代码解读 + 项目难点 + 面试题与回答 + 简历写法 + 项目拓展**。 全面帮助你用这个项目求职面试! + + +## 项目介绍 +本项目使用多个页面替换策略实现一个线程安全的缓存: +- LRU:最近最久未使用 +- LFU:最近不经常使用 +- ARC:自适应替换 + +对于LRU和LFU策略,我在其基础的缓存策略上进行了相应的优化,例如: + +- LRU优化: + - LRU分片:对多线程下的高并发访问有性能上的优化 + - LRU-k:一定程度上防止热点数据被冷数据挤出容器而造成缓存污染等问题 + +- LFU优化: + - LFU分片:对多线程下的高并发访问有性能上的优化 + - 引入最大平均访问频次:解决过去的热点数据最近一直没被访问,却仍占用缓存等问题 + +## 系统环境 +``` +Ubuntu 22.04 LTS +``` +## 编译 +创建一个build文件夹并进入 +``` +mkdir build && cd build +``` +生成构建文件 +``` +cmake .. +``` +构建项目 +``` +make +``` +如果要清理生成的可执行文件 +``` +make clean +``` + +## 运行 +``` +./main +``` + +## 测试结果 +不同缓存策略缓存命中率测试对比结果如下: +(ps: 该测试代码只是尽可能地模拟真实的访问场景,但是跟真实的场景仍存在一定差距,测试结果仅供参考。) + +![alt text](images/hitTest.jpg) diff --git a/images/hitTest.jpg b/images/hitTest.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5816558002b6d3d308603df4df597a85a9c314bc GIT binary patch literal 128994 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%0DBm>u%S^(DnOElf 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∨!c@i0visX$tN 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 literal 0 HcmV?d00001 diff --git a/testAllCachePolicy.cpp b/testAllCachePolicy.cpp new file mode 100644 index 0000000..44a7569 --- /dev/null +++ b/testAllCachePolicy.cpp @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "KICachePolicy.h" +#include "KLfuCache.h" +#include "KLruCache.h" +#include "KArcCache/KArcCache.h" + +class Timer { +public: + Timer() : start_(std::chrono::high_resolution_clock::now()) {} + + double elapsed() { + auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(now - start_).count(); + } + +private: + std::chrono::time_point start_; +}; + +// 辅助函数:打印结果 +void printResults(const std::string& testName, int capacity, + const std::vector& get_operations, + const std::vector& hits) { + std::cout << "=== " << testName << " 结果汇总 ===" << std::endl; + std::cout << "缓存大小: " << capacity << std::endl; + + // 假设对应的算法名称已在测试函数中定义 + std::vector names; + if (hits.size() == 3) { + names = {"LRU", "LFU", "ARC"}; + } else if (hits.size() == 4) { + names = {"LRU", "LFU", "ARC", "LRU-K"}; + } else if (hits.size() == 5) { + names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"}; + } + + for (size_t i = 0; i < hits.size(); ++i) { + double hitRate = 100.0 * hits[i] / get_operations[i]; + std::cout << (i < names.size() ? names[i] : "Algorithm " + std::to_string(i+1)) + << " - 命中率: " << std::fixed << std::setprecision(2) + << hitRate << "% "; + // 添加具体命中次数和总操作次数 + std::cout << "(" << hits[i] << "/" << get_operations[i] << ")" << std::endl; + } + + std::cout << std::endl; // 添加空行,使输出更清晰 +} + +void testHotDataAccess() { + std::cout << "\n=== 测试场景1:热点数据访问测试 ===" << std::endl; + + const int CAPACITY = 20; // 缓存容量 + const int OPERATIONS = 500000; // 总操作次数 + const int HOT_KEYS = 20; // 热点数据数量 + const int COLD_KEYS = 5000; // 冷数据数量 + + KamaCache::KLruCache lru(CAPACITY); + KamaCache::KLfuCache lfu(CAPACITY); + KamaCache::KArcCache arc(CAPACITY); + // 为LRU-K设置合适的参数: + // - 主缓存容量与其他算法相同 + // - 历史记录容量设为可能访问的所有键数量 + // - k=2表示数据被访问2次后才会进入缓存,适合区分热点和冷数据 + KamaCache::KLruKCache lruk(CAPACITY, HOT_KEYS + COLD_KEYS, 2); + KamaCache::KLfuCache lfuAging(CAPACITY, 20000); + + std::random_device rd; + std::mt19937 gen(rd()); + + // 基类指针指向派生类对象,添加LFU-Aging + std::array*, 5> caches = {&lru, &lfu, &arc, &lruk, &lfuAging}; + std::vector hits(5, 0); + std::vector get_operations(5, 0); + std::vector names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"}; + + // 为所有的缓存对象进行相同的操作序列测试 + for (int i = 0; i < caches.size(); ++i) { + // 先预热缓存,插入一些数据 + for (int key = 0; key < HOT_KEYS; ++key) { + std::string value = "value" + std::to_string(key); + caches[i]->put(key, value); + } + + // 交替进行put和get操作,模拟真实场景 + for (int op = 0; op < OPERATIONS; ++op) { + // 大多数缓存系统中读操作比写操作频繁 + // 所以设置30%概率进行写操作 + bool isPut = (gen() % 100 < 30); + int key; + + // 70%概率访问热点数据,30%概率访问冷数据 + if (gen() % 100 < 70) { + key = gen() % HOT_KEYS; // 热点数据 + } else { + key = HOT_KEYS + (gen() % COLD_KEYS); // 冷数据 + } + + if (isPut) { + // 执行put操作 + std::string value = "value" + std::to_string(key) + "_v" + std::to_string(op % 100); + caches[i]->put(key, value); + } else { + // 执行get操作并记录命中情况 + std::string result; + get_operations[i]++; + if (caches[i]->get(key, result)) { + hits[i]++; + } + } + } + } + + // 打印测试结果 + printResults("热点数据访问测试", CAPACITY, get_operations, hits); +} + +void testLoopPattern() { + std::cout << "\n=== 测试场景2:循环扫描测试 ===" << std::endl; + + const int CAPACITY = 50; // 缓存容量 + const int LOOP_SIZE = 500; // 循环范围大小 + const int OPERATIONS = 200000; // 总操作次数 + + KamaCache::KLruCache lru(CAPACITY); + KamaCache::KLfuCache lfu(CAPACITY); + KamaCache::KArcCache arc(CAPACITY); + // 为LRU-K设置合适的参数: + // - 历史记录容量设为总循环大小的两倍,覆盖范围内和范围外的数据 + // - k=2,对于循环访问,这是一个合理的阈值 + KamaCache::KLruKCache lruk(CAPACITY, LOOP_SIZE * 2, 2); + KamaCache::KLfuCache lfuAging(CAPACITY, 3000); + + std::array*, 5> caches = {&lru, &lfu, &arc, &lruk, &lfuAging}; + std::vector hits(5, 0); + std::vector get_operations(5, 0); + std::vector names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"}; + + std::random_device rd; + std::mt19937 gen(rd()); + + // 为每种缓存算法运行相同的测试 + for (int i = 0; i < caches.size(); ++i) { + // 先预热一部分数据(只加载20%的数据) + for (int key = 0; key < LOOP_SIZE / 5; ++key) { + std::string value = "loop" + std::to_string(key); + caches[i]->put(key, value); + } + + // 设置循环扫描的当前位置 + int current_pos = 0; + + // 交替进行读写操作,模拟真实场景 + for (int op = 0; op < OPERATIONS; ++op) { + // 20%概率是写操作,80%概率是读操作 + bool isPut = (gen() % 100 < 20); + int key; + + // 按照不同模式选择键 + if (op % 100 < 60) { // 60%顺序扫描 + key = current_pos; + current_pos = (current_pos + 1) % LOOP_SIZE; + } else if (op % 100 < 90) { // 30%随机跳跃 + key = gen() % LOOP_SIZE; + } else { // 10%访问范围外数据 + key = LOOP_SIZE + (gen() % LOOP_SIZE); + } + + if (isPut) { + // 执行put操作,更新数据 + std::string value = "loop" + std::to_string(key) + "_v" + std::to_string(op % 100); + caches[i]->put(key, value); + } else { + // 执行get操作并记录命中情况 + std::string result; + get_operations[i]++; + if (caches[i]->get(key, result)) { + hits[i]++; + } + } + } + } + + printResults("循环扫描测试", CAPACITY, get_operations, hits); +} + +void testWorkloadShift() { + std::cout << "\n=== 测试场景3:工作负载剧烈变化测试 ===" << std::endl; + + const int CAPACITY = 30; // 缓存容量 + const int OPERATIONS = 80000; // 总操作次数 + const int PHASE_LENGTH = OPERATIONS / 5; // 每个阶段的长度 + + KamaCache::KLruCache lru(CAPACITY); + KamaCache::KLfuCache lfu(CAPACITY); + KamaCache::KArcCache arc(CAPACITY); + KamaCache::KLruKCache lruk(CAPACITY, 500, 2); + KamaCache::KLfuCache lfuAging(CAPACITY, 10000); + + std::random_device rd; + std::mt19937 gen(rd()); + std::array*, 5> caches = {&lru, &lfu, &arc, &lruk, &lfuAging}; + std::vector hits(5, 0); + std::vector get_operations(5, 0); + std::vector names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"}; + + // 为每种缓存算法运行相同的测试 + for (int i = 0; i < caches.size(); ++i) { + // 先预热缓存,只插入少量初始数据 + for (int key = 0; key < 30; ++key) { + std::string value = "init" + std::to_string(key); + caches[i]->put(key, value); + } + + // 进行多阶段测试,每个阶段有不同的访问模式 + for (int op = 0; op < OPERATIONS; ++op) { + // 确定当前阶段 + int phase = op / PHASE_LENGTH; + + // 每个阶段的读写比例不同 + int putProbability; + switch (phase) { + case 0: putProbability = 15; break; // 阶段1: 热点访问,15%写入更合理 + case 1: putProbability = 30; break; // 阶段2: 大范围随机,写比例为30% + case 2: putProbability = 10; break; // 阶段3: 顺序扫描,10%写入保持不变 + case 3: putProbability = 25; break; // 阶段4: 局部性随机,微调为25% + case 4: putProbability = 20; break; // 阶段5: 混合访问,调整为20% + default: putProbability = 20; + } + + // 确定是读还是写操作 + bool isPut = (gen() % 100 < putProbability); + + // 根据不同阶段选择不同的访问模式生成key - 优化后的访问范围 + int key; + if (op < PHASE_LENGTH) { // 阶段1: 热点访问 - 热点数量5,使热点更集中 + key = gen() % 5; + } else if (op < PHASE_LENGTH * 2) { // 阶段2: 大范围随机 - 范围400,更适合30大小的缓存 + key = gen() % 400; + } else if (op < PHASE_LENGTH * 3) { // 阶段3: 顺序扫描 - 保持100个键 + key = (op - PHASE_LENGTH * 2) % 100; + } else if (op < PHASE_LENGTH * 4) { // 阶段4: 局部性随机 - 优化局部性区域大小 + // 产生5个局部区域,每个区域大小为15个键,与缓存大小20接近但略小 + int locality = (op / 800) % 5; // 调整为5个局部区域 + key = locality * 15 + (gen() % 15); // 每区域15个键 + } else { // 阶段5: 混合访问 - 增加热点访问比例 + int r = gen() % 100; + if (r < 40) { // 40%概率访问热点(从30%增加) + key = gen() % 5; // 5个热点键 + } else if (r < 70) { // 30%概率访问中等范围 + key = 5 + (gen() % 45); // 缩小中等范围为50个键 + } else { // 30%概率访问大范围(从40%减少) + key = 50 + (gen() % 350); // 大范围也相应缩小 + } + } + + if (isPut) { + // 执行写操作 + std::string value = "value" + std::to_string(key) + "_p" + std::to_string(phase); + caches[i]->put(key, value); + } else { + // 执行读操作并记录命中情况 + std::string result; + get_operations[i]++; + if (caches[i]->get(key, result)) { + hits[i]++; + } + } + } + } + + printResults("工作负载剧烈变化测试", CAPACITY, get_operations, hits); +} + +int main() { + testHotDataAccess(); + testLoopPattern(); + testWorkloadShift(); + return 0; +}