Skip to content

Commit

Permalink
use a sync.Pool to speed up forgetful sync map
Browse files Browse the repository at this point in the history
  • Loading branch information
bluntelk committed Nov 11, 2023
1 parent 95c3aa7 commit b545721
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 8 deletions.
49 changes: 41 additions & 8 deletions lib/dedupe/forgetfulmap/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type (
forgettable ForgettableFunc

itemCounter prometheus.Gauge

useSyncPool bool
}

// a generic wrapper for things that can be lost
Expand All @@ -31,11 +33,27 @@ type (
Option func(*ForgetfulSyncMap)
)

var (
marbleBag *sync.Pool
)

func init() {
marbleBag = &sync.Pool{
New: func() any {
return &marble{
added: time.Time{},
value: nil,
}
},
}
}

func NewForgetfulSyncMap(opts ...Option) *ForgetfulSyncMap {
f := &ForgetfulSyncMap{
lookup: &sync.Map{},
sweepInterval: 10 * time.Second,
oldAfter: 60 * time.Second,
useSyncPool: true,
}
for _, opt := range opts {
opt(f)
Expand All @@ -51,6 +69,12 @@ func NewForgetfulSyncMap(opts ...Option) *ForgetfulSyncMap {
return f
}

func UseMemSyncPool(use bool) Option {
return func(syncMap *ForgetfulSyncMap) {
syncMap.useSyncPool = use
}
}

func WithPrometheusCounters(numItems prometheus.Gauge) Option {
return func(syncMap *ForgetfulSyncMap) {
syncMap.itemCounter = numItems
Expand Down Expand Up @@ -194,15 +218,24 @@ func (f *ForgetfulSyncMap) Load(key any) (any, bool) {

// Store remembers an item
func (f *ForgetfulSyncMap) Store(key, value interface{}) {
f.lookup.Store(key, &marble{
added: time.Now(),
value: value,
})
var m *marble
if f.useSyncPool {
m = marbleBag.Get().(*marble)
} else {
m = &marble{}
}
m.added = time.Now()
m.value = value
f.lookup.Store(key, m)
}

// Delete Removes an item from the list
func (f *ForgetfulSyncMap) Delete(key interface{}) {
f.lookup.Delete(key)
m, ok := f.lookup.Load(key)
if ok {
marbleBag.Put(m)
f.lookup.Delete(key)
}
}

// Len returns a count of the number of items in the list
Expand All @@ -218,11 +251,11 @@ func (f *ForgetfulSyncMap) Len() (entries int32) {
// Range Iterates over the underlying sync.Map and calls the user function once per item
func (f *ForgetfulSyncMap) Range(rangeFunc func(key, value interface{}) bool) {
f.lookup.Range(func(key, value interface{}) bool {
if m, ok := value.(*marble); ok {
m, ok := value.(*marble)
if ok {
return rangeFunc(key, m.value)
} else {
return rangeFunc(key, value)
}
return rangeFunc(key, value)
})
}

Expand Down
42 changes: 42 additions & 0 deletions lib/dedupe/forgetfulmap/map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,45 @@ func TestForgetfulSyncMap_SweepWithCustomExpiryFunc(t *testing.T) {
t.Error("Failed load our item1")
}
}

func BenchmarkForgetfulSyncMap(b *testing.B) {
m := NewForgetfulSyncMap(
UseMemSyncPool(false),
WithSweepInterval(time.Second*300),
WithOldAgeAfterSeconds(300),
WithForgettableAction(func(key, value any, added time.Time) bool {
return false
}),
)

var i, j int64
for n := 0; n < b.N; n++ {
for j = 0; j < 100; j++ {
m.AddKey((i * 100) + j)
}
for j = 0; j < 100; j++ {
m.Delete((i * 100) + j)
}
}
}

func BenchmarkForgetfulSyncMapPool(b *testing.B) {
m := NewForgetfulSyncMap(
UseMemSyncPool(true),
WithSweepInterval(time.Second*300),
WithOldAgeAfterSeconds(300),
WithForgettableAction(func(key, value any, added time.Time) bool {
return false
}),
)

var i, j int64
for n := 0; n < b.N; n++ {
for j = 0; j < 100; j++ {
m.AddKey((i * 100) + j)
}
for j = 0; j < 100; j++ {
m.Delete((i * 100) + j)
}
}
}

0 comments on commit b545721

Please sign in to comment.