2022-12-07 LRU缓存实现

摘要:

LRU缓存实现

lru_cache.h

/**
@file lru_cache.h
@brief LRU缓存
*/

#pragma once

#include <list>
#include <map>
#include <utility>
#include <algorithm>    
#include <functional>   
#include <iostream>
#include <sstream>

#ifdef _HASH_MAP_
#include <unordered_map>
#endif

#include "assert.h"


struct LRU_RT_Info
{
	int limit			= 0;
	int clean_size		= 0;

	int cache			= 0;
	int hit				= 0;
	int miss			= 0;
	size_t cell			= 0;

	void Dump()
	{
		std::cout << "lru info : "
			<< ", limit = " << limit
			<< ", clean_size = " <<clean_size
			<< ", cache = " << cache
			<< ", hit = " << hit
			<< ", miss = " << miss
			<< ", cell = " << cell
		<< std::endl;
	}
};

template<typename key_type, typename value_type>
class CLRUCache
{
private:

	struct LRUData : public value_type
	{ 
		bool _del = false;
	};

private:

	// 数据序列
	using ValueContainer = std::list< std::pair<key_type, LRUData> >;
	using ValueContainer_iterator = typename ValueContainer::iterator;
	using ConstValueContainerIter = typename ValueContainer::const_iterator;

	// 数据索引

	template <typename I_K, typename I_V>
	using IndexType =
#ifdef _HASH_MAP_
		typename std::unordered_map <I_K, I_V>;
#else
		typename std::map<I_K, I_V>;
#endif

	using IndexContainer = IndexType<key_type, ValueContainer_iterator>;
	using IndexContainer_iterator = typename IndexContainer::iterator;
    
public:

	/**
	@brief 构造函数
	@param limit 缓存容量
	@param clean_size 到达容量后,一次清理的个数
	@param expire_time 过期时间,超过该时间视为过期,访问时删除. -1: 永不过期
	@return 
	*/
	CLRUCache(int limit = 10000, int clean_size = 100, time_t expire_time = -1)
		: m_limit ( limit )
		, m_clean_size ( clean_size )
	{

		assert( m_limit > 0 );
		assert( m_clean_size > 0 );

		assert( m_limit >= clean_size );

#ifdef _HASH_MAP_
		m_indexs.reserve(m_limit);
#endif
	}
	
	/**
	@brief 拿第一个元素
	@praram removed 用于保存被删除的值
	@return
	*/
	value_type* Front();

	/**
	@brief 添加缓存项到序列首部项
	@praram removed 用于保存被删除的值
	@return 
	*/
	void Add( key_type key, const value_type& value, value_type* removed = nullptr);
	
	/**
	@brief 批量添加缓存
	@praram
	@return
	*/
	void AddVec(const std::vector<std::pair<key_type, value_type>>& kv_vec);

	/**
	@brief 删除一个缓存项
	@return 
	*/
	void Remove(key_type key) { Remove(key, nullptr); };
	
	/**
	@brief 删除一个缓存项
	@param valueptr 保存被删除的值
	@return 
	*/
	void Remove(key_type key, value_type* valueptr);
	
	/**
	@brief 指定位置删除一个缓存项
	@param pos 删除缓存的位置
	@param valueptr 保存被删除的值
	@return
	*/
	bool RemovePosValue(ValueContainer_iterator& pos, value_type* valueptr = nullptr );

	bool RemovePosIndex(IndexContainer_iterator& pos, value_type* valueptr = nullptr);

	/**
	@brief 从序列首部删除一个缓存项
	@param valueptr 保存被删除的值
	@return 
	*/
	bool RemoveFirst(value_type* valueptr = nullptr) { return RemovePosValue(m_values.begin(), valueptr); };

	/**
	@brief 从序列尾部删除一个缓存项
	@param valueptr 保存被删除的值
	@return
	*/
	bool RemoveLast( value_type* valueptr = nullptr ) { return RemovePosValue(--m_values.end(), valueptr); };
	
