-
Notifications
You must be signed in to change notification settings - Fork 35
/
tinylfu.go
128 lines (100 loc) · 2.05 KB
/
tinylfu.go
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Package tinylfu is an implementation of the TinyLFU caching algorithm
/*
http://arxiv.org/abs/1512.00727
*/
package tinylfu
import "github.com/dgryski/go-tinylfu/internal/list"
type T[K comparable, V any] struct {
c *cm4
bouncer *doorkeeper
w int
samples int
lru *lruCache[K, V]
slru *slruCache[K, V]
data map[K]*list.Element[*slruItem[K, V]]
hash func(K) uint64
}
func New[K comparable, V any](size int, samples int, hash func(K) uint64) *T[K, V] {
const lruPct = 1
lruSize := (lruPct * size) / 100
if lruSize < 1 {
lruSize = 1
}
slruSize := size - lruSize
if slruSize < 1 {
slruSize = 1
}
slru20 := slruSize / 5
if slru20 < 1 {
slru20 = 1
}
data := make(map[K]*list.Element[*slruItem[K, V]], size)
return &T[K, V]{
c: newCM4(size),
w: 0,
samples: samples,
bouncer: newDoorkeeper(samples, 0.01),
data: data,
lru: newLRU(lruSize, data),
slru: newSLRU(slru20, slruSize-slru20, data),
hash: hash,
}
}
func (t *T[K, V]) Get(key K) (V, bool) {
t.w++
if t.w == t.samples {
t.c.reset()
t.bouncer.reset()
t.w = 0
}
val, ok := t.data[key]
if !ok {
keyh := t.hash(key)
t.c.add(keyh)
return *new(V), false
}
item := val.Value
t.c.add(item.keyh)
v := item.value
if item.listid == 0 {
t.lru.get(val)
} else {
t.slru.get(val)
}
return v, true
}
func (t *T[K, V]) Add(key K, val V) {
if e, ok := t.data[key]; ok {
// Key is already in our cache.
// `Add` will act as a `Get` for list movements
item := e.Value
item.value = val
t.c.add(item.keyh)
if item.listid == 0 {
t.lru.get(e)
} else {
t.slru.get(e)
}
return
}
newitem := slruItem[K, V]{0, key, val, t.hash(key)}
oitem, evicted := t.lru.add(newitem)
if !evicted {
return
}
// estimate count of what will be evicted from slru
victim := t.slru.victim()
if victim == nil {
t.slru.add(oitem)
return
}
if !t.bouncer.allow(oitem.keyh) {
return
}
vcount := t.c.estimate(victim.keyh)
ocount := t.c.estimate(oitem.keyh)
if ocount < vcount {
return
}
t.slru.add(oitem)
}