forked from coocood/freecache
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcache.go
136 lines (121 loc) · 3.34 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
package freecache
import (
"crypto/md5"
"sync"
"sync/atomic"
"unsafe"
)
type Cache struct {
locks [256]sync.Mutex
segments [256]segment
hitCount int64
missCount int64
}
func hashFunc(data []byte) uint64 {
sum := md5.Sum(data)
return *(*uint64)(unsafe.Pointer(&sum[0]))
}
// 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) EvacuateCount() (count int64) {
for i := 0; i < 256; i++ {
count += atomic.LoadInt64(&cache.segments[i].totalEvacuate)
}
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)
}