From f9d42b236c9b28f9927081e12983dfcb281eadc1 Mon Sep 17 00:00:00 2001 From: lintong Date: Sat, 23 Jan 2021 20:08:54 +0800 Subject: [PATCH 1/2] feat: optimize MSet method, add MRemove method --- concurrent_map.go | 40 +++++++++++++++++++++++++++++++++++++--- concurrent_map_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/concurrent_map.go b/concurrent_map.go index 191452a..0d0451b 100644 --- a/concurrent_map.go +++ b/concurrent_map.go @@ -28,14 +28,24 @@ func New() ConcurrentMap { // GetShard returns shard under given key func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared { - return m[uint(fnv32(key))%uint(SHARD_COUNT)] + return m[hash(key)] } func (m ConcurrentMap) MSet(data map[string]interface{}) { + bucket := make(map[uint]map[string]interface{}, 0) for key, value := range data { - shard := m.GetShard(key) + index := hash(key) + if _, ok := bucket[index]; !ok { + bucket[index] = make(map[string]interface{}, 0) + } + bucket[index][key] = value + } + for index, segment := range bucket { + shard := m[index] shard.Lock() - shard.items[key] = value + for key, value := range segment { + shard.items[key] = value + } shard.Unlock() } } @@ -122,6 +132,26 @@ func (m ConcurrentMap) Remove(key string) { shard.Unlock() } +func (m ConcurrentMap) MRemove(keys []string) { + bucket := make(map[uint][]string, 0) + for _, key := range keys { + index := hash(key) + if _, ok := bucket[index]; !ok { + bucket[index] = make([]string, 0) + } + bucket[index] = append(bucket[index], key) + } + + for index, segment := range bucket { + shard := m[index] + shard.Lock() + for _, key := range segment { + delete(shard.items, key) + } + shard.Unlock() + } +} + // RemoveCb is a callback executed in a map.RemoveCb() call, while Lock is held // If returns true, the element will be removed from the map type RemoveCb func(key string, v interface{}, exists bool) bool @@ -319,6 +349,10 @@ func fnv32(key string) uint32 { return hash } +func hash(key string) uint { + return uint(fnv32(key))%uint(SHARD_COUNT) +} + // Concurrent map uses Interface{} as its value, therefor JSON Unmarshal // will probably won't know which to type to unmarshal into, in such case // we'll end up with a value of type map[string]interface{}, In most cases this isn't diff --git a/concurrent_map_test.go b/concurrent_map_test.go index 18fbb09..36c23ec 100644 --- a/concurrent_map_test.go +++ b/concurrent_map_test.go @@ -122,6 +122,35 @@ func TestRemove(t *testing.T) { m.Remove("noone") } +func TestMRemove(t *testing.T) { + m := New() + monkey := Animal{"monkey"} + m.Set("monkey", monkey) + elephant := Animal{"elephant"} + m.Set("elephant", elephant) + + keys := []string{"monkey", "elephant"} + m.MRemove(keys) + + if m.Count() != 0 { + t.Error("Expecting count to be zero once multi items were removed.") + } + + temp, ok := m.Get("monkey") + + if ok != false { + t.Error("Expecting ok to be false for missing items after multi items were removed..") + } + + if temp != nil { + t.Error("Expecting item to be nil after multi items were removed.") + } + + // Remove a none existing element. + m.Remove("noone") +} + + func TestRemoveCb(t *testing.T) { m := New() From ce56b7f83743b959cf4f0d7d01051707d15f942a Mon Sep 17 00:00:00 2001 From: lintong Date: Sun, 24 Jan 2021 16:55:37 +0800 Subject: [PATCH 2/2] fix: fix build failure --- concurrent_map.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/concurrent_map.go b/concurrent_map.go index 0d0451b..82ca5ec 100644 --- a/concurrent_map.go +++ b/concurrent_map.go @@ -32,11 +32,11 @@ func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared { } func (m ConcurrentMap) MSet(data map[string]interface{}) { - bucket := make(map[uint]map[string]interface{}, 0) + bucket := make(map[uint]map[string]interface{}) for key, value := range data { index := hash(key) if _, ok := bucket[index]; !ok { - bucket[index] = make(map[string]interface{}, 0) + bucket[index] = make(map[string]interface{}) } bucket[index][key] = value } @@ -133,7 +133,7 @@ func (m ConcurrentMap) Remove(key string) { } func (m ConcurrentMap) MRemove(keys []string) { - bucket := make(map[uint][]string, 0) + bucket := make(map[uint][]string) for _, key := range keys { index := hash(key) if _, ok := bucket[index]; !ok {