缓冲技术之五:缓冲池的LRU管理策略C++实现

本文介绍了一种改进的LRU缓存算法实现,通过结合双向链表与map数据结构,实现了高效的数据项管理和更新策略。该算法在有限容量下能够提供接近O(logN)的查找效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
LRU事实上属于一类被称为内存置换算法(FIFO、LRU、LFU):都是指在有限的容量下,设计一个如何管理和更新数据项的策略
本程序是实现LRU-1算法,但是和常见的LRU算法不同,一般常规的LRU算法是直接用一个LRU双向链表队列实现管理,这种双向链
表会导致查找元素的时间复杂度位O(n),故而我的程序是在LRU双向链表的基础上附加一个map关联容器用以加速索引过程,将
查找元素的操作的时间复杂度降低为O(logN)
*/
#include <iostream>
#include <map> //map并非hash-table
#include <algorithm>
#include <assert.h>
#include <windows.h>
//这类STL文件均没有.h后缀
using namespace std;

#define BUFFER_BLOCK_SIZE 512
typedef struct __buffer_head
{
    __buffer_head* prev; //指向前一个缓冲块
    __buffer_head* next; //指向后一个缓冲块的__buffer_head
    int   block_number;//用来标示该buffer缓冲块的全局索引号
    char* buffer_content;//指向数据缓存区的指针
}buffer_head;

class LRUBufferList
{
private:
    int LRUBufferList_Size_MAX; //LRU队列上限
    int LRUBufferList_Size; //LRU队列当前存储量
    buffer_head* LRUBufferList_First; //LRU链首
    buffer_head* LRUBufferList_End; //LRU链尾
    map<int, buffer_head*> quickIndex; //除了LRU管理队列外,还要配备一个额外的快速索引结构,这里采用STL提供的map


public:
    LRUBufferList(int capacity)
    {
        LRUBufferList_Size_MAX = capacity;
        LRUBufferList_First = LRUBufferList_End = NULL;
        LRUBufferList_Size = 0;
    }

    ~LRUBufferList()
    {
        map<int, buffer_head*>::iterator iter;
        //清理所有的缓存块空间,释放内存
        for (iter = quickIndex.begin(); iter != quickIndex.end(); iter++ )
        {
            if (iter->second->buffer_content) //如果该缓冲块被分配了空间,则删除该空间
                delete iter->second->buffer_content;

            delete iter->second; //删除该缓冲块的buffer_head头部结构
        }
        quickIndex.erase(quickIndex.begin(), quickIndex.end()); //擦除map快速索引结构
        LRUBufferList_First = LRUBufferList_End = NULL; //置LRU队列的管理指针为空
    }

    //给定Block的缓冲块号,返回指定缓冲块头部指针
    buffer_head* get(int param_number)
    {
        //先在map快速索引结构中定位该block块
        map<int, buffer_head*>::iterator iter = quickIndex.find( param_number ); 

        if ( iter == quickIndex.end() )
            return NULL;
        else
        {
            //在map中找到了该缓冲块的头部指针,则意味着该缓冲块应该在LRU被提到链首
            moveToHead(iter->second);
            return iter->second;
        }
    }

    //将指定的block缓冲块号中的内容替换成content指定的内容,如果LRU队列中不存在该block,则新建该block并将其加入到LRU队列中
    buffer_head* rewrite(int param_number, char* content)
    {
        //如果该批次要修改的内容的有效数据内容超过511,即不能用单个缓冲块装载完,则说明是前面调度出现了问题,当然这种调度
        //应该是根据具体要缓冲的内容大小而分配适当块数的缓冲块,但至于如何切割内容,显然不是由LRU队列实现的。
        if ( strlen(content) > BUFFER_BLOCK_SIZE-1  ) 
            exit(-1);

        map<int, buffer_head*>::iterator iter = quickIndex.find( param_number );

        if (iter == quickIndex.end() )
        {
            buffer_head* newBuffer = getFreeBuffer(param_number);
            strcpy(newBuffer->buffer_content, content);

            //新缓冲块创建成功,下面可以执行将该新Block加入到LRU队列中
            addToHead(newBuffer);
            return newBuffer;
        }

        else //意味着LRU缓冲队列中原先便有了该缓冲块,故而需要调换该缓冲块的原先内容
        {
            strcpy(iter->second->buffer_content, content);
            moveToHead(iter->second);
            return iter->second;
        }
    }

    buffer_head* getFreeBuffer(int param_number)
    {
        if (LRUBufferList_Size < LRUBufferList_Size_MAX) //在内存中创建一个新的缓冲块
        {
            buffer_head newBuffer;
            newBuffer.prev = newBuffer.next = NULL;
            newBuffer.block_number = param_number;
            newBuffer.buffer_content = new char[BUFFER_BLOCK_SIZE]; //给新创建的block缓冲块分配512固定字节空间
            //在快速索引结构中也添加索引
            quickIndex[param_number] = &newBuffer;

            return &newBuffer;
        }
        else if (LRUBufferList_Size == LRUBufferList_Size_MAX)
        {
            //从LRU末尾淘汰一个对象,将其空间腾空给新的数据使用
            LRUBufferList_Size--; //这里先给LRU队列中有效数据数量减1,在addToHead()操作中将加回来,逻辑更清楚
            buffer_head* temp = LRUBufferList_End;
            LRUBufferList_End->prev->next = NULL;
            LRUBufferList_End = LRUBufferList_End->prev;

            temp->prev = temp->next = NULL;
            //将淘汰掉的LRU末尾缓冲块快速索引信息从map中删除掉
            quickIndex.erase( temp->block_number );
            temp->block_number = param_number;
            //在快速索引结构中修改索引
            quickIndex[param_number] = temp;
            return temp;
        }
    }

    void addToHead( buffer_head* target )
    {

        if (LRUBufferList_Size == 0)
        {
            LRUBufferList_First = LRUBufferList_End = target;
        }
        else
        {
            target->next = LRUBufferList_First;
            LRUBufferList_First->prev = target;
            LRUBufferList_First = target;
        }

        LRUBufferList_Size++;
    }

    void moveToHead( buffer_head* target )
    {   
        //如果LRU队列中只有当前target一个对象,则显然不需要再调整
        if (LRUBufferList_Size == 1)
            return;
        //如果当前对象本身就是LRU队列的链首,则也无需调整
        if (target == LRUBufferList_First)
            return;
        //调整target的邻居缓冲块的信息
        if (target->prev)
            target->prev->next = target->next;
        if (target->next) //如果目标缓冲块的后续还有缓冲块
            target->next->prev = target->prev;
        else //如果目标缓冲块的后续没有缓冲块,则意味着target本身就是链尾,这时移动target会牵涉到LRU队列链尾信息的变动
        {
            if (target->prev)
                LRUBufferList_End = target->prev;
            else //如果target前向也没有缓冲块,则意味着LRU队列只有target一个
            {
                assert( LRUBufferList_Size == 1);
                LRUBufferList_End = target;
            }
        }

        //调整目标缓冲块头部的前后缓冲块邻居信息
        target->prev = NULL;
        target->next = LRUBufferList_First;
        LRUBufferList_First->prev = target;
        //调整LRU队列链首信息
        LRUBufferList_First = target;
    }

    char* strcpy(char *dest, const char* src)
    {
        char* ret = dest;
        while(*src)
            *dest++ = *src++;
        *dest = '\0';//手动在字符串末尾加入\0结束符
        return ret; 
    }

    unsigned strlen(const char* str)
    {
        int cnt = 0;
        if (!str)
            return 0;
        for (; *str != '\0'; ++str)
            ++cnt;
        return cnt;
    }

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值