Source file
src/sync/map_bench_test.go
1
2
3
4
5 package sync_test
6
7 import (
8 "fmt"
9 isync "internal/sync"
10 "reflect"
11 "sync"
12 "sync/atomic"
13 "testing"
14 )
15
16 type bench struct {
17 setup func(*testing.B, mapInterface)
18 perG func(b *testing.B, pb *testing.PB, i int, m mapInterface)
19 }
20
21 func benchMap(b *testing.B, bench bench) {
22 for _, m := range [...]mapInterface{&DeepCopyMap{}, &RWMutexMap{}, &isync.HashTrieMap[any, any]{}, &sync.Map{}} {
23 b.Run(fmt.Sprintf("%T", m), func(b *testing.B) {
24 m = reflect.New(reflect.TypeOf(m).Elem()).Interface().(mapInterface)
25 if bench.setup != nil {
26 bench.setup(b, m)
27 }
28
29 b.ReportAllocs()
30 b.ResetTimer()
31
32 var i int64
33 b.RunParallel(func(pb *testing.PB) {
34 id := int(atomic.AddInt64(&i, 1) - 1)
35 bench.perG(b, pb, id*b.N, m)
36 })
37 })
38 }
39 }
40
41 func BenchmarkMapLoadMostlyHits(b *testing.B) {
42 const hits, misses = 1023, 1
43
44 benchMap(b, bench{
45 setup: func(_ *testing.B, m mapInterface) {
46 for i := 0; i < hits; i++ {
47 m.LoadOrStore(i, i)
48 }
49
50 for i := 0; i < hits*2; i++ {
51 m.Load(i % hits)
52 }
53 },
54
55 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
56 for ; pb.Next(); i++ {
57 m.Load(i % (hits + misses))
58 }
59 },
60 })
61 }
62
63 func BenchmarkMapLoadMostlyMisses(b *testing.B) {
64 const hits, misses = 1, 1023
65
66 benchMap(b, bench{
67 setup: func(_ *testing.B, m mapInterface) {
68 for i := 0; i < hits; i++ {
69 m.LoadOrStore(i, i)
70 }
71
72 for i := 0; i < hits*2; i++ {
73 m.Load(i % hits)
74 }
75 },
76
77 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
78 for ; pb.Next(); i++ {
79 m.Load(i % (hits + misses))
80 }
81 },
82 })
83 }
84
85 func BenchmarkMapLoadOrStoreBalanced(b *testing.B) {
86 const hits, misses = 128, 128
87
88 benchMap(b, bench{
89 setup: func(b *testing.B, m mapInterface) {
90 if _, ok := m.(*DeepCopyMap); ok {
91 b.Skip("DeepCopyMap has quadratic running time.")
92 }
93 for i := 0; i < hits; i++ {
94 m.LoadOrStore(i, i)
95 }
96
97 for i := 0; i < hits*2; i++ {
98 m.Load(i % hits)
99 }
100 },
101
102 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
103 for ; pb.Next(); i++ {
104 j := i % (hits + misses)
105 if j < hits {
106 if _, ok := m.LoadOrStore(j, i); !ok {
107 b.Fatalf("unexpected miss for %v", j)
108 }
109 } else {
110 if v, loaded := m.LoadOrStore(i, i); loaded {
111 b.Fatalf("failed to store %v: existing value %v", i, v)
112 }
113 }
114 }
115 },
116 })
117 }
118
119 func BenchmarkMapLoadOrStoreUnique(b *testing.B) {
120 benchMap(b, bench{
121 setup: func(b *testing.B, m mapInterface) {
122 if _, ok := m.(*DeepCopyMap); ok {
123 b.Skip("DeepCopyMap has quadratic running time.")
124 }
125 },
126
127 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
128 for ; pb.Next(); i++ {
129 m.LoadOrStore(i, i)
130 }
131 },
132 })
133 }
134
135 func BenchmarkMapLoadOrStoreCollision(b *testing.B) {
136 benchMap(b, bench{
137 setup: func(_ *testing.B, m mapInterface) {
138 m.LoadOrStore(0, 0)
139 },
140
141 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
142 for ; pb.Next(); i++ {
143 m.LoadOrStore(0, 0)
144 }
145 },
146 })
147 }
148
149 func BenchmarkMapLoadAndDeleteBalanced(b *testing.B) {
150 const hits, misses = 128, 128
151
152 benchMap(b, bench{
153 setup: func(b *testing.B, m mapInterface) {
154 if _, ok := m.(*DeepCopyMap); ok {
155 b.Skip("DeepCopyMap has quadratic running time.")
156 }
157 for i := 0; i < hits; i++ {
158 m.LoadOrStore(i, i)
159 }
160
161 for i := 0; i < hits*2; i++ {
162 m.Load(i % hits)
163 }
164 },
165
166 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
167 for ; pb.Next(); i++ {
168 j := i % (hits + misses)
169 if j < hits {
170 m.LoadAndDelete(j)
171 } else {
172 m.LoadAndDelete(i)
173 }
174 }
175 },
176 })
177 }
178
179 func BenchmarkMapLoadAndDeleteUnique(b *testing.B) {
180 benchMap(b, bench{
181 setup: func(b *testing.B, m mapInterface) {
182 if _, ok := m.(*DeepCopyMap); ok {
183 b.Skip("DeepCopyMap has quadratic running time.")
184 }
185 },
186
187 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
188 for ; pb.Next(); i++ {
189 m.LoadAndDelete(i)
190 }
191 },
192 })
193 }
194
195 func BenchmarkMapLoadAndDeleteCollision(b *testing.B) {
196 benchMap(b, bench{
197 setup: func(_ *testing.B, m mapInterface) {
198 m.LoadOrStore(0, 0)
199 },
200
201 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
202 for ; pb.Next(); i++ {
203 if _, loaded := m.LoadAndDelete(0); loaded {
204 m.Store(0, 0)
205 }
206 }
207 },
208 })
209 }
210
211 func BenchmarkMapRange(b *testing.B) {
212 const mapSize = 1 << 10
213
214 benchMap(b, bench{
215 setup: func(_ *testing.B, m mapInterface) {
216 for i := 0; i < mapSize; i++ {
217 m.Store(i, i)
218 }
219 },
220
221 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
222 for ; pb.Next(); i++ {
223 m.Range(func(_, _ any) bool { return true })
224 }
225 },
226 })
227 }
228
229
230
231
232
233
234 func BenchmarkMapAdversarialAlloc(b *testing.B) {
235 benchMap(b, bench{
236 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
237 var stores, loadsSinceStore int64
238 for ; pb.Next(); i++ {
239 m.Load(i)
240 if loadsSinceStore++; loadsSinceStore > stores {
241 m.LoadOrStore(i, stores)
242 loadsSinceStore = 0
243 stores++
244 }
245 }
246 },
247 })
248 }
249
250
251
252
253
254
255 func BenchmarkMapAdversarialDelete(b *testing.B) {
256 const mapSize = 1 << 10
257
258 benchMap(b, bench{
259 setup: func(_ *testing.B, m mapInterface) {
260 for i := 0; i < mapSize; i++ {
261 m.Store(i, i)
262 }
263 },
264
265 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
266 for ; pb.Next(); i++ {
267 m.Load(i)
268
269 if i%mapSize == 0 {
270 m.Range(func(k, _ any) bool {
271 m.Delete(k)
272 return false
273 })
274 m.Store(i, i)
275 }
276 }
277 },
278 })
279 }
280
281 func BenchmarkMapDeleteCollision(b *testing.B) {
282 benchMap(b, bench{
283 setup: func(_ *testing.B, m mapInterface) {
284 m.LoadOrStore(0, 0)
285 },
286
287 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
288 for ; pb.Next(); i++ {
289 m.Delete(0)
290 }
291 },
292 })
293 }
294
295 func BenchmarkMapSwapCollision(b *testing.B) {
296 benchMap(b, bench{
297 setup: func(_ *testing.B, m mapInterface) {
298 m.LoadOrStore(0, 0)
299 },
300
301 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
302 for ; pb.Next(); i++ {
303 m.Swap(0, 0)
304 }
305 },
306 })
307 }
308
309 func BenchmarkMapSwapMostlyHits(b *testing.B) {
310 const hits, misses = 1023, 1
311
312 benchMap(b, bench{
313 setup: func(_ *testing.B, m mapInterface) {
314 for i := 0; i < hits; i++ {
315 m.LoadOrStore(i, i)
316 }
317
318 for i := 0; i < hits*2; i++ {
319 m.Load(i % hits)
320 }
321 },
322
323 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
324 for ; pb.Next(); i++ {
325 if i%(hits+misses) < hits {
326 v := i % (hits + misses)
327 m.Swap(v, v)
328 } else {
329 m.Swap(i, i)
330 m.Delete(i)
331 }
332 }
333 },
334 })
335 }
336
337 func BenchmarkMapSwapMostlyMisses(b *testing.B) {
338 const hits, misses = 1, 1023
339
340 benchMap(b, bench{
341 setup: func(_ *testing.B, m mapInterface) {
342 for i := 0; i < hits; i++ {
343 m.LoadOrStore(i, i)
344 }
345
346 for i := 0; i < hits*2; i++ {
347 m.Load(i % hits)
348 }
349 },
350
351 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
352 for ; pb.Next(); i++ {
353 if i%(hits+misses) < hits {
354 v := i % (hits + misses)
355 m.Swap(v, v)
356 } else {
357 m.Swap(i, i)
358 m.Delete(i)
359 }
360 }
361 },
362 })
363 }
364
365 func BenchmarkMapCompareAndSwapCollision(b *testing.B) {
366 benchMap(b, bench{
367 setup: func(_ *testing.B, m mapInterface) {
368 m.LoadOrStore(0, 0)
369 },
370
371 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
372 for pb.Next() {
373 if m.CompareAndSwap(0, 0, 42) {
374 m.CompareAndSwap(0, 42, 0)
375 }
376 }
377 },
378 })
379 }
380
381 func BenchmarkMapCompareAndSwapNoExistingKey(b *testing.B) {
382 benchMap(b, bench{
383 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
384 for ; pb.Next(); i++ {
385 if m.CompareAndSwap(i, 0, 0) {
386 m.Delete(i)
387 }
388 }
389 },
390 })
391 }
392
393 func BenchmarkMapCompareAndSwapValueNotEqual(b *testing.B) {
394 benchMap(b, bench{
395 setup: func(_ *testing.B, m mapInterface) {
396 m.Store(0, 0)
397 },
398
399 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
400 for ; pb.Next(); i++ {
401 m.CompareAndSwap(0, 1, 2)
402 }
403 },
404 })
405 }
406
407 func BenchmarkMapCompareAndSwapMostlyHits(b *testing.B) {
408 const hits, misses = 1023, 1
409
410 benchMap(b, bench{
411 setup: func(b *testing.B, m mapInterface) {
412 if _, ok := m.(*DeepCopyMap); ok {
413 b.Skip("DeepCopyMap has quadratic running time.")
414 }
415
416 for i := 0; i < hits; i++ {
417 m.LoadOrStore(i, i)
418 }
419
420 for i := 0; i < hits*2; i++ {
421 m.Load(i % hits)
422 }
423 },
424
425 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
426 for ; pb.Next(); i++ {
427 v := i
428 if i%(hits+misses) < hits {
429 v = i % (hits + misses)
430 }
431 m.CompareAndSwap(v, v, v)
432 }
433 },
434 })
435 }
436
437 func BenchmarkMapCompareAndSwapMostlyMisses(b *testing.B) {
438 const hits, misses = 1, 1023
439
440 benchMap(b, bench{
441 setup: func(_ *testing.B, m mapInterface) {
442 for i := 0; i < hits; i++ {
443 m.LoadOrStore(i, i)
444 }
445
446 for i := 0; i < hits*2; i++ {
447 m.Load(i % hits)
448 }
449 },
450
451 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
452 for ; pb.Next(); i++ {
453 v := i
454 if i%(hits+misses) < hits {
455 v = i % (hits + misses)
456 }
457 m.CompareAndSwap(v, v, v)
458 }
459 },
460 })
461 }
462
463 func BenchmarkMapCompareAndDeleteCollision(b *testing.B) {
464 benchMap(b, bench{
465 setup: func(_ *testing.B, m mapInterface) {
466 m.LoadOrStore(0, 0)
467 },
468
469 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
470 for ; pb.Next(); i++ {
471 if m.CompareAndDelete(0, 0) {
472 m.Store(0, 0)
473 }
474 }
475 },
476 })
477 }
478
479 func BenchmarkMapCompareAndDeleteMostlyHits(b *testing.B) {
480 const hits, misses = 1023, 1
481
482 benchMap(b, bench{
483 setup: func(b *testing.B, m mapInterface) {
484 if _, ok := m.(*DeepCopyMap); ok {
485 b.Skip("DeepCopyMap has quadratic running time.")
486 }
487
488 for i := 0; i < hits; i++ {
489 m.LoadOrStore(i, i)
490 }
491
492 for i := 0; i < hits*2; i++ {
493 m.Load(i % hits)
494 }
495 },
496
497 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
498 for ; pb.Next(); i++ {
499 v := i
500 if i%(hits+misses) < hits {
501 v = i % (hits + misses)
502 }
503 if m.CompareAndDelete(v, v) {
504 m.Store(v, v)
505 }
506 }
507 },
508 })
509 }
510
511 func BenchmarkMapCompareAndDeleteMostlyMisses(b *testing.B) {
512 const hits, misses = 1, 1023
513
514 benchMap(b, bench{
515 setup: func(_ *testing.B, m mapInterface) {
516 for i := 0; i < hits; i++ {
517 m.LoadOrStore(i, i)
518 }
519
520 for i := 0; i < hits*2; i++ {
521 m.Load(i % hits)
522 }
523 },
524
525 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
526 for ; pb.Next(); i++ {
527 v := i
528 if i%(hits+misses) < hits {
529 v = i % (hits + misses)
530 }
531 if m.CompareAndDelete(v, v) {
532 m.Store(v, v)
533 }
534 }
535 },
536 })
537 }
538
539 func BenchmarkMapClear(b *testing.B) {
540 benchMap(b, bench{
541 perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
542 for ; pb.Next(); i++ {
543 k, v := i%256, i%256
544 m.Clear()
545 m.Store(k, v)
546 }
547 },
548 })
549 }
550
View as plain text