From d49f187da6b6ed06a1188007016f789886bf6217 Mon Sep 17 00:00:00 2001 From: old-tom <892955278@qq.com> Date: Fri, 22 Dec 2023 17:55:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:lru=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lfu/lfu.go | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lfu/queue.go | 62 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 lfu/lfu.go create mode 100644 lfu/queue.go diff --git a/lfu/lfu.go b/lfu/lfu.go new file mode 100644 index 0000000..11aa377 --- /dev/null +++ b/lfu/lfu.go @@ -0,0 +1,78 @@ +package lfu + +import ( + "cache" + "container/heap" +) + +// lfu +// @Description:LFU,即最少使用,也就是淘汰缓存中访问频率最低的记录。 +// LFU 认为,如果数据过去被访问多次,那么将来被访问的频率也更高。 +// LFU 的实现需要维护一个按照访问次数排序的队列,每次访问,访问次数加 1,队列重新排序,淘汰时选择访问次数最少的即可 +type lfu struct { + // 缓存最大容量 + maxBytes int + // 当一个 entry 从缓存中移除时调用该回调函数,默认为 nil + onEvicted func(key string, value any) + //已使用的字节数,只包括值,key 不算 + usedBytes int + queue *queue + cache map[string]*entry +} + +func New(maxBytes int, onEvicted func(key string, value any)) cache.Cache { + q := make(queue, 0, 1024) + return &lfu{ + maxBytes: maxBytes, + onEvicted: onEvicted, + queue: &q, + cache: make(map[string]*entry), + } +} + +func (l *lfu) Set(key string, value any) { + if e, ok := l.cache[key]; ok { + l.usedBytes = l.usedBytes - cache.CalcLen(e.value) + cache.CalcLen(value) + l.queue.update(e, value, e.weight+1) + } else { + en := &entry{key: key, value: value} + //利用堆特性实现排序 + heap.Push(l.queue, en) + l.cache[key] = en + l.usedBytes += en.Len() + if l.maxBytes > 0 && l.usedBytes > l.maxBytes { + + } + } +} + +func (l *lfu) Get(Key string) any { + if e, ok := l.cache[Key]; ok { + l.queue.update(e, e.value, e.weight+1) + return e.value + } + return nil +} + +func (l *lfu) Del(Key string) { + if e, ok := l.cache[Key]; ok { + heap.Remove(l.queue, e.index) + } +} + +func (l *lfu) DelOldest() { + +} + +func (l *lfu) Len() int { + return 0 +} + +func (l *lfu) removeElement(x any) { + if x == nil { + return + } + en := x.(*entry) + delete(l.cache, en.key) + l.usedBytes -= en.Len() +} diff --git a/lfu/queue.go b/lfu/queue.go new file mode 100644 index 0000000..1c6f343 --- /dev/null +++ b/lfu/queue.go @@ -0,0 +1,62 @@ +package lfu + +import ( + "cache" + "container/heap" +) + +type entry struct { + key string + value any + //权重或优先级 + weight int + //下标 + index int +} + +func (e *entry) Len() int { + return cache.CalcLen(e.value) + 8 +} + +type queue []*entry + +// Len +// +// @Description: 长度 +// @receiver e +// @return int +func (q queue) Len() int { + return len(q) +} + +func (q queue) Less(i, j int) bool { + return q[i].weight < q[j].weight +} + +func (q queue) Swap(i, j int) { + q[i], q[j] = q[j], q[i] + q[i].index = i + q[j].index = j +} + +func (q *queue) Push(x any) { + n := len(*q) + en := x.(*entry) + en.index = n + *q = append(*q, en) +} + +func (q *queue) Pop() any { + old := *q + n := len(old) + en := old[n-1] + en.index = -1 + *q = old[0 : n-1] + return en +} + +func (q *queue) update(en *entry, value any, weight int) { + en.value = value + en.weight = weight + heap.Fix(q, en.index) +}