forked from dgryski/go-tinylfu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtinylfu.go
103 lines (79 loc) · 1.67 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
// Package tinylfu is an implementation of the TinyLFU caching algorithm
/*
http://arxiv.org/abs/1512.00727
*/
package tinylfu
import (
"container/list"
"github.com/dchest/siphash"
)
type T struct {
c *cm4
bouncer *doorkeeper
w int
samples int
lru *lruCache
slru *slruCache
data map[string]*list.Element
}
func New(size int, samples int) *T {
const lruPct = 1
lruSize := (lruPct * size) / 100
slruSize := int(float64(size) * ((100.0 - lruPct) / 100.0))
slru20 := int(0.2 * float64(slruSize))
data := make(map[string]*list.Element, size)
return &T{
c: newCM4(size),
w: 0,
samples: samples,
bouncer: newDoorkeeper(samples, 0.01),
data: data,
lru: newLRU(lruSize, data),
slru: newSLRU(slru20, slruSize-slru20, data),
}
}
func (t *T) Get(key string) (interface{}, 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 := siphash.Hash(0, 0, stringToSlice(key))
t.c.add(keyh)
return nil, false
}
item := val.Value.(*slruItem)
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) Add(key string, val interface{}) {
newitem := slruItem{0, key, val, siphash.Hash(0, 0, stringToSlice(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)
}