c++实现LFU算法

c++实现LFU算法LFU(LeastFrequentlyUsed),是一种缓存算法,它对每一个数据块都有一个访问次数的记录,也就是一个数据块的访问次数越大,在将来越可能访问。1.每个节点(数据块)都有一个访问次数,新插入的节点访问次数为12.相同访问次数的依据时间排序,后插入的在前面3.当需要淘汰数据时,会从尾部,也就是访问次数从小到大,开始淘汰这里是使用c++实现的LFU缓存:主要利用了一个mu…

LFU (Least Frequently Used),是一种缓存算法,它对每一个数据块都有一个访问次数的记录,也就是一个数据块的访问次数越大,在将来越可能访问。

1.每个节点(数据块)都有一个访问次数,新插入的节点访问次数为1
2.相同访问次数的依据时间排序,后插入的在前面
3.当需要淘汰数据时,会从尾部,也就是访问次数从小到大,开始淘汰

这里是使用c++实现的LFU缓存:主要利用了一个multiset,unorder_map分别来根据访问次数排序和保存数据。

这里是需要使用的节点结构

#include <string>
#include <unordered_map>
#include <set>
using namespace std;

template<class KEY>
struct CacheKey
{
	KEY		key;
	int		count;
	CacheKey(KEY k) :key(k), count(1) {}
	void change(int x) {
		count += x;
	}
    int getcount() {
		return count;
	}
};

template<class KEY,class VAL>
struct CacheNode
{
	CacheKey<KEY>* cacheKey;
	VAL val;
	CacheNode(CacheKey<KEY>* ck, VAL v) :cacheKey(ck), val(v) {}
	CacheNode(KEY k, VAL v):val(v) {
		cacheKey = new CacheKey<KEY>(k);
	}
};

template<class KEY>
struct CMP     //仿函数,提供CacheKey的比较方式
{
	bool operator()(CacheKey<KEY> const* x, CacheKey<KEY> const* y) {
		return x->count < y->count;
	}
};

c++ LFU

/*
LFU (least Frequently Used)
根据数据的历史访问次数来决定淘汰数据,也就是当前时刻访问次数大的数据,
在下一时刻也可能被访问到。
1.每个节点(数据块)都有一个访问次数
2.相同访问次数的依据时间排序,后插入的在前面
3.当需要淘汰数据时,会从尾部,也就是访问次数从小到大,开始淘汰
*/
template<class KEY, class VAL >
class Lfu
{
public:
	Lfu(size_t size) :cacheSize(size){

	}
	~Lfu() {
		auto ite = cacheMap.begin();
		while (ite != cacheMap.end()) {
			delete ite->second->cacheKey;
			delete ite->second;
			++ite;
		}
		cacheMap.clear();
		cacheSet.clear();
	}
public:
	void		put(KEY key, VAL val) {
		this->check_cache_size();
		auto node = new CacheNode<KEY, VAL>(key, val);
		cacheMap[key] = node;
		cacheSet.insert(node->cacheKey);
	}

	bool		get(KEY key, VAL* val) {
		if (cacheMap.count(key)) {
			auto node = this->removeFromSet(key);
			if(val)
				*val = node->val;
			node->cacheKey->change(1);
			cacheSet.insert(node->cacheKey);
			return true;
		}
		return false;
	}

	VAL			get(KEY key) {
		VAL v;
		this->get(key, &v);
		return v;
	}

	void        remove(KEY key) {
        if(cacheMap.count(key)){
            auto node = cacheMap[key];
            this->removeFromSet(node->key);
            delete node->key;
            delete node;
            cacheMap.erase(key);
        }
	}

	bool		exist(KEY key) {
		if (cacheMap.count(key))
			return true;
		return false;
	}

	void		print_test() {
		auto ite = cacheSet.begin();
		while (ite != cacheSet.end()) {
			cout << "count: " << (*ite)->count << " key: " << (*ite)->key << endl;
			++ite;
		}
		cout << "======================================" << endl;
	}
private:
	//将节点从set中删除,返回删除的node
	void     removeFromSet(CacheKey<KEY>* key) {	
		auto ite = cacheSet.find(key);
        while(ite != cacheSet.end()){
            if(key == cacheMap[(*ite)->key]->key){
                cacheSet.erase(ite);
                break;
            }
            auto itex = ite;
            ++ite;
            if(ite != cacheSet.end() && (*ite)->getcount() != (*itex)->getcount()){
                break;
            }
        }
	}
	//检查缓存数量是否超出了大小限制
	void	check_cache_size() {	
		while (cacheMap.size() >= cacheSize) {
			auto ite = cacheSet.begin();
			cacheMap.erase((*ite)->key);
			cacheSet.erase(ite);
		}
	}
private:
	multiset<CacheKey<KEY>*,CMP<KEY>>            cacheSet;	//利用红黑树根据访问次数排序
	unordered_map<KEY, CacheNode<KEY, VAL>*>     cacheMap;	//缓存MAP
	size_t                                       cacheSize;	//缓存空间大小
};