	/**
	@brief 清空缓存,重置缓存统计数据
	@return 
	*/
	void Clear();

	/**
	@brief 清理空间,先按LRU清理,再清理过期
	@return
	*/
	void CleanCapacity();

	/**
	@brief 按LRU规则清理空间
	@return
	*/
	void CleanByLRU();

	/**
	@brief 清理应被删除的元素,包含标记删除和过期元素
	@return
	*/
	void CleanNeedDel();
	
	/**
	@brief 查找一个缓存项,如果存在,该缓存项会被放到序列首部
	@return 
	*/
	value_type* Find( key_type key );
	
	/**
	@brief 查找一个缓存项
	@return 
	*/
	value_type* Peek( key_type key );
	
	/**
	@brief 查看缓存项数量
	@return 
	*/
	int Size() const { return m_cache_count; }
	
	/**
	@brief 查看命中次数
	@return 
	*/
	int Hit() const { return m_hit_count; }
	
	/**
	@brief 查看未命中次数
	@return 
	*/
	int Miss() const { return m_miss_count; }
	
	/**
	@brief 查看缓存容量
	@return 
	*/
    int GetLimit() const{ return m_limit; }

	/**
	@brief 查看清理容量的数量设定
	@return
	*/
	int GetCleanSize() const { return m_clean_size; }

	/**
	@brief 设置缓存容量, 只允许增大
	@return
	*/
	void SetLimit(int limit) { m_limit = (limit <= m_limit) ? m_limit : limit; };
	
	/**
	@brief 遍历函数模板
	@return 
	*/
    template <typename FunctorT>
    void ForEach(FunctorT _Func);

    template <typename FunctorT>
    void ForEach(FunctorT _Func) const;
	
	/**
	@brief 输出缓存项内容到标准输出,要求key和value支持ostream <<运算
	@return 
	*/
	void Dump() const;

	/**
	@brief 获取缓存索引
	@return 
	*/
	const IndexContainer& GetIndex() { return m_indexs; };

	/**
	@brief 获取运行时数据
	@return
	*/
	void GetRTInfo(LRU_RT_Info& lru_rt_info);

	/**
	@brief 标记删除,真正删除时机在清理容量时
	@return
	*/
	void MarkDelete(key_type key);

private:
	
	/**
	@brief Forward
	@return
	*/
	bool Forward(ConstValueContainerIter& itr, int step) const
	{
		while((step > 0) && (itr != m_values.end()))
		{
			++itr;
			--step;
		}

		return itr != m_values.end();
	}

private: // 拷贝构造函数和赋值构造函数不允许
	CLRUCache(const CLRUCache&) = delete;
	CLRUCache& operator = (const CLRUCache&) = delete;

private:

	ValueContainer m_values;	///< 缓存序列,保存了缓存项的值
	IndexContainer m_indexs;	///< 缓存索引,保存了缓存序列的迭代器

	int m_limit = 0;
	int m_clean_size = 0;
	int m_cache_count = 0;
	int m_hit_count = 0;
	int m_miss_count = 0;

	static constexpr size_t m_cell = sizeof(LRUData);
};

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::Dump() const
{	
	std::stringstream ss;

	ss << "limit = " << m_limit << ", clean_size = " 
		<< m_clean_size;
	ss << ", cache_count = " << m_cache_count 
		<< ", hit_count = " << m_hit_count << ", miss_count = " << m_miss_count;

	std::cout << ss.str() << std::endl;
}

template<typename key_type, typename value_type>
bool CLRUCache<key_type, value_type>::RemovePosValue(ValueContainer_iterator& pos, value_type* valueptr)
{
	if (pos == m_values.end())
	{
		return false;
	}

	if (valueptr)
	{
		*valueptr = pos->second;
	}

	m_indexs.erase(pos->first);
	m_values.erase(pos);

	--m_cache_count;

	return true;
}

