Compare commits

..

No commits in common. 'dev' and 'master' have entirely different histories.
dev ... master

2
.gitignore vendored

@ -1,4 +1,4 @@
### Go template
# ---> Go
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#

@ -0,0 +1,9 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,3 @@
# study-go-cache
golang学习

@ -1,18 +0,0 @@
package cache
type Cache interface {
// Set 设置/添加一个缓存,如果 key 存在,用新值覆盖旧值
Set(key string, value any)
// Get 通过 key 获取一个缓存值
Get(Key string) any
// Del 通过 key 删除一个缓存值
Del(Key string)
// DelOldest 删除最“无用”的一个缓存值
DelOldest()
// Len 获取缓存已存在的记录数
Len() int
}
type Value interface {
Len() int
}

@ -1,97 +0,0 @@
package fifo
import (
"cache"
"container/list"
)
/**
*
*/
type fifo struct {
// 缓存最大容量
maxBytes int
// 当一个 entry 从缓存中移除时调用该回调函数,默认为 nil
onEvicted func(key string, value any)
//已使用的字节数只包括值key 不算
usedBytes int
//缓存容器,存放entry
ll *list.List
//指针容器
cache map[string]*list.Element
}
func New(maxBytes int, onEvicted func(key string, value any)) cache.Cache {
return &fifo{
maxBytes: maxBytes,
onEvicted: onEvicted,
ll: list.New(),
cache: make(map[string]*list.Element),
}
}
// Set 往 Cache 尾部增加一个元素(如果已经存在,则放入尾部,并修改值)
func (f *fifo) Set(key string, value any) {
//元素存在
if e, ok := f.cache[key]; ok {
f.ll.MoveToBack(e)
en := e.Value.(*entry)
f.usedBytes = f.usedBytes - cache.CalcLen(en.value) + cache.CalcLen(value)
en.value = value
} else {
en := &entry{key, value}
e := f.ll.PushBack(en)
f.cache[key] = e
f.usedBytes += en.Len()
//元素容量校验
if f.CheckOverFlow() {
f.DelOldest()
}
}
}
// CheckOverFlow 校验容量是否溢出
func (f *fifo) CheckOverFlow() bool {
return f.maxBytes > 0 && f.usedBytes > f.maxBytes
}
func (f *fifo) Get(key string) any {
if e, ok := f.cache[key]; ok {
return e.Value.(*entry).value
}
return nil
}
func (f *fifo) Del(key string) {
if e, ok := f.cache[key]; ok {
f.removeElement(e)
}
}
func (f *fifo) DelOldest() {
//删除列表头部元素
f.removeElement(f.ll.Front())
}
func (f *fifo) Len() int {
return f.ll.Len()
}
// removeElement 删除元素
func (f *fifo) removeElement(e *list.Element) {
if e == nil {
return
}
f.ll.Remove(e)
en := e.Value.(*entry)
f.usedBytes -= en.Len()
delete(f.cache, en.key)
if f.onEvicted != nil {
f.onEvicted(en.key, en.value)
}
}
type entry struct {
key string
value any
}
func (e *entry) Len() int {
return cache.CalcLen(e.value)
}

@ -1,42 +0,0 @@
package fifo_test
import (
"cache/fifo"
"github.com/matryer/is"
"testing"
)
// TestFifo
//
// @Description: 单元测试
// @param t
func TestFifo(t *testing.T) {
is := is.New(t)
cache := fifo.New(24, nil)
cache.DelOldest()
cache.Set("k1", 1)
v := cache.Get("k1")
is.Equal(v, 1)
cache.Del("k1")
is.Equal(0, cache.Len())
}
// TestFifoWithCallBack
//
// @Description: 带删除回调
// @param t
func TestFifoWithCallBack(t *testing.T) {
is := is.New(t)
keys := make([]string, 8)
onEvicted := func(key string, value any) {
keys = append(keys, key)
}
cache := fifo.New(24, onEvicted)
cache.Set("k1", int8(1))
cache.Set("k2", int8(2))
cache.Set("k3", int8(3))
cache.Set("k4", int8(4))
expected := []string{"k1", "k2"}
is.Equal(expected, keys)
is.Equal(2, cache.Len())
}

@ -1,5 +0,0 @@
module cache
go 1.21.5
require github.com/matryer/is v1.4.1

@ -1,2 +0,0 @@
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=

@ -1,41 +0,0 @@
package cache
import (
"fmt"
"runtime"
)
func CalcLen(value any) int {
var n int
switch v := value.(type) {
case Value:
n = v.Len()
case string:
if runtime.GOARCH == "amd64" {
n = 16 + len(v)
} else {
n = 8 + len(v)
}
case bool, uint8, int8:
n = 1
case int16, uint16:
n = 2
case int32, uint32, float32:
n = 4
case int64, uint64, float64:
n = 8
case int, uint:
if runtime.GOARCH == "amd64" {
n = 8
} else {
n = 4
}
case complex64:
n = 8
case complex128:
n = 16
default:
panic(fmt.Sprintf("%T is not implement cache.Value", value))
}
return n
}

@ -1,85 +0,0 @@
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 {
l.removeElement(heap.Pop(l.queue))
}
}
}
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() {
if l.queue.Len() == 0 {
return
}
l.removeElement(heap.Pop(l.queue))
}
func (l *lfu) Len() int {
return l.queue.Len()
}
func (l *lfu) removeElement(x any) {
if x == nil {
return
}
en := x.(*entry)
delete(l.cache, en.key)
l.usedBytes -= en.Len()
if l.onEvicted != nil {
l.onEvicted(en.key, en.value)
}
}

@ -1,34 +0,0 @@
package lfu_test
import (
"cache/lfu"
"github.com/matryer/is"
"testing"
)
func TestSet(t *testing.T) {
is := is.New(t)
cache := lfu.New(24, nil)
cache.DelOldest()
cache.Set("k1", 1)
v := cache.Get("k1")
is.Equal(v, 1)
cache.Del("k1")
is.Equal(0, cache.Len())
}
func TestOnEvicted(t *testing.T) {
is := is.New(t)
keys := make([]string, 0, 8)
onEvicted := func(key string, value interface{}) {
keys = append(keys, key)
}
cache := lfu.New(32, onEvicted)
cache.Set("k1", 1)
cache.Set("k2", 2)
cache.Set("k3", 3)
cache.Set("k4", 4)
expected := []string{"k1", "k3"}
is.Equal(expected, keys)
is.Equal(2, cache.Len())
}

@ -1,62 +0,0 @@
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)
}
Loading…
Cancel
Save