LRU是最近最少使用算法。一般内存管理的时候采用LRU算法可以提高性能。
将cache缓存块位置用LRU双向链表链接起来,将新加入的块直接放到链表的头,当一个块被命中后,把该块调整到链表的头,这样经过多次操作之后,最近被命中过的块就会向链表头部移动,而没有被命中的内容会向链表尾部移动,需要替换时,就直接从链表尾部替换即可。新的内容直接插入链表头部,这样就实现了LRU的思想。
算法中维护了一个freelist,用于管理空闲块的位置,每次需要插入新的内容时,就从freelist里面获取一个空闲块,将新内容复制,然后插入到链表头部;删除时就从链表尾部删除结点,然后更新到freelist里;查找一个结点时,在一般链表中的时间复杂度是O(n),为了提高查找效率,在算法中加入了一个hash表,每次新加入的结点都会更新到hash表里,删除结点时会从hash表中移除,这样查找的时候直接从hash表里查询,获取结点的位置,从而时间复杂度为O(1)。
C语言实现的代码如下(已经测试通过了):
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define LRU_SIZE 20
#define HASH_BASE 20
#define LRU_SUCCESS 0
#define LRU_ERROR -1
#define LRU_MISS 0
#define LRU_HIT 1
#define LRU_HASH_KEY(_key,_base) (int)(((int)_key)%(_base))
typedef struct _LRU_NODE{
int id;
int key;
int free;
int prev;
int next;
}LRU_NODE,*PLRU_NODE;
typedef struct _LRU_HASH{
unsigned long basesize;
int base[HASH_BASE];
}LRU_HASH,*PLRU_HASH;
typedef struct _LRU_LIST{
int capacity;
int usage;
LRU_NODE bucket[LRU_SIZE];
int freelist;
int head;
int tail;
LRU_HASH hash;
}LRU_LIST,*PLRU_LIST;
int lru_init(PLRU_LIST list, int capacity, int hashbase)
{
int i;
PLRU_NODE node;
list->capacity = capacity;
list->usage = 0;
list->head = -1;
list->tail = -1;
list->freelist = -1;
for(i = capacity - 1; i >= 0; i--)
{
list->bucket[i].id = i;
list->bucket[i].key = -1;
list->bucket[i].free = 1;
list->bucket[i].prev = -1;
list->bucket[i].next = -1;
if(list->freelist == -1)
{
list->freelist = list->bucket[i].id;
}
else
{
node = &(list->bucket[list->freelist]);
node->prev = list->bucket[i].id;
list->bucket[i].next = node->id;
list->freelist = list->bucket[i].id;
}
}
for(i = 0; i < hashbase; i++)
{
list->hash.base[i] = -1;
}
list->hash.basesize = hashbase;
return LRU_SUCCESS;
}
int lru_sethead(PLRU_LIST list, PLRU_NODE node)
{
PLRU_NODE tempnode;
if(node->free)
return LRU_SUCCESS;
if(node->id == list->head)
return LRU_SUCCESS;
if(list->head == -1)
{
//应该不会到这里面来
list->head = node->id;
list->tail = node->id;
node->prev = -1;
node->next = -1;
}
else
{
if(list->tail == node->id)
{
list->tail = node->prev;
tempnode = &(list->bucket[list->tail]);
tempnode->next = -1;
}
else
{
tempnode = &(list->bucket[node->prev]);
tempnode->next = node->next;
tempnode = &(list->bucket[node->next]);
tempnode->prev = node->prev;
}
tempnode = &(list->bucket[list->head]);
tempnode->prev = node->id;
node->next = list->head;
node->prev = -1;
list->head = node->id;
}
return LRU_SUCCESS;
}
int lru_getfree(PLRU_LIST list, PLRU_NODE *freenode)
{
PLRU_NODE node = NULL;
if(list->freelist != -1)
{
node = &(list->bucket[list->freelist]);
list->freelist = node->next;
if(list->freelist != -1)
{
list->bucket[list->freelist].prev = -1;
}
node->free = 1;
*freenode = node;
return LRU_SUCCESS;
}
else
{
*freenode = NULL;
return LRU_ERROR;
}
}
/*lru_delete:delete tail lru node
list:lru list
*/
int lru_delete(PLRU_LIST list)
{
PLRU_NODE tempnode;
int hashkey;
if(list->tail == -1)
return LRU_ERROR;
tempnode = &(list->bucket[list->tail]);
list->tail = tempnode->prev;
if(list->tail != -1)
list->bucket[list->tail].next = -1;
else
list->head = -1;
//删除hash结点
hashkey = LRU_HASH_KEY(tempnode->key, list->hash.basesize);
list->hash.base[hashkey] = -1;
if(list->freelist != -1)
{
tempnode->next = list->freelist;
list->bucket[list->freelist].prev = tempnode->id;
list->freelist = tempnode->id;
tempnode->prev = -1;
tempnode->free = 1;
tempnode->key = -1;
}
else
{
list->freelist = tempnode->id;
tempnode->key = -1;
tempnode->free = 1;
tempnode->prev = -1;
tempnode->next = -1;
}
list->usage--;
return LRU_SUCCESS;
}
int lru_insert(PLRU_LIST list, PLRU_NODE node)
{
//对于list满的情况和插入有相同key的node
//在函数调用之前作判断
PLRU_NODE tempnode;
int key;
int hashkey;
//插入lru hash表
key = node->key;
hashkey = LRU_HASH_KEY(key, list->hash.basesize);
list->hash.base[hashkey] = node->id;
if(list->head == -1)
{
list->head = node->id;
list->tail = node->id;
node->prev = -1;
node->next = -1;
}
else
{
tempnode = &(list->bucket[list->head]);
node->next = list->head;
tempnode->prev = node->id;
list->head = node->id;
node->prev = -1;
}
node->free = 0;
list->usage++;
return LRU_SUCCESS;
}
int lru_inquiry(PLRU_LIST list, int key)
{
PLRU_NODE tempnode;
int hashkey;
if(list->head == -1)
return LRU_MISS;
hashkey = LRU_HASH_KEY(key, list->hash.basesize);
if(list->hash.base[hashkey] == -1)
return LRU_MISS;
else
{
tempnode = &(list->bucket[list->hash.base[hashkey]]);
}
//没加lru hash表之前的查询方式
/*tempnode = &(list->bucket[list->head]);
while(tempnode && tempnode->key != key)
{
if(tempnode->next == -1)
tempnode = NULL;
else
tempnode = &(list->bucket[tempnode->next]);
}
if(!tempnode)
return LRU_MISS;*/
lru_sethead(list,tempnode);
return LRU_HIT;
}
int lru_show(PLRU_LIST list)
{
PLRU_NODE node;
if(list->head == -1)
{
printf("list is empty!\n");
return LRU_ERROR;
}
node = &(list->bucket[list->head]);
while(node)
{
printf("id:%d,key:%d\n", node->id, node->key);
if(node->next == -1)
node = NULL;
else
node = &(list->bucket[node->next]);
}
return LRU_SUCCESS;
}
int lru_clear(PLRU_LIST list)
{
PLRU_NODE tempnode,node;
unsigned long j;
if(list->head == -1)
return LRU_SUCCESS;
tempnode = &(list->bucket[list->head]);
while(tempnode)
{
if(tempnode->next == -1)
node = NULL;
else
node = &(list->bucket[tempnode->next]);
if(list->freelist == -1)
{
list->freelist = tempnode->id;
tempnode->free = 1;
tempnode->key = -1;
tempnode->next = -1;
tempnode->prev = -1;
}
else
{
tempnode->next = list->freelist;
list->bucket[list->freelist].prev = tempnode->id;
list->freelist = tempnode->id;
tempnode->prev = -1;
tempnode->key = -1;
tempnode->free = 1;
}
tempnode = node;
}
for(j = 0; j < list->hash.basesize; j++)
{
list->hash.base[j] = -1;
}
list->head = -1;
list->tail = -1;
list->usage = 0;
return LRU_SUCCESS;
}
void main()
{
int key_table[LRU_SIZE];
int i, usage;
PLRU_LIST list;
PLRU_NODE node;
srand(time(NULL));
for(i = 0; i < LRU_SIZE; i++)
{
key_table[i] = rand() % LRU_SIZE;
printf("key_table[%d]:%d\n", i, key_table[i]);
}
list = (PLRU_LIST)malloc(sizeof(LRU_LIST));
if(!list)
printf("malloc error!\n");
lru_init(list, LRU_SIZE, HASH_BASE);
//node = (PLRU_NODE)malloc(sizeof(LRU_NODE));
//if(!node)
//printf("malloc error!\n");
for(i = 0; i < LRU_SIZE; i++)
{
if(LRU_MISS == lru_inquiry(list, key_table[i]))
{
if(LRU_SUCCESS == lru_getfree(list, &node))
{
node->key =key_table[i];
lru_insert(list, node);
}
else
{
printf("lru_getfree:get no free node!\n");
}
}
else
{
printf("***************\n");
printf("lru_inquiry hit key_table[%d]:%d\n", i, key_table[i]);
lru_show(list);
}
}
lru_show(list);
usage = list->usage;
for(i = 0; i < usage; i++)
{
lru_delete(list);
printf("after delete one node!\n");
lru_show(list);
}
lru_clear(list);
}