您的位置:首页 > 编程语言 > C语言/C++

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

2017-11-28 17:22 891 查看
/*
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;
}

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  缓冲池 LRU策略