Skip to content

Commit 2fcc8ab

Browse files
authored
[patch] remove cornelk/hashmap due to the slow write performance, and use customized sync.Map generics version (#132)
* [patch] remove cornelk/hashmap due to the slow write performance, and use original sync.Map generics version Signed-off-by: kpango <[email protected]>
1 parent 0a329cf commit 2fcc8ab

File tree

5 files changed

+405
-74
lines changed

5 files changed

+405
-74
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ profile: clean init
3131
mkdir bench
3232
mkdir pprof
3333
\
34+
# go test -count=3 -timeout=30m -run=NONE -bench=BenchmarkChangeOutAllInt_gache -benchmem -o pprof/gache-test.bin -cpuprofile pprof/cpu-gache.out -memprofile pprof/mem-gache.out
3435
go test -count=3 -timeout=30m -run=NONE -bench=BenchmarkGacheSetBigDataWithTTL -benchmem -o pprof/gache-test.bin -cpuprofile pprof/cpu-gache.out -memprofile pprof/mem-gache.out
3536
go tool pprof --svg pprof/gache-test.bin pprof/cpu-gache.out > cpu-gache.svg
3637
go tool pprof --svg pprof/gache-test.bin pprof/mem-gache.out > mem-gache.svg

gache.go

+56-68
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import (
1010
"time"
1111
"unsafe"
1212

13-
"github.com/cornelk/hashmap"
1413
"github.com/kpango/fastime"
1514
"github.com/zeebo/xxh3"
16-
"golang.org/x/sync/singleflight"
1715
)
1816

1917
type (
@@ -64,10 +62,9 @@ type (
6462
expire int64
6563
l uint64
6664
cancel atomic.Pointer[context.CancelFunc]
67-
expGroup singleflight.Group
6865
expChan chan string
6966
expFunc func(context.Context, string)
70-
shards [slen]*hashmap.Map[string, value[V]]
67+
shards [slen]*Map[string, *value[V]]
7168
}
7269

7370
value[V any] struct {
@@ -101,23 +98,19 @@ func New[V any](opts ...Option[V]) Gache[V] {
10198
return g
10299
}
103100

104-
func newMap[V any]() (m *hashmap.Map[string, value[V]]) {
105-
m = hashmap.New[string, value[V]]()
106-
m.SetHasher(func(k string) uintptr {
107-
return uintptr(xxh3.HashString(k))
108-
})
109-
return m
101+
func newMap[V any]() (m *Map[string, *value[V]]) {
102+
return new(Map[string, *value[V]])
110103
}
111104

112-
func getShardID(key string) uint64 {
105+
func getShardID(key string) (id uint64) {
113106
if len(key) > 128 {
114107
return xxh3.HashString(key[:128]) & mask
115108
}
116109
return xxh3.HashString(key) & mask
117110
}
118111

119112
// isValid checks expiration of value
120-
func (v *value[V]) isValid() bool {
113+
func (v *value[V]) isValid() (valid bool) {
121114
return v.expire <= 0 || fastime.UnixNanoNow() <= v.expire
122115
}
123116

@@ -170,8 +163,14 @@ func (g *gache[V]) StartExpired(ctx context.Context, dur time.Duration) Gache[V]
170163
// ToMap returns All Cache Key-Value sync.Map
171164
func (g *gache[V]) ToMap(ctx context.Context) *sync.Map {
172165
m := new(sync.Map)
173-
g.Range(ctx, func(key string, val V, exp int64) bool {
174-
go m.Store(key, val)
166+
var wg sync.WaitGroup
167+
defer wg.Wait()
168+
g.Range(ctx, func(key string, val V, exp int64) (ok bool) {
169+
wg.Add(1)
170+
go func() {
171+
m.Store(key, val)
172+
wg.Done()
173+
}()
175174
return true
176175
})
177176

@@ -182,7 +181,7 @@ func (g *gache[V]) ToMap(ctx context.Context) *sync.Map {
182181
func (g *gache[V]) ToRawMap(ctx context.Context) map[string]V {
183182
m := make(map[string]V, g.Len())
184183
mu := new(sync.Mutex)
185-
g.Range(ctx, func(key string, val V, exp int64) bool {
184+
g.Range(ctx, func(key string, val V, exp int64) (ok bool) {
186185
mu.Lock()
187186
m[key] = val
188187
mu.Unlock()
@@ -192,30 +191,29 @@ func (g *gache[V]) ToRawMap(ctx context.Context) map[string]V {
192191
}
193192

194193
// get returns value & exists from key
195-
func (g *gache[V]) get(key string) (V, int64, bool) {
196-
var val V
197-
v, ok := g.shards[getShardID(key)].Get(key)
194+
func (g *gache[V]) get(key string) (v V, expire int64, ok bool) {
195+
var val *value[V]
196+
val, ok = g.shards[getShardID(key)].Load(key)
198197
if !ok {
199-
return val, 0, false
198+
return v, 0, false
200199
}
201200

202-
if v.isValid() {
203-
val = v.val
204-
return val, v.expire, true
201+
if val.isValid() {
202+
return val.val, val.expire, true
205203
}
206204

207205
g.expiration(key)
208-
return val, v.expire, false
206+
return v, val.expire, false
209207
}
210208

211209
// Get returns value & exists from key
212-
func (g *gache[V]) Get(key string) (V, bool) {
213-
v, _, ok := g.get(key)
210+
func (g *gache[V]) Get(key string) (v V, ok bool) {
211+
v, _, ok = g.get(key)
214212
return v, ok
215213
}
216214

217215
// GetWithExpire returns value & expire & exists from key
218-
func (g *gache[V]) GetWithExpire(key string) (V, int64, bool) {
216+
func (g *gache[V]) GetWithExpire(key string) (v V, expire int64, ok bool) {
219217
return g.get(key)
220218
}
221219

@@ -224,11 +222,13 @@ func (g *gache[V]) set(key string, val V, expire int64) {
224222
if expire > 0 {
225223
expire = fastime.UnixNanoNow() + expire
226224
}
227-
atomic.AddUint64(&g.l, 1)
228-
g.shards[getShardID(key)].Set(key, value[V]{
225+
_, loaded := g.shards[getShardID(key)].Swap(key, &value[V]{
229226
expire: expire,
230227
val: val,
231228
})
229+
if !loaded {
230+
atomic.AddUint64(&g.l, 1)
231+
}
232232
}
233233

234234
// SetWithExpire sets key-value & expiration to Gache
@@ -243,41 +243,39 @@ func (g *gache[V]) Set(key string, val V) {
243243

244244
// Delete deletes value from Gache using key
245245
func (g *gache[V]) Delete(key string) (loaded bool) {
246-
atomic.AddUint64(&g.l, ^uint64(0))
247-
return g.shards[getShardID(key)].Del(key)
246+
_, loaded = g.shards[getShardID(key)].LoadAndDelete(key)
247+
if loaded {
248+
atomic.AddUint64(&g.l, ^uint64(0))
249+
}
250+
return
248251
}
249252

250253
func (g *gache[V]) expiration(key string) {
251-
g.expGroup.Do(key, func() (interface{}, error) {
252-
g.Delete(key)
253-
if g.expFuncEnabled {
254-
g.expChan <- key
255-
}
256-
return nil, nil
257-
})
254+
g.Delete(key)
255+
if g.expFuncEnabled {
256+
g.expChan <- key
257+
}
258258
}
259259

260260
// DeleteExpired deletes expired value from Gache it can be cancel using context
261-
func (g *gache[V]) DeleteExpired(ctx context.Context) uint64 {
262-
wg := new(sync.WaitGroup)
263-
var rows uint64
261+
func (g *gache[V]) DeleteExpired(ctx context.Context) (rows uint64) {
262+
var wg sync.WaitGroup
264263
for i := range g.shards {
265264
wg.Add(1)
266265
go func(c context.Context, idx int) {
267-
g.shards[idx].Range(func(k string, v value[V]) bool {
268-
select {
269-
case <-c.Done():
270-
return false
271-
default:
266+
defer wg.Done()
267+
select {
268+
case <-c.Done():
269+
return
270+
default:
271+
g.shards[idx].Range(func(k string, v *value[V]) (ok bool) {
272272
if !v.isValid() {
273273
g.expiration(k)
274274
atomic.AddUint64(&rows, 1)
275-
runtime.Gosched()
276275
}
277276
return true
278-
}
279-
})
280-
wg.Done()
277+
})
278+
}
281279
}(ctx, i)
282280
}
283281
wg.Wait()
@@ -290,20 +288,19 @@ func (g *gache[V]) Range(ctx context.Context, f func(string, V, int64) bool) Gac
290288
for i := range g.shards {
291289
wg.Add(1)
292290
go func(c context.Context, idx int) {
293-
g.shards[idx].Range(func(k string, v value[V]) bool {
294-
select {
295-
case <-c.Done():
296-
return false
297-
default:
291+
defer wg.Done()
292+
select {
293+
case <-c.Done():
294+
return
295+
default:
296+
g.shards[idx].Range(func(k string, v *value[V]) (ok bool) {
298297
if v.isValid() {
299298
return f(k, v.val, v.expire)
300299
}
301-
runtime.Gosched()
302300
g.expiration(k)
303301
return true
304-
}
305-
})
306-
wg.Done()
302+
})
303+
}
307304
}(ctx, i)
308305
}
309306
wg.Wait()
@@ -318,16 +315,7 @@ func (g *gache[V]) Len() int {
318315

319316
// Write writes all cached data to writer
320317
func (g *gache[V]) Write(ctx context.Context, w io.Writer) error {
321-
mu := new(sync.Mutex)
322-
m := make(map[string]V, g.Len())
323-
324-
g.Range(ctx, func(key string, val V, exp int64) bool {
325-
gob.Register(val)
326-
mu.Lock()
327-
m[key] = val
328-
mu.Unlock()
329-
return true
330-
})
318+
m := g.ToRawMap(ctx)
331319
gob.Register(map[string]V{})
332320
return gob.NewEncoder(w).Encode(&m)
333321
}

go.mod

-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ module github.com/kpango/gache/v2
33
go 1.20
44

55
require (
6-
github.com/cornelk/hashmap v1.0.8
76
github.com/kpango/fastime v1.1.9
87
github.com/kpango/glg v1.6.15
98
github.com/zeebo/xxh3 v1.0.2
10-
golang.org/x/sync v0.1.0
119
)
1210

1311
require (

go.sum

-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
github.com/cornelk/hashmap v1.0.8 h1:nv0AWgw02n+iDcawr5It4CjQIAcdMMKRrs10HOJYlrc=
2-
github.com/cornelk/hashmap v1.0.8/go.mod h1:RfZb7JO3RviW/rT6emczVuC/oxpdz4UsSB2LJSclR1k=
31
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
42
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
53
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
@@ -15,6 +13,4 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
1513
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
1614
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
1715
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
18-
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
19-
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
2016
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=

0 commit comments

Comments
 (0)