
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
常见的LRU实现策略是:
- 新数据插入到链表头部;
- 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
- 当链表满的时候,将链表尾部的数据丢弃。
但是如上实现存在一个问题:命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部,时间复杂度是$O(N)$。
未解决遍历带来的消耗,再此设计的是一种哈希表维护的双向链表结构,以减少寻找数据块时的时间消耗。
如上图所示,每个数据块拥有2个角度的身份,一个身份是缓存双向链表的成员,一个身份是哈希表成员。
LRU结构体设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| typedef struct cacheEntryS{ char key; char data; struct cacheEntryS *hashListPrev; struct cacheEntryS *hashListNext; struct cacheEntryS *lruListPrev; struct cacheEntryS *lruListNext; }cacheEntryS; typedef struct LRUCacheS{ int cacheCapacity; cacheEntryS **hashMap; cacheEntryS *lruListHead; cacheEntryS *lruListTail; int lruListSize; }LRUCacheS;
|
双休链表相关接口实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| static void removeFromList(LRUCacheS *cache, cacheEntryS *entry) { if (cache->lruListSize==0) return ; if (entry==cache->lruListHead && entry==cache->lruListTail) { cache->lruListHead = cache->lruListTail = NULL; } else if (entry == cache->lruListHead) { cache->lruListHead = entry->lruListNext; cache->lruListHead->lruListPrev = NULL; } else if (entry == cache->lruListTail) { cache->lruListTail = entry->lruListPrev; cache->lruListTail->lruListNext = NULL; } else { entry->lruListPrev->lruListNext = entry->lruListNext; entry->lruListNext->lruListPrev = entry->lruListPrev; } cache->lruListSize--; }
static cacheEntryS * insertToListHead(LRUCacheS *cache, cacheEntryS *entry) { cacheEntryS *removedEntry = NULL; if (++cache->lruListSize > cache->cacheCapacity) { removedEntry = cache->lruListTail; removeFromList(cache, cache->lruListTail); } if (cache->lruListHead==NULL&&cache->lruListTail==NULL) { cache->lruListHead = cache->lruListTail = entry; } else { entry->lruListNext = cache->lruListHead; entry->lruListPrev = NULL; cache->lruListHead->lruListPrev = entry; cache->lruListHead = entry; } return removedEntry; }
static void updateLRUList(LRUCacheS *cache, cacheEntryS *entry) { removeFromList(cache, entry); insertToListHead(cache, entry); }
|
哈希表相关接口实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| static int hashKey(LRUCacheS *cache, char key) { return (int)key%cache->cacheCapacity; }
static cacheEntryS *getValueFromHashMap(LRUCacheS *cache, int key) { cacheEntryS *entry = cache->hashMap[hashKey(cache,key)]; while (entry) { if (entry->key == key) break; entry = entry->hashListNext; } return entry; }
static void insertentryToHashMap(LRUCacheS *cache, cacheEntryS *entry) { cacheEntryS *n = cache->hashMap[hashKey(cache, entry->key)]; if (n!=NULL) { entry->hashListNext = n; n->hashListPrev = entry; } cache->hashMap[hashKey(cache,entry->key)] = entry; }
static void removeEntryFromHashMap(LRUCacheS *cache, cacheEntryS *entry) { if (NULL==entry || NULL==cache || NULL==cache->hashMap) return ; cacheEntryS *n = cache->hashMap[hashKey(cache,entry->key)]; while (n) { if (n->key==entry->key) { if (n->hashListPrev) { n->hashListPrev->hashListNext = n->hashListNext; } else { cache->hashMap[hashKey(cache, entry->key)] = n->hashListNext; } if (n->hashListNext) n->hashListNext->hashListPrev = n->hashListPrev; return ; } n = n->hashListNext; } } ```` LRU相关接口实现
```c
int LRUCacheSet(void *lruCache, char key, char data) { LRUCacheS *cache = (LRUCacheS *)lruCache; cacheEntryS *entry = getValueFromHashMap(cache, key); if (entry!=NULL) { entry->data = data; updateLRUList(cache, entry); } else { entry = newCacheEntry(key, data); cacheEntryS *removedEntry = insertToListHead(cache, entry); if (NULL != removedEntry) { removeEntryFromHashMap(cache, removedEntry); freeCacheEntry(removedEntry); } insertentryToHashMap(cache, entry); } return 0; }
char LRUCacheGet(void *lruCache, char key) { LRUCacheS *cache = (LRUCacheS *)lruCache; cacheEntryS *entry = getValueFromHashMap(cache,key); if (NULL != entry) { updateLRUList(cache, entry); return entry->data; } else { return '\0'; } }
|