...
1
17
18 package primitives_test
19
20 import (
21 "sync"
22 "sync/atomic"
23 "testing"
24 )
25
26 type incrementUint64Map interface {
27 increment(string)
28 result(string) uint64
29 }
30
31 type mapWithLock struct {
32 mu sync.Mutex
33 m map[string]uint64
34 }
35
36 func newMapWithLock() incrementUint64Map {
37 return &mapWithLock{
38 m: make(map[string]uint64),
39 }
40 }
41
42 func (mwl *mapWithLock) increment(c string) {
43 mwl.mu.Lock()
44 mwl.m[c]++
45 mwl.mu.Unlock()
46 }
47
48 func (mwl *mapWithLock) result(c string) uint64 {
49 return mwl.m[c]
50 }
51
52 type mapWithAtomicFastpath struct {
53 mu sync.RWMutex
54 m map[string]*uint64
55 }
56
57 func newMapWithAtomicFastpath() incrementUint64Map {
58 return &mapWithAtomicFastpath{
59 m: make(map[string]*uint64),
60 }
61 }
62
63 func (mwaf *mapWithAtomicFastpath) increment(c string) {
64 mwaf.mu.RLock()
65 if p, ok := mwaf.m[c]; ok {
66 atomic.AddUint64(p, 1)
67 mwaf.mu.RUnlock()
68 return
69 }
70 mwaf.mu.RUnlock()
71
72 mwaf.mu.Lock()
73 if p, ok := mwaf.m[c]; ok {
74 atomic.AddUint64(p, 1)
75 mwaf.mu.Unlock()
76 return
77 }
78 var temp uint64 = 1
79 mwaf.m[c] = &temp
80 mwaf.mu.Unlock()
81 }
82
83 func (mwaf *mapWithAtomicFastpath) result(c string) uint64 {
84 return atomic.LoadUint64(mwaf.m[c])
85 }
86
87 type mapWithSyncMap struct {
88 m sync.Map
89 }
90
91 func newMapWithSyncMap() incrementUint64Map {
92 return &mapWithSyncMap{}
93 }
94
95 func (mwsm *mapWithSyncMap) increment(c string) {
96 p, ok := mwsm.m.Load(c)
97 if !ok {
98 tp := new(uint64)
99 p, _ = mwsm.m.LoadOrStore(c, tp)
100 }
101 atomic.AddUint64(p.(*uint64), 1)
102 }
103
104 func (mwsm *mapWithSyncMap) result(c string) uint64 {
105 p, _ := mwsm.m.Load(c)
106 return atomic.LoadUint64(p.(*uint64))
107 }
108
109 func benchmarkIncrementUint64Map(b *testing.B, f func() incrementUint64Map) {
110 const cat = "cat"
111 benches := []struct {
112 name string
113 goroutineCount int
114 }{
115 {
116 name: " 1",
117 goroutineCount: 1,
118 },
119 {
120 name: " 10",
121 goroutineCount: 10,
122 },
123 {
124 name: " 100",
125 goroutineCount: 100,
126 },
127 {
128 name: "1000",
129 goroutineCount: 1000,
130 },
131 }
132 for _, bb := range benches {
133 b.Run(bb.name, func(b *testing.B) {
134 m := f()
135 var wg sync.WaitGroup
136 wg.Add(bb.goroutineCount)
137 b.ResetTimer()
138 for i := 0; i < bb.goroutineCount; i++ {
139 go func() {
140 for j := 0; j < b.N; j++ {
141 m.increment(cat)
142 }
143 wg.Done()
144 }()
145 }
146 wg.Wait()
147 b.StopTimer()
148 if m.result(cat) != uint64(bb.goroutineCount*b.N) {
149 b.Fatalf("result is %d, want %d", m.result(cat), b.N)
150 }
151 })
152 }
153 }
154
155 func BenchmarkMapWithSyncMutexContetion(b *testing.B) {
156 benchmarkIncrementUint64Map(b, newMapWithLock)
157 }
158
159 func BenchmarkMapWithAtomicFastpath(b *testing.B) {
160 benchmarkIncrementUint64Map(b, newMapWithAtomicFastpath)
161 }
162
163 func BenchmarkMapWithSyncMap(b *testing.B) {
164 benchmarkIncrementUint64Map(b, newMapWithSyncMap)
165 }
166
View as plain text