166 строки
3.3 KiB
Go
166 строки
3.3 KiB
Go
/*
|
|
*
|
|
* 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)
|
|
}
|