Skip to content

Commit 7ce6f7d

Browse files
committed
add lru option
1 parent d585e85 commit 7ce6f7d

File tree

8 files changed

+146
-100
lines changed

8 files changed

+146
-100
lines changed

benchmark/benchmark_test.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
const (
1414
sharding = 128
15+
capacity = 10000
1516
benchcount = 1 << 20
1617
)
1718

@@ -20,8 +21,9 @@ var (
2021

2122
options = []memorycache.Option{
2223
memorycache.WithBucketNum(sharding),
23-
memorycache.WithBucketSize(benchcount/sharding/10, benchcount/sharding),
24-
memorycache.WithSwissTable(),
24+
memorycache.WithBucketSize(capacity/10, capacity),
25+
memorycache.WithSwissTable(true),
26+
memorycache.WithLRU(true),
2527
}
2628
)
2729

@@ -87,9 +89,9 @@ func BenchmarkMemoryCache_SetAndGet(b *testing.B) {
8789

8890
func BenchmarkRistretto_Set(b *testing.B) {
8991
var mc, _ = ristretto.NewCache(&ristretto.Config{
90-
NumCounters: benchcount * 10, // number of keys to track frequency of (10M).
91-
MaxCost: 1 << 30, // maximum cost of cache (1GB).
92-
BufferItems: 64, // number of keys per Get buffer.
92+
NumCounters: capacity * sharding * 10, // number of keys to track frequency of (10M).
93+
MaxCost: 1 << 30, // maximum cost of cache (1GB).
94+
BufferItems: 64, // number of keys per Get buffer.
9395
})
9496
b.RunParallel(func(pb *testing.PB) {
9597
var i = 0
@@ -103,9 +105,9 @@ func BenchmarkRistretto_Set(b *testing.B) {
103105

104106
func BenchmarkRistretto_Get(b *testing.B) {
105107
var mc, _ = ristretto.NewCache(&ristretto.Config{
106-
NumCounters: benchcount * 10, // number of keys to track frequency of (10M).
107-
MaxCost: 1 << 30, // maximum cost of cache (1GB).
108-
BufferItems: 64, // number of keys per Get buffer.
108+
NumCounters: capacity * sharding * 10, // number of keys to track frequency of (10M).
109+
MaxCost: 1 << 30, // maximum cost of cache (1GB).
110+
BufferItems: 64, // number of keys per Get buffer.
109111
})
110112
for i := 0; i < benchcount; i++ {
111113
mc.SetWithTTL(benchkeys[i%benchcount], 1, 1, time.Hour)
@@ -124,9 +126,9 @@ func BenchmarkRistretto_Get(b *testing.B) {
124126

125127
func BenchmarkRistretto_SetAndGet(b *testing.B) {
126128
var mc, _ = ristretto.NewCache(&ristretto.Config{
127-
NumCounters: benchcount * 10, // number of keys to track frequency of (10M).
128-
MaxCost: 1 << 30, // maximum cost of cache (1GB).
129-
BufferItems: 64, // number of keys per Get buffer.
129+
NumCounters: capacity * sharding * 10, // number of keys to track frequency of (10M).
130+
MaxCost: 1 << 30, // maximum cost of cache (1GB).
131+
BufferItems: 64, // number of keys per Get buffer.
130132
})
131133
for i := 0; i < benchcount; i++ {
132134
mc.SetWithTTL(benchkeys[i%benchcount], 1, 1, time.Hour)
@@ -148,7 +150,7 @@ func BenchmarkRistretto_SetAndGet(b *testing.B) {
148150
}
149151

150152
func BenchmarkTheine_Set(b *testing.B) {
151-
mc, _ := theine.NewBuilder[string, int](benchcount).Build()
153+
mc, _ := theine.NewBuilder[string, int](sharding * capacity).Build()
152154
b.RunParallel(func(pb *testing.PB) {
153155
i := 0
154156
for pb.Next() {
@@ -160,7 +162,7 @@ func BenchmarkTheine_Set(b *testing.B) {
160162
}
161163

162164
func BenchmarkTheine_Get(b *testing.B) {
163-
mc, _ := theine.NewBuilder[string, int](benchcount).Build()
165+
mc, _ := theine.NewBuilder[string, int](sharding * capacity).Build()
164166
for i := 0; i < benchcount; i++ {
165167
mc.SetWithTTL(benchkeys[i%benchcount], 1, 1, time.Hour)
166168
}
@@ -177,7 +179,7 @@ func BenchmarkTheine_Get(b *testing.B) {
177179
}
178180

179181
func BenchmarkTheine_SetAndGet(b *testing.B) {
180-
mc, _ := theine.NewBuilder[string, int](benchcount).Build()
182+
mc, _ := theine.NewBuilder[string, int](sharding * capacity).Build()
181183
for i := 0; i < benchcount; i++ {
182184
mc.SetWithTTL(benchkeys[i%benchcount], 1, 1, time.Hour)
183185
}

cache.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type MemoryCache[K comparable, V any] struct {
2727
// New 创建缓存数据库实例
2828
// Creating a Cached Database Instance
2929
func New[K comparable, V any](options ...Option) *MemoryCache[K, V] {
30-
var conf = &config{TimeCacheEnabled: true}
30+
var conf = &config{CachedTime: true}
3131
options = append(options, withInitialize())
3232
for _, fn := range options {
3333
fn(conf)
@@ -46,10 +46,10 @@ func New[K comparable, V any](options ...Option) *MemoryCache[K, V] {
4646

4747
for i, _ := range mc.storage {
4848
mc.storage[i] = &bucket[K, V]{
49-
MaxCapacity: conf.MaxCapacity,
50-
Map: containers.NewMap[K, *Element[K, V]](conf.InitialSize, conf.SwissTable),
51-
Heap: newHeap[K, V](conf.InitialSize),
52-
List: new(queue[K, V]),
49+
conf: conf,
50+
Map: containers.NewMap[K, *Element[K, V]](conf.BucketSize, conf.SwissTable),
51+
Heap: newHeap[K, V](conf.BucketSize),
52+
List: newQueue[K, V](conf.LRU),
5353
}
5454
}
5555

@@ -66,11 +66,11 @@ func New[K comparable, V any](options ...Option) *MemoryCache[K, V] {
6666
case now := <-ticker.C:
6767
var sum = 0
6868
for _, b := range mc.storage {
69-
sum += b.ExpireCheck(now.UnixMilli(), conf.MaxKeysDeleted)
69+
sum += b.ExpireCheck(now.UnixMilli(), conf.DeleteLimits)
7070
}
7171

7272
// 删除数量超过阈值, 缩小时间间隔
73-
if d1 := utils.SelectValue(sum > conf.BucketNum*conf.MaxKeysDeleted*7/10, conf.MinInterval, conf.MaxInterval); d1 != d0 {
73+
if d1 := utils.SelectValue(sum > conf.BucketNum*conf.DeleteLimits*7/10, conf.MinInterval, conf.MaxInterval); d1 != d0 {
7474
d0 = d1
7575
ticker.Reset(d0)
7676
}
@@ -102,9 +102,9 @@ func New[K comparable, V any](options ...Option) *MemoryCache[K, V] {
102102
func (c *MemoryCache[K, V]) Clear() {
103103
for _, b := range c.storage {
104104
b.Lock()
105-
b.Heap = newHeap[K, V](c.conf.InitialSize)
106-
b.Map = containers.NewMap[K, *Element[K, V]](c.conf.InitialSize, c.conf.SwissTable)
107-
b.List = new(queue[K, V])
105+
b.Heap = newHeap[K, V](c.conf.BucketSize)
106+
b.Map = containers.NewMap[K, *Element[K, V]](c.conf.BucketSize, c.conf.SwissTable)
107+
b.List = newQueue[K, V](c.conf.LRU)
108108
b.Unlock()
109109
}
110110
}
@@ -123,7 +123,7 @@ func (c *MemoryCache[K, V]) getBucket(key K) *bucket[K, V] {
123123
}
124124

125125
func (c *MemoryCache[K, V]) getTimestamp() int64 {
126-
if c.conf.TimeCacheEnabled {
126+
if c.conf.CachedTime {
127127
return c.timestamp.Load()
128128
}
129129
return time.Now().UnixMilli()
@@ -276,10 +276,10 @@ func (c *MemoryCache[K, V]) Len() int {
276276

277277
type bucket[K comparable, V any] struct {
278278
sync.Mutex
279-
MaxCapacity int
280-
Map containers.Map[K, *Element[K, V]]
281-
Heap *heap[K, V]
282-
List *queue[K, V]
279+
conf *config
280+
Map containers.Map[K, *Element[K, V]]
281+
Heap *heap[K, V]
282+
List *queue[K, V]
283283
}
284284

285285
// ExpireCheck 过期时间检查
@@ -314,8 +314,9 @@ func (c *bucket[K, V]) UpdateTTL(ele *Element[K, V], expireAt int64) {
314314
}
315315

316316
func (c *bucket[K, V]) Insert(key K, value V, expireAt int64, cb CallbackFunc[*Element[K, V]]) {
317-
if c.List.Len() >= c.MaxCapacity {
318-
c.Delete(c.List.Front(), ReasonEvicted)
317+
if c.Heap.Len() >= c.conf.BucketCap {
318+
head := utils.SelectValue(c.conf.LRU, c.List.Front(), c.Heap.Front())
319+
c.Delete(head, ReasonEvicted)
319320
}
320321

321322
var ele = &Element[K, V]{Key: key, Value: value, ExpireAt: expireAt, cb: cb}

cache_test.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func TestMemoryCache(t *testing.T) {
2828
var db = New[string, any](
2929
WithInterval(10*time.Millisecond, 10*time.Millisecond),
3030
WithBucketNum(1),
31-
WithTimeCache(false),
31+
WithCachedTime(false),
3232
)
3333
db.Set("a", 1, 100*time.Millisecond)
3434
db.Set("b", 1, 300*time.Millisecond)
@@ -45,7 +45,7 @@ func TestMemoryCache(t *testing.T) {
4545
t.Run("", func(t *testing.T) {
4646
var db = New[string, any](
4747
WithInterval(10*time.Millisecond, 10*time.Millisecond),
48-
WithTimeCache(false),
48+
WithCachedTime(false),
4949
)
5050
db.Set("a", 1, 100*time.Millisecond)
5151
db.Set("b", 1, 200*time.Millisecond)
@@ -62,7 +62,7 @@ func TestMemoryCache(t *testing.T) {
6262
t.Run("", func(t *testing.T) {
6363
var db = New[string, any](
6464
WithInterval(10*time.Millisecond, 10*time.Millisecond),
65-
WithTimeCache(false),
65+
WithCachedTime(false),
6666
)
6767
db.Set("a", 1, 100*time.Millisecond)
6868
db.Set("b", 1, 200*time.Millisecond)
@@ -80,7 +80,7 @@ func TestMemoryCache(t *testing.T) {
8080
var mc = New[string, any](
8181
WithInterval(10*time.Millisecond, 10*time.Millisecond),
8282
WithBucketNum(1),
83-
WithTimeCache(false),
83+
WithCachedTime(false),
8484
)
8585
var m1 = make(map[string]int)
8686
var m2 = make(map[string]int64)
@@ -121,9 +121,9 @@ func TestMemoryCache(t *testing.T) {
121121
t.Run("expire", func(t *testing.T) {
122122
var mc = New[string, any](
123123
WithBucketNum(1),
124-
WithMaxKeysDeleted(3),
124+
WithDeleteLimits(3),
125125
WithInterval(50*time.Millisecond, 100*time.Millisecond),
126-
WithTimeCache(false),
126+
WithCachedTime(false),
127127
)
128128
mc.Set("a", 1, 150*time.Millisecond)
129129
mc.Set("b", 1, 150*time.Millisecond)
@@ -161,6 +161,7 @@ func TestMemoryCache_Set(t *testing.T) {
161161
var mc = New[string, any](
162162
WithBucketNum(1),
163163
WithBucketSize(0, 2),
164+
WithLRU(true),
164165
)
165166
mc.Set("ming", 1, 3*time.Hour)
166167
mc.Set("hong", 1, 1*time.Hour)
@@ -578,7 +579,10 @@ func TestMemoryCache_Range(t *testing.T) {
578579

579580
func TestMemoryCache_LRU(t *testing.T) {
580581
const count = 10000
581-
var mc = New[string, int](WithBucketNum(1))
582+
var mc = New[string, int](
583+
WithBucketNum(1),
584+
WithLRU(true),
585+
)
582586
var indexes []int
583587
for i := 0; i < count; i++ {
584588
indexes = append(indexes, i)
@@ -605,7 +609,9 @@ func TestMemoryCache_LRU(t *testing.T) {
605609
}
606610

607611
func TestMemoryCache_Random(t *testing.T) {
608-
var mc = New[string, int]()
612+
var mc = New[string, int](
613+
WithLRU(true),
614+
)
609615
const count = 10000
610616
for i := 0; i < count; i++ {
611617
var key = string(utils.AlphabetNumeric.Generate(3))

options.go

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import (
77
)
88

99
const (
10-
defaultBucketNum = 16
11-
defaultMinInterval = 5 * time.Second
12-
defaultMaxInterval = 30 * time.Second
13-
defaultMaxKeysDeleted = 1000
14-
defaultInitialSize = 1000
15-
defaultMaxCapacity = 100000
10+
defaultBucketNum = 16
11+
defaultMinInterval = 5 * time.Second
12+
defaultMaxInterval = 30 * time.Second
13+
defaultDeleteLimits = 1000
14+
defaultBucketSize = 1000
15+
defaultBucketCap = 100000
1616
)
1717

1818
type Option func(c *config)
@@ -25,20 +25,20 @@ func WithBucketNum(num int) Option {
2525
}
2626
}
2727

28-
// WithMaxKeysDeleted 设置每次TTL检查最大删除key数量. (单个存储桶)
28+
// WithDeleteLimits 设置每次TTL检查最大删除key数量. (单个存储桶)
2929
// Set the maximum number of keys to be deleted per TTL check (single bucket)
30-
func WithMaxKeysDeleted(num int) Option {
30+
func WithDeleteLimits(num int) Option {
3131
return func(c *config) {
32-
c.MaxKeysDeleted = num
32+
c.DeleteLimits = num
3333
}
3434
}
3535

3636
// WithBucketSize 设置初始化大小和最大容量. 超过最大容量会被清除. (单个存储桶)
3737
// Set the initial size and maximum capacity. Exceeding the maximum capacity will be erased. (Single bucket)
3838
func WithBucketSize(size, cap int) Option {
3939
return func(c *config) {
40-
c.InitialSize = size
41-
c.MaxCapacity = cap
40+
c.BucketSize = size
41+
c.BucketCap = cap
4242
}
4343
}
4444

@@ -51,19 +51,27 @@ func WithInterval(min, max time.Duration) Option {
5151
}
5252
}
5353

54-
// WithTimeCache 是否开启时间缓存
54+
// WithCachedTime 是否开启时间缓存
5555
// Whether to turn on time caching
56-
func WithTimeCache(enabled bool) Option {
56+
func WithCachedTime(enabled bool) Option {
5757
return func(c *config) {
58-
c.TimeCacheEnabled = enabled
58+
c.CachedTime = enabled
5959
}
6060
}
6161

6262
// WithSwissTable 使用swiss table替代runtime map
6363
// Using swiss table instead of runtime map
64-
func WithSwissTable() Option {
64+
func WithSwissTable(enabled bool) Option {
6565
return func(c *config) {
66-
c.SwissTable = true
66+
c.SwissTable = enabled
67+
}
68+
}
69+
70+
// WithLRU 是否开启LRU缓存驱逐算法. 默认为false
71+
// Whether to enable LRU cache eviction. Default is false
72+
func WithLRU(enabled bool) Option {
73+
return func(c *config) {
74+
c.LRU = enabled
6775
}
6876
}
6977

@@ -82,16 +90,46 @@ func withInitialize() Option {
8290
c.MaxInterval = defaultMaxInterval
8391
}
8492

85-
if c.MaxKeysDeleted <= 0 {
86-
c.MaxKeysDeleted = defaultMaxKeysDeleted
93+
if c.DeleteLimits <= 0 {
94+
c.DeleteLimits = defaultDeleteLimits
8795
}
8896

89-
if c.InitialSize <= 0 {
90-
c.InitialSize = defaultInitialSize
97+
if c.BucketSize <= 0 {
98+
c.BucketSize = defaultBucketSize
9199
}
92100

93-
if c.MaxCapacity <= 0 {
94-
c.MaxCapacity = defaultMaxCapacity
101+
if c.BucketCap <= 0 {
102+
c.BucketCap = defaultBucketCap
95103
}
96104
}
97105
}
106+
107+
type config struct {
108+
// 检查周期, 默认30s, 最小检查周期为5s
109+
// Check period, default 30s, minimum 5s.
110+
MinInterval, MaxInterval time.Duration
111+
112+
// 存储桶的初始化大小和最大容量, 默认为1000和100000
113+
// Initialized bucket size and maximum capacity, defaults to 1000 and 100000.
114+
BucketSize, BucketCap int
115+
116+
// 存储桶数量, 默认为16
117+
// Number of buckets, default is 16
118+
BucketNum int
119+
120+
// 每次检查至多删除key的数量(单个存储桶)
121+
// The number of keys to be deleted per check (for a single bucket).
122+
DeleteLimits int
123+
124+
// 是否开启时间缓存, 默认为true
125+
// Whether to enable time caching, true by default.
126+
CachedTime bool
127+
128+
// 是否使用swiss table, 默认为false
129+
// Whether to use swiss table, false by default.
130+
SwissTable bool
131+
132+
// 是否开启LRU缓存驱逐算法. 默认为false
133+
// Whether to enable LRU cache eviction. Default is false
134+
LRU bool
135+
}

0 commit comments

Comments
 (0)