-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy patharc.go
167 lines (147 loc) · 3.03 KB
/
arc.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Package arc implements an Adaptive replacement cache
package arc
import (
"container/list"
"sync"
)
type ARC struct {
p int
c int
t1 *list.List
b1 *list.List
t2 *list.List
b2 *list.List
mutex sync.RWMutex
len int
cache map[interface{}]*entry
}
// New returns a new Adaptive Replacement Cache (ARC).
func New(c int) *ARC {
return &ARC{
p: 0,
c: c,
t1: list.New(),
b1: list.New(),
t2: list.New(),
b2: list.New(),
len: 0,
cache: make(map[interface{}]*entry, c),
}
}
// Put inserts a new key-value pair into the cache.
// This optimizes future access to this entry (side effect).
func (a *ARC) Put(key, value interface{}) bool {
a.mutex.Lock()
defer a.mutex.Unlock()
ent, ok := a.cache[key]
if ok != true {
a.len++
ent = &entry{
key: key,
value: value,
ghost: false,
}
a.req(ent)
a.cache[key] = ent
} else {
if ent.ghost {
a.len++
}
ent.value = value
ent.ghost = false
a.req(ent)
}
return ok
}
// Get retrieves a previously via Set inserted entry.
// This optimizes future access to this entry (side effect).
func (a *ARC) Get(key interface{}) (value interface{}, ok bool) {
a.mutex.Lock()
defer a.mutex.Unlock()
ent, ok := a.cache[key]
if ok {
a.req(ent)
return ent.value, !ent.ghost
}
return nil, false
}
// Len determines the number of currently cached entries.
// This method is side-effect free in the sense that it does not attempt to optimize random cache access.
func (a *ARC) Len() int {
a.mutex.Lock()
defer a.mutex.Unlock()
return a.len
}
func (a *ARC) req(ent *entry) {
if ent.ll == a.t1 || ent.ll == a.t2 {
// Case I
ent.setMRU(a.t2)
} else if ent.ll == a.b1 {
// Case II
// Cache Miss in t1 and t2
// Adaptation
var d int
if a.b1.Len() >= a.b2.Len() {
d = 1
} else {
d = a.b2.Len() / a.b1.Len()
}
a.p = min(a.p+d, a.c)
a.replace(ent)
ent.setMRU(a.t2)
} else if ent.ll == a.b2 {
// Case III
// Cache Miss in t1 and t2
// Adaptation
var d int
if a.b2.Len() >= a.b1.Len() {
d = 1
} else {
d = a.b1.Len() / a.b2.Len()
}
a.p = max(a.p-d, 0)
a.replace(ent)
ent.setMRU(a.t2)
} else if ent.ll == nil {
// Case IV
if a.t1.Len()+a.b1.Len() == a.c {
// Case A
if a.t1.Len() < a.c {
a.delLRU(a.b1)
a.replace(ent)
} else {
a.delLRU(a.t1)
}
} else if a.t1.Len()+a.b1.Len() < a.c {
// Case B
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {
a.delLRU(a.b2)
}
a.replace(ent)
}
}
ent.setMRU(a.t1)
}
}
func (a *ARC) delLRU(list *list.List) {
lru := list.Back()
list.Remove(lru)
a.len--
delete(a.cache, lru.Value.(*entry).key)
}
func (a *ARC) replace(ent *entry) {
if a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) {
lru := a.t1.Back().Value.(*entry)
lru.value = nil
lru.ghost = true
a.len--
lru.setMRU(a.b1)
} else {
lru := a.t2.Back().Value.(*entry)
lru.value = nil
lru.ghost = true
a.len--
lru.setMRU(a.b2)
}
}