Golang sync map
What is sync.Map
sync map is concurrent safe map data type.
Use Cases
used to store keys and values in multiple concurrent goroutines.
Example
valueStore is a sync map used to store values in multiple goroutines. after all goroutines finished store data in valueStore, Call Range to get all keys and values to result.
package main
import (
"fmt"
"sync"
)
func geValuesByKey(key string) []int {
switch key {
case "k1":
return []int{1, 2, 3}
case "k2":
return []int{100, 200, 300}
case "k6":
return []int{8, 9, 10}
}
return []int{}
}
func concurrentGetValues(keys []string) map[string][]int {
var valueStore sync.Map
var wg sync.WaitGroup
for _, key := range keys {
wg.Add(1)
go func(k string) {
values := geValuesByKey(k)
if len(values) > 0 {
valueStore.Store(k, values)
}
wg.Done()
}(key)
}
wg.Wait()
result := make(map[string][]int, len(keys))
valueStore.Range(func(key, value interface{}) bool {
result[key.(string)] = value.([]int)
return true
})
return result
}
func main() {
keys := []string{"k1", "k2", "k6", "k3"}
result := concurrentGetValues(keys)
fmt.Printf("concurrentGetValues: %+v\n", result) // concurrentGetValues: map[k1:[1 2 3] k2:[100 200 300] k6:[8 9 10]]
}
The performance of sync map vs locked map
package bench
import "sync"
type LockedStore struct {
sync.RWMutex
m map[string]interface{}
}
func (s *LockedStore) Get(key string) interface{} {
s.RLock()
defer s.RUnlock()
return s.m[key]
}
func (s *LockedStore) Set(key string, value interface{}) {
s.Lock()
s.m[key] = value
s.Unlock()
}
Bench mark
package bench
import (
"strconv"
"sync"
"testing"
)
var (
number = 1000000
lockedStore = initStore(number, false)
syncStore = initSyncMap(number, false)
)
func initStore(n int, noValue bool) *LockedStore {
s := &LockedStore{m: make(map[string]interface{}, n)}
if noValue {
return s
}
var i int
for ; i < n; i++ {
s.Set(strconv.Itoa(i), i)
}
return s
}
func initSyncMap(n int, noValue bool) sync.Map {
var m sync.Map
if noValue {
return m
}
for i := 0; i < n; i++ {
m.Store(strconv.Itoa(i), i)
}
return m
}
func BenchmarkSyncMapGet(b *testing.B) {
n := b.N
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
k := strconv.Itoa(i)
go func(key string) {
syncStore.Load(key)
wg.Done()
}(k)
}
wg.Wait()
}
func BenchmarkSyncMapSet(b *testing.B) {
n := b.N
syncStore = initSyncMap(n, true)
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
k := strconv.Itoa(i)
go func(key string) {
syncStore.Store(key, ":"+key+":")
wg.Done()
}(k)
}
wg.Wait()
}
func BenchmarkLockedStore_Get(b *testing.B) {
n := b.N
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
k := strconv.Itoa(i)
go func(key string) {
lockedStore.Get(key)
wg.Done()
}(k)
}
wg.Wait()
}
func BenchmarkLockedStore_Set(b *testing.B) {
n := b.N
lockedStore = initStore(n, true)
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
k := strconv.Itoa(i)
go func(key string) {
lockedStore.Set(key, ":"+key+":")
wg.Done()
}(k)
}
wg.Wait()
}
/*Bench Result
go test -bench=.
goos: darwin
goarch: amd64
pkg: go-common/bench
BenchmarkSyncMapGet-8 1000000 1051 ns/op
BenchmarkSyncMapSet-8 805191 2834 ns/op
BenchmarkLockedStore_Get-8 2943969 390 ns/op
BenchmarkLockedStore_Set-8 1281457 989 ns/op
*/