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

来源:互联网 发布:知乎live500场 百度云 编辑:程序博客网 时间:2024/06/06 10:03
/*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 512typedef 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提供的mappublic:    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;    }};