LFU-Aging LFU的改进版

由于LFU的淘汰策略是淘汰访问次数最小的数据块,但是新插入的数据块的访问次数为1,这样就会产生缓存污染,使得新数据块被淘汰。所以在LFU算法之上,引入访问次数平均值概念,当平均值大于最大平均值限制时,将所有节点的访问次数减去最大平均值限制的一半或一个固定值。

c++ LFU-Aging

/*
LFU-Aging 对于LFU的改进版
LFU对于新数据不友好,因为新插入的数据访问次数count=1,放在尾部
如果需要淘汰就会将新数据淘汰。
所以LFU-Aging加入了平均访问次数的概念
如果节点的平均访问次数大于某个固定值x时,则将所有节点的count值减去x/2
这样可解决“缓存污染”
*/
template<class KEY, class VAL >
class LfuAging
{
public:
	LfuAging(size_t size,int maxAverage = 10) 
		:cacheSize(size), countMaxAverage(maxAverage), countCurAverage(1), countAll(0){

	}
	~LfuAging() {
		auto ite = cacheMap.begin();
		while (ite != cacheMap.end()) {
			delete ite->second->cacheKey;
			delete ite->second;
			++ite;
		}
		cacheMap.clear();
		cacheSet.clear();
	}
public:
	void		put(KEY key, VAL val) {
		this->check_cache_size();
		auto node = new CacheNode<KEY, VAL>(key, val);
		cacheMap[key] = node;
		cacheSet.insert(node->cacheKey);
        this->average(1);	
	}

	bool		get(KEY key, VAL* val) {
		if (cacheMap.count(key)) {
			auto node = this->removeFromSet(key);
			if (val)
				* val = node->val;
			node->cacheKey->change(1);
			cacheSet.insert(node->cacheKey);
			this->average(1);
			return true;
		}
		return false;
	}

	VAL			get(KEY key) {
		VAL v;
		this->get(key, &v);
		return v;
	}

	void        remove(KEY key) {
		auto node = this->removeFromSet(key);
		if (node) {
			cacheMap.erase(key);
			this->average(-node->cacheKey->getcount());
			delete node->cacheKey;
			delete node;
		}
	}

	bool		exist(KEY key) {
		if (cacheMap.count(key))
			return true;
		return false;
	}

	void		print_test() {
		auto ite = cacheSet.begin();
		while (ite != cacheSet.end()) {
			cout << "count: " << (*ite)->count << " key: " << (*ite)->key << endl;
			++ite;
		}
		cout << "======================================" << endl;
	}
private:
	CacheNode<KEY, VAL>* removeFromSet(KEY key) {
		if (cacheMap.count(key)) {
			auto node = cacheMap[key];
			auto ite = cacheSet.begin();
			while (ite != cacheSet.end()) {
				if ((*ite) == node->cacheKey) {
					cacheSet.erase(ite);
					break;
				}
				++ite;
			}
			return node;
		}
		return nullptr;
	}

	void	check_cache_size() {
		while (cacheMap.size() >= cacheSize) {
			auto ite = cacheSet.begin();
			cacheMap.erase((*ite)->key);
			cacheSet.erase(ite);
		}
	}

	void		average(int change) {
		countAll += change;
		int av = countAll / cacheMap.size();
		if (av >= countMaxAverage) {
			auto ite = cacheSet.begin();
			while (ite != cacheSet.end()) {
				(*ite)->change(-(countMaxAverage / 2));
				++ite;
			}
			countAll = countAll - ((countMaxAverage / 2) * cacheMap.size());
		}
	}
private:
	multiset<CacheKey<KEY>*, CMP<KEY>>            cacheSet;
	unordered_map<KEY, CacheNode<KEY, VAL>*>      cacheMap;
	size_t                                        cacheSize;
	int                                           countMaxAverage;
	int                                           countCurAverage;
	int                                           countAll;
};

LFU与LRU对比

LFU算法的命中率会更高一些,且能解决一些周期性的访问带来命中率下降的情况。

 

今天的文章c++实现LFU算法分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/9366.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注