blob: 3ece8d92034b95c822b5921fc8862f3c4097aef6 [file] [log] [blame]
/*
*
* 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 BenchmarkMapWithSyncMutexContetion(b *testing.B) {
benchmarkIncrementUint64Map(b, newMapWithLock)
}
func BenchmarkMapWithAtomicFastpath(b *testing.B) {
benchmarkIncrementUint64Map(b, newMapWithAtomicFastpath)
}
func BenchmarkMapWithSyncMap(b *testing.B) {
benchmarkIncrementUint64Map(b, newMapWithSyncMap)
}