forked from andeya/faygo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache.go
171 lines (150 loc) · 4.26 KB
/
cache.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
168
169
170
171
package freecache
import (
"encoding/binary"
"sync"
"sync/atomic"
"github.com/henrylee2cn/faygo/freecache/murmur3"
)
type Cache struct {
locks [256]sync.Mutex
segments [256]segment
hitCount int64
missCount int64
}
func hashFunc(data []byte) uint64 {
return murmur3.Sum64(data)
}
// The cache size will be set to 512KB at minimum.
// If the size is set relatively large, you should call
// `debug.SetGCPercent()`, set it to a much smaller value
// to limit the memory consumption and GC pause time.
func NewCache(size int) (cache *Cache) {
if size < 512*1024 {
size = 512 * 1024
}
cache = new(Cache)
for i := 0; i < 256; i++ {
cache.segments[i] = newSegment(size/256, i)
}
return
}
// If the key is larger than 65535 or value is larger than 1/1024 of the cache size,
// the entry will not be written to the cache. expireSeconds <= 0 means no expire,
// but it can be evicted when cache is full.
func (cache *Cache) Set(key, value []byte, expireSeconds int) (err error) {
hashVal := hashFunc(key)
segId := hashVal & 255
cache.locks[segId].Lock()
err = cache.segments[segId].set(key, value, hashVal, expireSeconds)
cache.locks[segId].Unlock()
return
}
// Get the value or not found error.
func (cache *Cache) Get(key []byte) (value []byte, err error) {
hashVal := hashFunc(key)
segId := hashVal & 255
cache.locks[segId].Lock()
value, err = cache.segments[segId].get(key, hashVal)
cache.locks[segId].Unlock()
if err == nil {
atomic.AddInt64(&cache.hitCount, 1)
} else {
atomic.AddInt64(&cache.missCount, 1)
}
return
}
func (cache *Cache) Del(key []byte) (affected bool) {
hashVal := hashFunc(key)
segId := hashVal & 255
cache.locks[segId].Lock()
affected = cache.segments[segId].del(key, hashVal)
cache.locks[segId].Unlock()
return
}
func (cache *Cache) SetInt(key int64, value []byte, expireSeconds int) (err error) {
var bKey [8]byte
binary.LittleEndian.PutUint64(bKey[:], uint64(key))
return cache.Set(bKey[:], value, expireSeconds)
}
func (cache *Cache) GetInt(key int64) (value []byte, err error) {
var bKey [8]byte
binary.LittleEndian.PutUint64(bKey[:], uint64(key))
return cache.Get(bKey[:])
}
func (cache *Cache) DelInt(key int64) (affected bool) {
var bKey [8]byte
binary.LittleEndian.PutUint64(bKey[:], uint64(key))
return cache.Del(bKey[:])
}
func (cache *Cache) EvacuateCount() (count int64) {
for i := 0; i < 256; i++ {
count += atomic.LoadInt64(&cache.segments[i].totalEvacuate)
}
return
}
func (cache *Cache) ExpiredCount() (count int64) {
for i := 0; i < 256; i++ {
count += atomic.LoadInt64(&cache.segments[i].totalExpired)
}
return
}
func (cache *Cache) EntryCount() (entryCount int64) {
for i := 0; i < 256; i++ {
entryCount += atomic.LoadInt64(&cache.segments[i].entryCount)
}
return
}
// The average unix timestamp when a entry being accessed.
// Entries have greater access time will be evacuated when it
// is about to be overwritten by new value.
func (cache *Cache) AverageAccessTime() int64 {
var entryCount, totalTime int64
for i := 0; i < 256; i++ {
totalTime += atomic.LoadInt64(&cache.segments[i].totalTime)
entryCount += atomic.LoadInt64(&cache.segments[i].totalCount)
}
if entryCount == 0 {
return 0
} else {
return totalTime / entryCount
}
}
func (cache *Cache) HitCount() int64 {
return atomic.LoadInt64(&cache.hitCount)
}
func (cache *Cache) LookupCount() int64 {
return atomic.LoadInt64(&cache.hitCount) + atomic.LoadInt64(&cache.missCount)
}
func (cache *Cache) HitRate() float64 {
lookupCount := cache.LookupCount()
if lookupCount == 0 {
return 0
} else {
return float64(cache.HitCount()) / float64(lookupCount)
}
}
func (cache *Cache) OverwriteCount() (overwriteCount int64) {
for i := 0; i < 256; i++ {
overwriteCount += atomic.LoadInt64(&cache.segments[i].overwrites)
}
return
}
func (cache *Cache) Clear() {
for i := 0; i < 256; i++ {
cache.locks[i].Lock()
newSeg := newSegment(len(cache.segments[i].rb.data), i)
cache.segments[i] = newSeg
cache.locks[i].Unlock()
}
atomic.StoreInt64(&cache.hitCount, 0)
atomic.StoreInt64(&cache.missCount, 0)
}
func (cache *Cache) ResetStatistics() {
atomic.StoreInt64(&cache.hitCount, 0)
atomic.StoreInt64(&cache.missCount, 0)
for i := 0; i < 256; i++ {
cache.locks[i].Lock()
cache.segments[i].resetStatistics()
cache.locks[i].Unlock()
}
}