| /* | |
| * | |
| * Copyright 2019 gRPC authors. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package primitives_test | |
| import ( | |
| "sync" | |
| "sync/atomic" | |
| "testing" | |
| ) | |
| type incrementUint64Map interface { | |
| increment(string) | |
| result(string) uint64 | |
| } | |
| type mapWithLock struct { | |
| mu sync.Mutex | |
| m map[string]uint64 | |
| } | |
| func newMapWithLock() incrementUint64Map { | |
| return &mapWithLock{ | |
| m: make(map[string]uint64), | |
| } | |
| } | |
| func (mwl *mapWithLock) increment(c string) { | |
| mwl.mu.Lock() | |
| mwl.m[c]++ | |
| mwl.mu.Unlock() | |
| } | |
| func (mwl *mapWithLock) result(c string) uint64 { | |
| return mwl.m[c] | |
| } | |
| type mapWithAtomicFastpath struct { | |
| mu sync.RWMutex | |
| m map[string]*uint64 | |
| } | |
| func newMapWithAtomicFastpath() incrementUint64Map { | |
| return &mapWithAtomicFastpath{ | |
| m: make(map[string]*uint64), | |
| } | |
| } | |
| func (mwaf *mapWithAtomicFastpath) increment(c string) { | |
| mwaf.mu.RLock() | |
| if p, ok := mwaf.m[c]; ok { | |
| atomic.AddUint64(p, 1) | |
| mwaf.mu.RUnlock() | |
| return | |
| } | |
| mwaf.mu.RUnlock() | |
| mwaf.mu.Lock() | |
| if p, ok := mwaf.m[c]; ok { | |
| atomic.AddUint64(p, 1) | |
| mwaf.mu.Unlock() | |
| return | |
| } | |
| var temp uint64 = 1 | |
| mwaf.m[c] = &temp | |
| mwaf.mu.Unlock() | |
| } | |
| func (mwaf *mapWithAtomicFastpath) result(c string) uint64 { | |
| return atomic.LoadUint64(mwaf.m[c]) | |
| } | |
| type mapWithSyncMap struct { | |
| m sync.Map | |
| } | |
| func newMapWithSyncMap() incrementUint64Map { | |
| return &mapWithSyncMap{} | |
| } | |
| func (mwsm *mapWithSyncMap) increment(c string) { | |
| p, ok := mwsm.m.Load(c) | |
| if !ok { | |
| tp := new(uint64) | |
| p, _ = mwsm.m.LoadOrStore(c, tp) | |
| } | |
| atomic.AddUint64(p.(*uint64), 1) | |
| } | |
| func (mwsm *mapWithSyncMap) result(c string) uint64 { | |
| p, _ := mwsm.m.Load(c) | |
| return atomic.LoadUint64(p.(*uint64)) | |
| } | |
| func benchmarkIncrementUint64Map(b *testing.B, f func() incrementUint64Map) { | |
| const cat = "cat" | |
| benches := []struct { | |
| name string | |
| goroutineCount int | |
| }{ | |
| { | |
| name: " 1", | |
| goroutineCount: 1, | |
| }, | |
| { | |
| name: " 10", | |
| goroutineCount: 10, | |
| }, | |
| { | |
| name: " 100", | |
| goroutineCount: 100, | |
| }, | |
| { | |
| name: "1000", | |
| goroutineCount: 1000, | |
| }, | |
| } | |
| for _, bb := range benches { | |
| b.Run(bb.name, func(b *testing.B) { | |
| m := f() | |
| var wg sync.WaitGroup | |
| wg.Add(bb.goroutineCount) | |
| b.ResetTimer() | |
| for i := 0; i < bb.goroutineCount; i++ { | |
| go func() { | |
| for j := 0; j < b.N; j++ { | |
| m.increment(cat) | |
| } | |
| wg.Done() | |
| }() | |
| } | |
| wg.Wait() | |
| b.StopTimer() | |
| if m.result(cat) != uint64(bb.goroutineCount*b.N) { | |
| b.Fatalf("result is %d, want %d", m.result(cat), b.N) | |
| } | |
| }) | |
| } | |
| } | |
| func BenchmarkMapWithSyncMutexContention(b *testing.B) { | |
| benchmarkIncrementUint64Map(b, newMapWithLock) | |
| } | |
| func BenchmarkMapWithAtomicFastpath(b *testing.B) { | |
| benchmarkIncrementUint64Map(b, newMapWithAtomicFastpath) | |
| } | |
| func BenchmarkMapWithSyncMap(b *testing.B) { | |
| benchmarkIncrementUint64Map(b, newMapWithSyncMap) | |
| } |