概要
golang 1.9からsync.Mapが導入されました。
このスライドによると,導入のモチベーションとしてはCPUのCore数が上昇すると,同期にコストがかかる(cache-contentionの問題と呼ばれている)ので,
内部ではこれをなんとかするために色々やってるっぽいです。(コード読んでもよくわからなかった)
使ってみる
とりあえず今まででは、sync.Map
っぽいものを実装しようとすると
type Target struct {
foo int
bar string
}
type OldSyncMap struct {
mu sync.Mutex
m map[int]Target
}
func NewOldSyncMap() OldSyncMap {
return OldSyncMap{m: map[int]Target{}}
}
func (s *OldSyncMap) Store(key int, value Target) {
s.mu.Lock()
defer s.mu.Unlock()
s.m[key] = value
}
func (s *OldSyncMap) Load(key int) (Target, error) {
s.mu.Lock()
defer s.mu.Unlock()
t, ok := s.m[key]
if !ok {
return Target{}, errors.New("not found")
}
return t, nil
}
func main() {
s := NewOldSyncMap()
s.Store(1, Target{
foo: 1,
bar: "bar",
})
t, err := s.Load(1)
fmt.Println(t, err) // => {1 bar} <nil>
}
みたいな感じだと思うが、sync.Map
使うと
type Target struct {
foo int
bar string
}
type SyncMap struct {
s sync.Map
}
func NewSyncMap() SyncMap {
return SyncMap{}
}
func (s *SyncMap) Store(key int, value Target) {
s.s.Store(key, value)
}
func (s *SyncMap) Load(key int) (Target, error) {
v, ok := s.s.Load(key)
if !ok {
return Target{}, errors.New("not found")
}
t, ok := v.(Target)
if !ok {
return Target{}, errors.New("stored type is invalid")
}
return t, nil
}
func main() {
s := NewSyncMap()
s.Store(1, Target{
foo: 1,
bar: "bar",
})
t, err := s.Load(1)
fmt.Println(t, err) // => {1 bar} <nil>
}
こんな感じで書ける。
実際に測定すると
雑に書いたベンチマークで測定してみる
Intel Core i5-6200U(Core 2, Thread 4), Memory 8GB, Ubuntu 16.04で
func BenchmarkOldSyncMap(b *testing.B) {
for j := 0; j < b.N; j++ {
m := NewOldSyncMap()
wg := sync.WaitGroup{}
for i := 0; i < 1000000; i++ {
wg.Add(1)
go func(i int) {
m.Store(i, Target{
foo: i,
})
_, err := m.Load(i)
if err != nil {
b.Errorf("in %d, %s", i, err)
}
wg.Done()
}(i)
}
wg.Wait()
}
}
func BenchmarkNewSyncMap(b *testing.B) {
for j := 0; j < b.N; j++ {
m := NewSyncMap()
wg := sync.WaitGroup{}
for i := 0; i < 1000000; i++ {
wg.Add(1)
go func(i int) {
m.Store(i, Target{
foo: i,
})
_, err := m.Load(i)
if err != nil {
b.Errorf("in %d, %s", i, err)
}
wg.Done()
}(i)
}
wg.Wait()
}
}
goos: linux
goarch: amd64
pkg: test
BenchmarkOldSyncMap-4 1 5575385714 ns/op
BenchmarkNewSyncMap-4 1 3358420579 ns/op
PASS
ok test 9.298s
若干早い。