From 0bafdc98c76aa19d23a46f3ec0ebd9c41bc5e040 Mon Sep 17 00:00:00 2001 From: tqcq <99722391+tqcq@users.noreply.github.com> Date: Fri, 12 Apr 2024 22:41:45 +0800 Subject: [PATCH] feat add cache --- CMakeLists.txt | 2 + src/sled/cache/abstract_cache.h | 191 ++++++++++++++++++ src/sled/cache/cache.h | 9 + src/sled/cache/expire_cache.h | 16 ++ src/sled/cache/fifo_cache.h | 18 ++ src/sled/cache/fifo_cache_test.cc | 28 +++ src/sled/cache/lru_cache.h | 18 ++ src/sled/cache/lru_cache_test.cc | 30 +++ src/sled/cache/policy/abstract_cache_policy.h | 30 +++ src/sled/cache/policy/default_cache_policy.h | 31 +++ src/sled/cache/policy/expire_cache_policy.h | 71 +++++++ src/sled/cache/policy/fifo_cache_policy.h | 57 ++++++ src/sled/cache/policy/lru_cache_policy.h | 74 +++++++ src/sled/lang/attributes.h | 2 - src/sled/sled.h | 2 + src/sled/units/timestamp.h | 17 +- 16 files changed, 581 insertions(+), 15 deletions(-) create mode 100644 src/sled/cache/abstract_cache.h create mode 100644 src/sled/cache/cache.h create mode 100644 src/sled/cache/expire_cache.h create mode 100644 src/sled/cache/fifo_cache.h create mode 100644 src/sled/cache/fifo_cache_test.cc create mode 100644 src/sled/cache/lru_cache.h create mode 100644 src/sled/cache/lru_cache_test.cc create mode 100644 src/sled/cache/policy/abstract_cache_policy.h create mode 100644 src/sled/cache/policy/default_cache_policy.h create mode 100644 src/sled/cache/policy/expire_cache_policy.h create mode 100644 src/sled/cache/policy/fifo_cache_policy.h create mode 100644 src/sled/cache/policy/lru_cache_policy.h diff --git a/CMakeLists.txt b/CMakeLists.txt index dda367e..be7bc74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,6 +201,8 @@ if(SLED_BUILD_TESTS) sled_add_test(NAME sled_ioc_test SRCS src/sled/ioc/ioc_test.cc) sled_add_test(NAME sled_inja_test SRCS src/sled/nonstd/inja_test.cc) sled_add_test(NAME sled_fsm_test SRCS src/sled/nonstd/fsm_test.cc) + sled_add_test(NAME sled_cache_test SRCS src/sled/cache/lru_cache_test.cc + src/sled/cache/fifo_cache_test.cc) endif(SLED_BUILD_TESTS) if(SLED_BUILD_FUZZ) diff --git a/src/sled/cache/abstract_cache.h b/src/sled/cache/abstract_cache.h new file mode 100644 index 0000000..8be6795 --- /dev/null +++ b/src/sled/cache/abstract_cache.h @@ -0,0 +1,191 @@ +#ifndef SLED_CACHE_CACHE_H +#define SLED_CACHE_CACHE_H + +#pragma once +#include "policy/abstract_cache_policy.h" +#include "sled/synchronization/mutex.h" + +namespace sled { +template> +class AbstractCache { + using Iterator = typename std::map>::iterator; + +public: + AbstractCache() { DoInitialize(); } + + virtual ~AbstractCache() { DoUninitialize(); } + + void Add(const TKey &key, const TValue &value) + { + sled::MutexLock lock(&mutex_); + DoAdd(key, value); + } + + void Update(const TKey &key, const TValue &value) + { + sled::MutexLock lock(&mutex_); + DoUpdate(key, value); + } + + void Add(const TKey &key, std::shared_ptr value) + { + sled::MutexLock lock(&mutex_); + DoAdd(key, value); + } + + void Update(const TKey &key, std::shared_ptr value) + { + sled::MutexLock lock(&mutex_); + DoUpdate(key, value); + } + + void Remove(const TKey &key) + { + sled::MutexLock lock(&mutex_); + Iterator iter = data_.find(key); + DoRemove(iter); + } + + bool Has(const TKey &key) + { + sled::MutexLock lock(&mutex_); + return DoHas(key); + } + + std::shared_ptr Get(const TKey &key) + { + sled::MutexLock lock(&mutex_); + return DoGet(key); + } + + void Clear() + { + sled::MutexLock lock(&mutex_); + DoClear(); + } + + std::size_t size() + { + sled::MutexLock lock(&mutex_); + DoReplace(); + return data_.size(); + } + + std::size_t empty() { return size() == 0; } + + void ForceReplace() + { + sled::MutexLock lock(&mutex_); + DoReplace(); + } + + std::set GetAllKeys() + { + sled::MutexLock lock(&mutex_); + DoReplace(); + std::set keys; + for (const auto &pair : data_) { keys.insert(pair.first); } + return keys; + } + +protected: + void DoInitialize() {} + + void DoUninitialize() {} + + void DoAdd(const TKey &key, const TValue &value) + { + auto value_ptr = std::shared_ptr(new TValue(value)); + DoAdd(key, value_ptr); + } + + void DoAdd(const TKey &key, std::shared_ptr value) + { + Iterator iter = data_.find(key); + DoRemove(iter); + for (auto &policy : policy_) { policy->OnAdd(key); } + data_.insert(std::make_pair(key, value)); + DoReplace(); + } + + void DoUpdate(const TKey &key, const TValue &value) + { + auto value_ptr = std::shared_ptr(new TValue(value)); + DoUpdate(key, value_ptr); + } + + void DoUpdate(const TKey &key, std::shared_ptr value) + { + Iterator iter = data_.find(key); + if (iter != data_.end()) { + for (auto &policy : policy_) { policy->OnRemove(iter->first); } + iter->second = value; + } else { + for (auto &policy : policy_) { policy->OnAdd(key); } + data_.insert(std::make_pair(key, value)); + } + DoReplace(); + } + + void DoRemove(Iterator iter) + { + if (iter != data_.end()) { + for (auto &policy : policy_) { policy->OnRemove(iter->first); } + data_.erase(iter); + } + } + + bool DoHas(const TKey &key) + { + Iterator iter = data_.find(key); + if (iter != data_.end()) { + bool valid = true; + for (auto &policy : policy_) { valid = policy->IsValid(key) && valid; } + return valid; + } + return false; + } + + std::shared_ptr DoGet(const TKey &key) + { + Iterator iter = data_.find(key); + if (iter != data_.end()) { + for (auto &policy : policy_) { policy->OnGet(key); } + bool valid = true; + for (auto &policy : policy_) { valid = policy->IsValid(key) && valid; } + if (!valid) { + DoRemove(iter); + } else { + return iter->second; + } + } + + return nullptr; + } + + void DoClear() + { + for (auto &policy : policy_) { policy->OnClear(); } + data_.clear(); + } + + void DoReplace() + { + std::set del_me; + for (auto &policy : policy_) { policy->OnReplace(del_me); } + for (const TKey &key : del_me) { + auto iter = data_.find(key); + DoRemove(iter); + } + } + +protected: + std::set> policy_; + +private: + sled::Mutex mutex_; + std::map> data_; +}; +}// namespace sled + +#endif// SLED_CACHE_CACHE_H diff --git a/src/sled/cache/cache.h b/src/sled/cache/cache.h new file mode 100644 index 0000000..2f0ecbb --- /dev/null +++ b/src/sled/cache/cache.h @@ -0,0 +1,9 @@ +#ifndef SLED_SLED_CACHE_CACHE_H +#define SLED_SLED_CACHE_CACHE_H + +#pragma once +#include "expire_cache.h" +#include "fifo_cache.h" +#include "lru_cache.h" + +#endif// SLED_SLED_CACHE_CACHE_H diff --git a/src/sled/cache/expire_cache.h b/src/sled/cache/expire_cache.h new file mode 100644 index 0000000..223e0d4 --- /dev/null +++ b/src/sled/cache/expire_cache.h @@ -0,0 +1,16 @@ +#ifndef SLED_SLED_CACHE_EXPIRE_CACHE_H +#define SLED_SLED_CACHE_EXPIRE_CACHE_H + +#pragma once +#include "abstract_cache.h" +#include "policy/expire_cache_policy.h" + +namespace sled { +template +class ExpireCache : public AbstractCache> { +public: + ~ExpireCache() override = default; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_EXPIRE_CACHE_H diff --git a/src/sled/cache/fifo_cache.h b/src/sled/cache/fifo_cache.h new file mode 100644 index 0000000..2239b0d --- /dev/null +++ b/src/sled/cache/fifo_cache.h @@ -0,0 +1,18 @@ +#ifndef SLED_SLED_CACHE_FIFO_CACHE_H +#define SLED_SLED_CACHE_FIFO_CACHE_H + +#pragma once +#include "abstract_cache.h" +#include "policy/fifo_cache_policy.h" + +namespace sled { +template +class FIFOCache : public AbstractCache> { +public: + FIFOCache(std::size_t size) { this->policy_.insert(std::make_shared>(size)); } + + ~FIFOCache() override = default; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_FIFO_CACHE_H diff --git a/src/sled/cache/fifo_cache_test.cc b/src/sled/cache/fifo_cache_test.cc new file mode 100644 index 0000000..1b53ea9 --- /dev/null +++ b/src/sled/cache/fifo_cache_test.cc @@ -0,0 +1,28 @@ +#include + +TEST_SUITE("FIFO Cache") +{ + TEST_CASE("Remove Oldest Key") + { + sled::FIFOCache fifo_cache(3); + CHECK(fifo_cache.empty()); + fifo_cache.Add(1, 1); + CHECK_EQ(fifo_cache.size(), 1); + fifo_cache.Add(2, 2); + CHECK_EQ(fifo_cache.size(), 2); + fifo_cache.Add(3, 3); + CHECK_EQ(fifo_cache.size(), 3); + + CHECK(fifo_cache.Has(1)); + CHECK(fifo_cache.Has(2)); + CHECK(fifo_cache.Has(3)); + + CHECK_EQ(*fifo_cache.Get(1), 1); + CHECK_EQ(*fifo_cache.Get(2), 2); + CHECK_EQ(*fifo_cache.Get(3), 3); + + fifo_cache.Add(4, 4); + CHECK_EQ(fifo_cache.size(), 3); + CHECK_FALSE(fifo_cache.Has(1)); + } +} diff --git a/src/sled/cache/lru_cache.h b/src/sled/cache/lru_cache.h new file mode 100644 index 0000000..139685a --- /dev/null +++ b/src/sled/cache/lru_cache.h @@ -0,0 +1,18 @@ +#ifndef SLED_SLED_CACHE_LRU_CACHE_H +#define SLED_SLED_CACHE_LRU_CACHE_H + +#pragma once +#include "abstract_cache.h" +#include "policy/lru_cache_policy.h" + +namespace sled { +template +class LRUCache : public AbstractCache> { +public: + LRUCache(std::size_t size) { this->policy_.insert(std::make_shared>(size)); } + + ~LRUCache() override = default; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_LRU_CACHE_H diff --git a/src/sled/cache/lru_cache_test.cc b/src/sled/cache/lru_cache_test.cc new file mode 100644 index 0000000..fe1f03a --- /dev/null +++ b/src/sled/cache/lru_cache_test.cc @@ -0,0 +1,30 @@ +#include + +TEST_SUITE("LRUCache") +{ + TEST_CASE("Remove Unused Key") + { + sled::LRUCache lru_cache(3); + CHECK(lru_cache.empty()); + + lru_cache.Add(1, 1); + CHECK_EQ(lru_cache.size(), 1); + + lru_cache.Add(2, 2); + CHECK_EQ(lru_cache.size(), 2); + + lru_cache.Add(3, 3); + CHECK_EQ(lru_cache.size(), 3); + + CHECK(lru_cache.Has(1)); + CHECK(lru_cache.Has(2)); + CHECK(lru_cache.Has(3)); + CHECK_EQ(*lru_cache.Get(3), 3); + CHECK_EQ(*lru_cache.Get(2), 2); + CHECK_EQ(*lru_cache.Get(1), 1); + + lru_cache.Add(4, 4); + CHECK_EQ(lru_cache.size(), 3); + CHECK_FALSE(lru_cache.Has(3)); + } +} diff --git a/src/sled/cache/policy/abstract_cache_policy.h b/src/sled/cache/policy/abstract_cache_policy.h new file mode 100644 index 0000000..cf880f7 --- /dev/null +++ b/src/sled/cache/policy/abstract_cache_policy.h @@ -0,0 +1,30 @@ +#ifndef SLED_SLED_CACHE_POLICY_ABSTRACT_CACHE_POLICY_H +#define SLED_SLED_CACHE_POLICY_ABSTRACT_CACHE_POLICY_H + +#pragma once +#include + +namespace sled { +template +class AbstractCachePolicy { +public: + virtual ~AbstractCachePolicy() = default; + + virtual void OnUpdate(const TKey &key) + { + OnRemove(key); + OnAdd(key); + } + + virtual void OnAdd(const TKey &key) = 0; + virtual void OnRemove(const TKey &key) = 0; + virtual void OnGet(const TKey &key) = 0; + virtual void OnClear() = 0; + + // 缓存策略指定需要移除哪些元素,实际移除又Cache来决定 + virtual void OnReplace(std::set &elemes_to_remove) = 0; + virtual bool IsValid(const TKey &key) = 0; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_POLICY_ABSTRACT_CACHE_POLICY_H diff --git a/src/sled/cache/policy/default_cache_policy.h b/src/sled/cache/policy/default_cache_policy.h new file mode 100644 index 0000000..57f74aa --- /dev/null +++ b/src/sled/cache/policy/default_cache_policy.h @@ -0,0 +1,31 @@ +#ifndef SLED_SLED_CACHE_POLICY_DEFAULT_CACHE_POLICY_H +#define SLED_SLED_CACHE_POLICY_DEFAULT_CACHE_POLICY_H + +#pragma once + +#include "abstract_cache_policy.h" + +namespace sled { +template +class DefaultCachePolicy : public AbstractCachePolicy { +public: + ~DefaultCachePolicy() override = default; + + void OnAdd(const TKey &key) override { keys_.insert(key); } + + void OnRemove(const TKey &key) override { keys_.erase(key); } + + void OnGet(const TKey &key) override {} + + void OnClear() override { keys_.clear(); } + + void OnReplace(std::set &key) override {} + + bool IsValid(const TKey &key) override { return keys_.find(key) != keys_.end(); } + +private: + std::set keys_; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_POLICY_DEFAULT_CACHE_POLICY_H diff --git a/src/sled/cache/policy/expire_cache_policy.h b/src/sled/cache/policy/expire_cache_policy.h new file mode 100644 index 0000000..d29ab46 --- /dev/null +++ b/src/sled/cache/policy/expire_cache_policy.h @@ -0,0 +1,71 @@ +#ifndef SLED_SLED_CACHE_POLICY_EXPIRE_CACHE_POLICY_H +#define SLED_SLED_CACHE_POLICY_EXPIRE_CACHE_POLICY_H + +#pragma once +#include "abstract_cache_policy.h" +#include "sled/time_utils.h" +#include "sled/units/timestamp.h" +#include + +namespace sled { + +template +class ExpireCachePolicy : public AbstractCachePolicy { +public: + ~ExpireCachePolicy() override = default; + + void OnAdd(const TKey &key) override + { + Timestamp now = Timestamp::Nanos(TimeNanos()); + auto iter = key_index_.insert(std::make_pair(now, key)); + keys_[key] = iter; + } + + void OnRemove(const TKey &key) override + { + auto iter = keys_.find(key); + if (iter != keys_.end()) { + key_index_.erase(iter->second); + keys_.erase(iter); + } + } + + void OnGet(const TKey &key) override + { + // no change + } + + void OnClear() override + { + key_index_.clear(); + keys_.clear(); + } + + void OnReplace(std::set &elems_to_remove) override + { + auto iter = key_index_.begin(); + Timestamp now = Timestamp::Nanos(TimeNanos()); + while (iter != key_index_.end() && iter->first < now) { + elems_to_remove.insert(iter->second); + ++iter; + } + } + + bool IsValid(const TKey &key) override + { + auto iter = keys_.find(key); + if (iter != keys_.end()) { + return iter->second->first + expire_time_ > Timestamp::Nanos(TimeNanos()); + } else { + return false; + } + } + +private: + TimeDelta expire_time_; + std::multimap key_index_; + std::map::iterator> keys_; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_POLICY_EXPIRE_CACHE_POLICY_H diff --git a/src/sled/cache/policy/fifo_cache_policy.h b/src/sled/cache/policy/fifo_cache_policy.h new file mode 100644 index 0000000..7424f8d --- /dev/null +++ b/src/sled/cache/policy/fifo_cache_policy.h @@ -0,0 +1,57 @@ +#ifndef SLED_SLED_CACHE_POLICY_FIFO_CACHE_POLICY_H +#define SLED_SLED_CACHE_POLICY_FIFO_CACHE_POLICY_H + +#pragma once + +#include "default_cache_policy.h" +#include "sled/log/log.h" +#include + +namespace sled { +template +class FIFOCachePolicy : public DefaultCachePolicy { +private: + using BaseClass = DefaultCachePolicy; + +public: + FIFOCachePolicy(size_t size) : size_(size) { SLED_ASSERT(size > 0, "size must be greater than 0"); } + + ~FIFOCachePolicy() override = default; + + void OnAdd(const TKey &key) override + + { + BaseClass::OnAdd(key); + keys_.push_back(key); + } + + void OnRemove(const TKey &key) override + { + if (BaseClass::IsValid(key)) { + keys_.remove(key); + BaseClass::OnRemove(key); + } + } + + void OnGet(const TKey &key) override {} + + void OnClear() override + { + keys_.clear(); + BaseClass::OnClear(); + } + + void OnReplace(std::set &key) override + { + // do nothing + } + + bool IsValid(const TKey &key) override { return BaseClass::IsValid(key); } + +private: + size_t size_; + std::list keys_; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_POLICY_FIFO_CACHE_POLICY_H diff --git a/src/sled/cache/policy/lru_cache_policy.h b/src/sled/cache/policy/lru_cache_policy.h new file mode 100644 index 0000000..4277d1a --- /dev/null +++ b/src/sled/cache/policy/lru_cache_policy.h @@ -0,0 +1,74 @@ +#ifndef SLED_SLED_CACHE_POLICY_LRU_CACHE_POLICY_H +#define SLED_SLED_CACHE_POLICY_LRU_CACHE_POLICY_H + +#pragma once + +#include "abstract_cache_policy.h" +#include "sled/log/log.h" +#include +#include + +namespace sled { +template +class LRUCachePolicy : AbstractCachePolicy { +public: + LRUCachePolicy(size_t size) : size_(size) { SLED_ASSERT(size > 0, "size must be greater than 0"); } + + ~LRUCachePolicy() override = default; + + void OnAdd(const TKey &key) override + + { + keys_.push_front(key); + key_index_[key] = keys_.begin(); + } + + void OnRemove(const TKey &key) override + { + auto iter = key_index_.find(key); + if (iter != key_index_.end()) { + keys_.erase(iter->second); + key_index_.erase(iter); + } + } + + void OnGet(const TKey &key) override + { + auto iter = key_index_.find(key); + if (iter != key_index_.end()) { + keys_.erase(iter->second); + keys_.push_front(key); + iter->second = keys_.begin(); + } + } + + void OnClear() override + { + keys_.clear(); + key_index_.clear(); + } + + void OnReplace(std::set &elems_to_remove) override + { + std::size_t cur_size = key_index_.size(); + if (cur_size < size_) { return; } + std::size_t diff = cur_size - size_; + + auto iter = keys_.rbegin(); + std::size_t index = 0; + while (index++ < diff) { + elems_to_remove.insert(*iter); + if (iter != keys_.rend()) { ++iter; } + } + } + + bool IsValid(const TKey &key) override { return key_index_.find(key) != key_index_.end(); } + +private: + size_t size_; + std::list keys_; + std::map::iterator> key_index_; +}; +}// namespace sled + +#endif// SLED_SLED_CACHE_POLICY_LRU_CACHE_POLICY_H diff --git a/src/sled/lang/attributes.h b/src/sled/lang/attributes.h index 7c25180..b32178d 100644 --- a/src/sled/lang/attributes.h +++ b/src/sled/lang/attributes.h @@ -2,8 +2,6 @@ #define SLED_LANG_ATTRIBUTES_H #pragma once -// #define SLED_DEPRECATED __attribute__((deprecated)) - #if defined(__clang__) && (!defined(SWIG)) #define SLED_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) #else diff --git a/src/sled/sled.h b/src/sled/sled.h index 39c88ed..d029231 100644 --- a/src/sled/sled.h +++ b/src/sled/sled.h @@ -12,6 +12,8 @@ #include "sled/nonstd/string_view.h" #include "toml.hpp" +#include "sled/cache/cache.h" + #include "sled/config.h" // experimental #include "sled/experimental/design_patterns/dispatcher.h" diff --git a/src/sled/units/timestamp.h b/src/sled/units/timestamp.h index 0ec164f..850c96c 100644 --- a/src/sled/units/timestamp.h +++ b/src/sled/units/timestamp.h @@ -39,7 +39,7 @@ public: static constexpr Timestamp Nanos(T value) { static_assert(std::is_arithmetic::value, ""); - return FromValue(value) * 1000; + return FromValue(value * 1000LL); } Timestamp() = delete; @@ -68,20 +68,11 @@ public: return ToMultiple<1000, T>(); } - constexpr int64_t seconds_or(int64_t fallback_value) const - { - return ToFractionOr<1000000>(fallback_value); - } + constexpr int64_t seconds_or(int64_t fallback_value) const { return ToFractionOr<1000000>(fallback_value); } - constexpr int64_t ms_or(int64_t fallback_value) const - { - return ToFractionOr<1000>(fallback_value); - } + constexpr int64_t ms_or(int64_t fallback_value) const { return ToFractionOr<1000>(fallback_value); } - constexpr int64_t us_or(int64_t fallback_value) const - { - return ToValueOr(fallback_value); - } + constexpr int64_t us_or(int64_t fallback_value) const { return ToValueOr(fallback_value); } Timestamp operator+(const TimeDelta delta) const {