template<typename key_type, typename value_type>
bool CLRUCache<key_type, value_type>::RemovePosIndex(IndexContainer_iterator& pos, value_type* valueptr)
{
	if (pos == m_indexs.end())
	{
		return false;
	}

	ValueContainer_iterator v_pos = pos->second;

	if (valueptr)
	{
		*valueptr = v_pos->second;
	}

	m_indexs.erase(pos);
	m_values.erase(v_pos);

	--m_cache_count;

	return true;
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::Clear()
{
	m_indexs.clear();
	m_values.clear();

	m_cache_count = 0;
	m_hit_count = 0;
	m_miss_count = 0;
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::CleanCapacity()
{
	CleanNeedDel();
	CleanByLRU();
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::CleanByLRU()
{
	if (m_cache_count < m_clean_size)
	{
		return;
	}

	int num = m_clean_size;
	while (num--)
	{
		RemoveLast();
	}
}

template<typename key_type, typename value_type>
value_type* CLRUCache<key_type, value_type>::Find( key_type key )
{
	IndexContainer_iterator itr = m_indexs.find( key );

	if (itr == m_indexs.end())
	{
		++m_miss_count;
		return nullptr;
	}

	m_values.splice(m_values.begin(), m_values, itr->second);

	LRUData& data = itr->second->second;
	value_type* result = (value_type*)(&data);
	return result;
}

template<typename key_type, typename value_type>
value_type* CLRUCache<key_type, value_type>::Peek( key_type key )
{
	IndexContainer_iterator itr = m_indexs.find( key );

	if (itr == m_indexs.end())
	{
		return nullptr;
	}

	LRUData& data = itr->second->second;
	value_type* result = (value_type*)(&data);

	return result;
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::Add( key_type key, const value_type& value, value_type* removed )
{
	IndexContainer_iterator itr = m_indexs.find( key );

	if ( itr == m_indexs.end() )
	{
		if ( m_cache_count >= m_limit ) // 需要返回则删末尾并返回, 否则按配置清理空间
		{
			removed ? (void) RemoveLast(removed) : CleanCapacity();
		}

		LRUData data;
		data._del = false;

		value_type* data_base = &data;
		*data_base = value;

		m_values.emplace_front( std::make_pair( key, data ) );
		m_indexs.emplace(key, m_values.begin());

		++m_cache_count;
	}
	else
	{
		LRUData& data = itr->second->second;

		value_type* data_base = &data;
		*data_base = value;

		data._del = false;
	}
}

template <typename key_type, typename value_type> template <typename FunctorT>
void CLRUCache<key_type, value_type>::ForEach(FunctorT _Func)
{
	std::for_each(m_values.begin(), m_values.end(), _Func);
}

template <typename key_type, typename value_type> template <typename FunctorT>
void CLRUCache<key_type, value_type>::ForEach(FunctorT _Func) const
{
	std::for_each(m_values.begin(), m_values.end(), _Func);
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::Remove(key_type key, value_type* valueptr)
{ 
	IndexContainer_iterator iter = m_indexs.find(key);
	if (iter == m_indexs.end())
	{
		return;
	}

	RemovePosIndex(iter, valueptr); 
};

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::GetRTInfo(LRU_RT_Info& lru_rt_info)
{
	lru_rt_info.limit			= m_limit;
	lru_rt_info.clean_size		= m_clean_size;

	lru_rt_info.cache			= m_cache_count;
	lru_rt_info.hit				= m_hit_count;
	lru_rt_info.miss			= m_miss_count;
	lru_rt_info.cell			= m_cell;
}

template<typename key_type, typename value_type>
value_type* CLRUCache<key_type, value_type>::Front()
{
	if (m_cache_count <= 0)
	{
		return nullptr;
	}

	std::pair<key_type, LRUData>& value = m_values.front();

	LRUData& data = value.second;
	return (value_type*)(&data);
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::AddVec(const std::vector<std::pair<key_type, value_type>>& kv_vec)
{
	if (kv_vec.empty())
	{
		return;
	}

	for_each(kv_vec.begin(), kv_vec.end(), [this](const std::pair<key_type, value_type>& kv) {
		this->Add(kv.first, kv.second);
	});
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::MarkDelete(key_type key)
{
	IndexContainer_iterator itr = m_indexs.find(key);
	if (itr == m_indexs.end())
	{
		return;
	}

	itr->second->second._del = true;
}

template<typename key_type, typename value_type>
void CLRUCache<key_type, value_type>::CleanNeedDel()
{
	std::vector<IndexContainer_iterator> del_elements;

	for (IndexContainer_iterator itr = m_indexs.begin(); itr != m_indexs.end(); ++itr)
	{
		if (itr->second->second._del) // 如果标记删除,则无论过期都应删除
		{
			del_elements.emplace_back(itr);
			continue;
		}
	}

	if (del_elements.empty())
	{
		return;
	}

	for (auto& itr : del_elements)
	{
		RemovePosIndex(itr);
	}
}

辅助相关:
 

base.h

#pragma once

#include <stdint.h>

typedef long long int64;
typedef unsigned long long uint64;
typedef unsigned int size32_t;

template<typename T, int tid>
struct TGenericID
{
	T id;

	TGenericID():id(0)
	{
	}

	TGenericID(const T& i):id(i)
	{
	}

	TGenericID& operator = (const T& val)
	{
		id = val;
		return *this;
	}

	bool operator < (const TGenericID& b)const
	{
		return id < b.id;
	}

	bool operator > (const TGenericID& b)const
	{
		return id > b.id;
	}

	TGenericID operator ++()
	{
		++id;
		return *this;
	}

	bool operator != (const TGenericID& val)const
	{
		return id!=val.id;
	}
	bool operator == (const TGenericID& val)const
	{
		return id==val.id;
	}
	bool operator <= (const TGenericID& val)const
	{
		return id<=val.id;
	}
	bool operator >= (const TGenericID& val)const
	{
		return id>=val.id;
	}
};

typedef TGenericID<int64, 2> TPersistID;

main.cpp

#include <iostream>

#include "lru_cache.h"
#include "base.h"


class Data
{
public:
	Data() {}

	Data(TPersistID _pstid, int _age)
		: pstid(_pstid)
		, age(_age)
	{
	}

	TPersistID pstid = 0;
	int age = 0;
};

static void CacheInitData(CLRUCache<TPersistID, Data>& cache, int size)
{
	cache.Clear();
	for (int i = 0; i < size; ++i)
	{
		Data data{ i + 10001, i + 101 };
		cache.Add(data.pstid, data);
	}
}

int main(int argc, const char** argv) {


    CLRUCache<TPersistID, Data> cache;

	constexpr int size = 5;
	CacheInitData(cache, size);
	cache.Dump();


    LRU_RT_Info info;
    cache.GetRTInfo(info);

    // info.Dump();


    return 0;
}

makefile

TARGET	=	cache
S_SRCS 	=	$(wildcard src/*.cpp)
OBJS	=	$(patsubst src/%.cpp, src/%.o, $(S_SRCS))

CFLAGS	+=	-std=c++11 -Isrc -L../innobase/buf -lbuf -I../innobase/include
CFLAGS 	+= 	-D__LINUX__
CFLAGS	+=  -ggdb3 -O0 -w -fpermissive
USEDE	=	-DDEBUG

CC		=	g++

$(TARGET):$(OBJS)
	$(CC) $(CFLAGS) $(USEDE) $(OBJS) -o $@

%.o: %.cpp
	$(CC) -c $^ $(CFLAGS) $(USEDE)  -o $@


.PHONY : clean

clean:
	rm $(TARGET) -f
	rm $(OBJS) -f

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟世者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值