feat add cache
Some checks failed
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Failing after 1m40s
linux-x64-gcc / linux-gcc (Debug) (push) Failing after 2m19s
linux-x64-gcc / linux-gcc (Release) (push) Failing after 2m41s
linux-arm-gcc / linux-gcc-armhf (push) Failing after 6m27s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Failing after 7m41s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Failing after 9m20s

This commit is contained in:
tqcq 2024-04-12 22:41:45 +08:00
parent f52d34a312
commit 0bafdc98c7
16 changed files with 581 additions and 15 deletions

View File

@ -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)

191
src/sled/cache/abstract_cache.h vendored Normal file
View File

@ -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<typename TKey, typename TValue, typename TPolicy = AbstractCachePolicy<TKey>>
class AbstractCache {
using Iterator = typename std::map<TKey, std::shared_ptr<TValue>>::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<TValue> value)
{
sled::MutexLock lock(&mutex_);
DoAdd(key, value);
}
void Update(const TKey &key, std::shared_ptr<TValue> 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<TValue> 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<TKey> GetAllKeys()
{
sled::MutexLock lock(&mutex_);
DoReplace();
std::set<TKey> 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<TValue>(new TValue(value));
DoAdd(key, value_ptr);
}
void DoAdd(const TKey &key, std::shared_ptr<TValue> 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<TValue>(new TValue(value));
DoUpdate(key, value_ptr);
}
void DoUpdate(const TKey &key, std::shared_ptr<TValue> 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<TValue> 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<TKey> 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<std::shared_ptr<TPolicy>> policy_;
private:
sled::Mutex mutex_;
std::map<TKey, std::shared_ptr<TValue>> data_;
};
}// namespace sled
#endif// SLED_CACHE_CACHE_H

9
src/sled/cache/cache.h vendored Normal file
View File

@ -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

16
src/sled/cache/expire_cache.h vendored Normal file
View File

@ -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<typename TKey, typename TValue>
class ExpireCache : public AbstractCache<TKey, TValue, ExpireCachePolicy<TKey>> {
public:
~ExpireCache() override = default;
};
}// namespace sled
#endif// SLED_SLED_CACHE_EXPIRE_CACHE_H

18
src/sled/cache/fifo_cache.h vendored Normal file
View File

@ -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<typename TKey, typename TValue>
class FIFOCache : public AbstractCache<TKey, TValue, FIFOCachePolicy<TKey>> {
public:
FIFOCache(std::size_t size) { this->policy_.insert(std::make_shared<FIFOCachePolicy<TKey>>(size)); }
~FIFOCache() override = default;
};
}// namespace sled
#endif// SLED_SLED_CACHE_FIFO_CACHE_H

28
src/sled/cache/fifo_cache_test.cc vendored Normal file
View File

@ -0,0 +1,28 @@
#include <sled/cache/fifo_cache.h>
TEST_SUITE("FIFO Cache")
{
TEST_CASE("Remove Oldest Key")
{
sled::FIFOCache<int, int> 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));
}
}

18
src/sled/cache/lru_cache.h vendored Normal file
View File

@ -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<typename TKey, typename TValue>
class LRUCache : public AbstractCache<TKey, TValue, LRUCachePolicy<TKey>> {
public:
LRUCache(std::size_t size) { this->policy_.insert(std::make_shared<LRUCachePolicy<TKey>>(size)); }
~LRUCache() override = default;
};
}// namespace sled
#endif// SLED_SLED_CACHE_LRU_CACHE_H

30
src/sled/cache/lru_cache_test.cc vendored Normal file
View File

@ -0,0 +1,30 @@
#include <sled/cache/cache.h>
TEST_SUITE("LRUCache")
{
TEST_CASE("Remove Unused Key")
{
sled::LRUCache<int, int> 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));
}
}

View File

@ -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 <set>
namespace sled {
template<typename TKey>
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<TKey> &elemes_to_remove) = 0;
virtual bool IsValid(const TKey &key) = 0;
};
}// namespace sled
#endif// SLED_SLED_CACHE_POLICY_ABSTRACT_CACHE_POLICY_H

View File

@ -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<typename TKey>
class DefaultCachePolicy : public AbstractCachePolicy<TKey> {
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<TKey> &key) override {}
bool IsValid(const TKey &key) override { return keys_.find(key) != keys_.end(); }
private:
std::set<TKey> keys_;
};
}// namespace sled
#endif// SLED_SLED_CACHE_POLICY_DEFAULT_CACHE_POLICY_H

View File

@ -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 <map>
namespace sled {
template<typename TKey>
class ExpireCachePolicy : public AbstractCachePolicy<TKey> {
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<TKey> &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<Timestamp, TKey> key_index_;
std::map<TKey, typename std::multimap<Timestamp, TKey>::iterator> keys_;
};
}// namespace sled
#endif// SLED_SLED_CACHE_POLICY_EXPIRE_CACHE_POLICY_H

View File

@ -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 <list>
namespace sled {
template<typename TKey>
class FIFOCachePolicy : public DefaultCachePolicy<TKey> {
private:
using BaseClass = DefaultCachePolicy<TKey>;
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<TKey> &key) override
{
// do nothing
}
bool IsValid(const TKey &key) override { return BaseClass::IsValid(key); }
private:
size_t size_;
std::list<TKey> keys_;
};
}// namespace sled
#endif// SLED_SLED_CACHE_POLICY_FIFO_CACHE_POLICY_H

View File

@ -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 <list>
#include <map>
namespace sled {
template<typename TKey>
class LRUCachePolicy : AbstractCachePolicy<TKey> {
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<TKey> &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<TKey> keys_;
std::map<TKey, typename std::list<TKey>::iterator> key_index_;
};
}// namespace sled
#endif// SLED_SLED_CACHE_POLICY_LRU_CACHE_POLICY_H

View File

@ -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

View File

@ -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"

View File

@ -39,7 +39,7 @@ public:
static constexpr Timestamp Nanos(T value)
{
static_assert(std::is_arithmetic<T>::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
{