Source file
src/runtime/map_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "fmt"
9 "internal/abi"
10 "internal/goarch"
11 "internal/testenv"
12 "math"
13 "os"
14 "reflect"
15 "runtime"
16 "slices"
17 "strconv"
18 "strings"
19 "sync"
20 "testing"
21 "unsafe"
22 )
23
24 func TestHmapSize(t *testing.T) {
25
26
27
28 var hmapSize = uintptr(8 + 5*goarch.PtrSize)
29 if runtime.RuntimeHmapSize != hmapSize {
30 t.Errorf("sizeof(runtime.hmap{})==%d, want %d", runtime.RuntimeHmapSize, hmapSize)
31 }
32
33 }
34
35
36
37
38
39
40
41 func TestNegativeZero(t *testing.T) {
42 m := make(map[float64]bool, 0)
43
44 m[+0.0] = true
45 m[math.Copysign(0.0, -1.0)] = true
46
47 if len(m) != 1 {
48 t.Error("length wrong")
49 }
50
51 for k := range m {
52 if math.Copysign(1.0, k) > 0 {
53 t.Error("wrong sign")
54 }
55 }
56
57 m = make(map[float64]bool, 0)
58 m[math.Copysign(0.0, -1.0)] = true
59 m[+0.0] = true
60
61 if len(m) != 1 {
62 t.Error("length wrong")
63 }
64
65 for k := range m {
66 if math.Copysign(1.0, k) < 0 {
67 t.Error("wrong sign")
68 }
69 }
70 }
71
72 func testMapNan(t *testing.T, m map[float64]int) {
73 if len(m) != 3 {
74 t.Error("length wrong")
75 }
76 s := 0
77 for k, v := range m {
78 if k == k {
79 t.Error("nan disappeared")
80 }
81 if (v & (v - 1)) != 0 {
82 t.Error("value wrong")
83 }
84 s |= v
85 }
86 if s != 7 {
87 t.Error("values wrong")
88 }
89 }
90
91
92
93 func TestMapAssignmentNan(t *testing.T) {
94 m := make(map[float64]int, 0)
95 nan := math.NaN()
96
97
98 m[nan] = 1
99 m[nan] = 2
100 m[nan] = 4
101 testMapNan(t, m)
102 }
103
104
105
106 func TestMapOperatorAssignmentNan(t *testing.T) {
107 m := make(map[float64]int, 0)
108 nan := math.NaN()
109
110
111 m[nan] += 1
112 m[nan] += 2
113 m[nan] += 4
114 testMapNan(t, m)
115 }
116
117 func TestMapOperatorAssignment(t *testing.T) {
118 m := make(map[int]int, 0)
119
120
121
122
123 m[0] = 12345
124 m[0] += 67890
125 m[0] /= 123
126 m[0] %= 456
127
128 const want = (12345 + 67890) / 123 % 456
129 if got := m[0]; got != want {
130 t.Errorf("got %d, want %d", got, want)
131 }
132 }
133
134 var sinkAppend bool
135
136 func TestMapAppendAssignment(t *testing.T) {
137 m := make(map[int][]int, 0)
138
139 m[0] = nil
140 m[0] = append(m[0], 12345)
141 m[0] = append(m[0], 67890)
142 sinkAppend, m[0] = !sinkAppend, append(m[0], 123, 456)
143 a := []int{7, 8, 9, 0}
144 m[0] = append(m[0], a...)
145
146 want := []int{12345, 67890, 123, 456, 7, 8, 9, 0}
147 if got := m[0]; !reflect.DeepEqual(got, want) {
148 t.Errorf("got %v, want %v", got, want)
149 }
150 }
151
152
153 func TestAlias(t *testing.T) {
154 m := make(map[int]int, 0)
155 m[0] = 5
156 n := m
157 n[0] = 6
158 if m[0] != 6 {
159 t.Error("alias didn't work")
160 }
161 }
162
163 func TestGrowWithNaN(t *testing.T) {
164 m := make(map[float64]int, 4)
165 nan := math.NaN()
166
167
168
169 m[nan] = 1
170 m[nan] = 2
171 m[nan] += 4
172
173 cnt := 0
174 s := 0
175 growflag := true
176 for k, v := range m {
177 if growflag {
178
179 for i := 0; i < 50; i++ {
180 m[float64(i)] = i
181 }
182 for i := 50; i < 100; i++ {
183 m[float64(i)] += i
184 }
185 growflag = false
186 }
187 if k != k {
188 cnt++
189 s |= v
190 }
191 }
192 if cnt != 3 {
193 t.Error("NaN keys lost during grow")
194 }
195 if s != 7 {
196 t.Error("NaN values lost during grow")
197 }
198 }
199
200 type FloatInt struct {
201 x float64
202 y int
203 }
204
205 func TestGrowWithNegativeZero(t *testing.T) {
206 negzero := math.Copysign(0.0, -1.0)
207 m := make(map[FloatInt]int, 4)
208 m[FloatInt{0.0, 0}] = 1
209 m[FloatInt{0.0, 1}] += 2
210 m[FloatInt{0.0, 2}] += 4
211 m[FloatInt{0.0, 3}] = 8
212 growflag := true
213 s := 0
214 cnt := 0
215 negcnt := 0
216
217
218
219
220
221 for k, v := range m {
222 if v == 0 {
223 continue
224 }
225 cnt++
226 if math.Copysign(1.0, k.x) < 0 {
227 if v&16 == 0 {
228 t.Error("key/value not updated together 1")
229 }
230 negcnt++
231 s |= v & 15
232 } else {
233 if v&16 == 16 {
234 t.Error("key/value not updated together 2", k, v)
235 }
236 s |= v
237 }
238 if growflag {
239
240 for i := 0; i < 100; i++ {
241 m[FloatInt{3.0, i}] = 0
242 }
243
244
245 m[FloatInt{negzero, 0}] = 1 | 16
246 m[FloatInt{negzero, 1}] = 2 | 16
247 m[FloatInt{negzero, 2}] = 4 | 16
248 m[FloatInt{negzero, 3}] = 8 | 16
249 growflag = false
250 }
251 }
252 if s != 15 {
253 t.Error("entry missing", s)
254 }
255 if cnt != 4 {
256 t.Error("wrong number of entries returned by iterator", cnt)
257 }
258 if negcnt != 3 {
259 t.Error("update to negzero missed by iteration", negcnt)
260 }
261 }
262
263 func TestIterGrowAndDelete(t *testing.T) {
264 m := make(map[int]int, 4)
265 for i := 0; i < 100; i++ {
266 m[i] = i
267 }
268 growflag := true
269 for k := range m {
270 if growflag {
271
272 for i := 100; i < 1000; i++ {
273 m[i] = i
274 }
275
276 for i := 1; i < 1000; i += 2 {
277 delete(m, i)
278 }
279 growflag = false
280 } else {
281 if k&1 == 1 {
282 t.Error("odd value returned")
283 }
284 }
285 }
286 }
287
288
289
290 func TestIterGrowWithGC(t *testing.T) {
291 m := make(map[int]int, 4)
292 for i := 0; i < 8; i++ {
293 m[i] = i
294 }
295 for i := 8; i < 16; i++ {
296 m[i] += i
297 }
298 growflag := true
299 bitmask := 0
300 for k := range m {
301 if k < 16 {
302 bitmask |= 1 << uint(k)
303 }
304 if growflag {
305
306 for i := 100; i < 1000; i++ {
307 m[i] = i
308 }
309
310 runtime.GC()
311 growflag = false
312 }
313 }
314 if bitmask != 1<<16-1 {
315 t.Error("missing key", bitmask)
316 }
317 }
318
319 func testConcurrentReadsAfterGrowth(t *testing.T, useReflect bool) {
320 t.Parallel()
321 if runtime.GOMAXPROCS(-1) == 1 {
322 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16))
323 }
324 numLoop := 10
325 numGrowStep := 250
326 numReader := 16
327 if testing.Short() {
328 numLoop, numGrowStep = 2, 100
329 }
330 for i := 0; i < numLoop; i++ {
331 m := make(map[int]int, 0)
332 for gs := 0; gs < numGrowStep; gs++ {
333 m[gs] = gs
334 var wg sync.WaitGroup
335 wg.Add(numReader * 2)
336 for nr := 0; nr < numReader; nr++ {
337 go func() {
338 defer wg.Done()
339 for range m {
340 }
341 }()
342 go func() {
343 defer wg.Done()
344 for key := 0; key < gs; key++ {
345 _ = m[key]
346 }
347 }()
348 if useReflect {
349 wg.Add(1)
350 go func() {
351 defer wg.Done()
352 mv := reflect.ValueOf(m)
353 keys := mv.MapKeys()
354 for _, k := range keys {
355 mv.MapIndex(k)
356 }
357 }()
358 }
359 }
360 wg.Wait()
361 }
362 }
363 }
364
365 func TestConcurrentReadsAfterGrowth(t *testing.T) {
366 testConcurrentReadsAfterGrowth(t, false)
367 }
368
369 func TestConcurrentReadsAfterGrowthReflect(t *testing.T) {
370 testConcurrentReadsAfterGrowth(t, true)
371 }
372
373 func TestBigItems(t *testing.T) {
374 var key [256]string
375 for i := 0; i < 256; i++ {
376 key[i] = "foo"
377 }
378 m := make(map[[256]string][256]string, 4)
379 for i := 0; i < 100; i++ {
380 key[37] = fmt.Sprintf("string%02d", i)
381 m[key] = key
382 }
383 var keys [100]string
384 var values [100]string
385 i := 0
386 for k, v := range m {
387 keys[i] = k[37]
388 values[i] = v[37]
389 i++
390 }
391 slices.Sort(keys[:])
392 slices.Sort(values[:])
393 for i := 0; i < 100; i++ {
394 if keys[i] != fmt.Sprintf("string%02d", i) {
395 t.Errorf("#%d: missing key: %v", i, keys[i])
396 }
397 if values[i] != fmt.Sprintf("string%02d", i) {
398 t.Errorf("#%d: missing value: %v", i, values[i])
399 }
400 }
401 }
402
403 func TestMapHugeZero(t *testing.T) {
404 type T [4000]byte
405 m := map[int]T{}
406 x := m[0]
407 if x != (T{}) {
408 t.Errorf("map value not zero")
409 }
410 y, ok := m[0]
411 if ok {
412 t.Errorf("map value should be missing")
413 }
414 if y != (T{}) {
415 t.Errorf("map value not zero")
416 }
417 }
418
419 type empty struct {
420 }
421
422 func TestEmptyKeyAndValue(t *testing.T) {
423 a := make(map[int]empty, 4)
424 b := make(map[empty]int, 4)
425 c := make(map[empty]empty, 4)
426 a[0] = empty{}
427 b[empty{}] = 0
428 b[empty{}] = 1
429 c[empty{}] = empty{}
430
431 if len(a) != 1 {
432 t.Errorf("empty value insert problem")
433 }
434 if b[empty{}] != 1 {
435 t.Errorf("empty key returned wrong value")
436 }
437 }
438
439
440
441 func TestSingleBucketMapStringKeys_DupLen(t *testing.T) {
442 testMapLookups(t, map[string]string{
443 "x": "x1val",
444 "xx": "x2val",
445 "foo": "fooval",
446 "bar": "barval",
447 "xxxx": "x4val",
448 strings.Repeat("x", 128): "longval1",
449 strings.Repeat("y", 128): "longval2",
450 })
451 }
452
453
454 func TestSingleBucketMapStringKeys_NoDupLen(t *testing.T) {
455 testMapLookups(t, map[string]string{
456 "x": "x1val",
457 "xx": "x2val",
458 "foo": "fooval",
459 "xxxx": "x4val",
460 "xxxxx": "x5val",
461 "xxxxxx": "x6val",
462 strings.Repeat("x", 128): "longval",
463 })
464 }
465
466 func testMapLookups(t *testing.T, m map[string]string) {
467 for k, v := range m {
468 if m[k] != v {
469 t.Fatalf("m[%q] = %q; want %q", k, m[k], v)
470 }
471 }
472 }
473
474
475
476 func TestMapNanGrowIterator(t *testing.T) {
477 m := make(map[float64]int)
478 nan := math.NaN()
479 const nBuckets = 16
480
481 nKeys := int(nBuckets * runtime.HashLoad)
482
483
484 for i := 0; i < nKeys; i++ {
485 m[nan] = i
486 }
487
488 m[1.0] = 1
489 delete(m, 1.0)
490
491
492 found := make(map[int]struct{})
493 for _, v := range m {
494 if v != -1 {
495 if _, repeat := found[v]; repeat {
496 t.Fatalf("repeat of value %d", v)
497 }
498 found[v] = struct{}{}
499 }
500 if len(found) == nKeys/2 {
501
502 for i := 0; i < nBuckets; i++ {
503 delete(m, 1.0)
504 }
505 }
506 }
507 if len(found) != nKeys {
508 t.Fatalf("missing value")
509 }
510 }
511
512 func TestMapIterOrder(t *testing.T) {
513 sizes := []int{3, 7, 9, 15}
514 if abi.MapBucketCountBits >= 5 {
515
516 t.Fatalf("This test becomes flaky if abi.MapBucketCountBits(=%d) is 5 or larger", abi.MapBucketCountBits)
517 }
518 for _, n := range sizes {
519 for i := 0; i < 1000; i++ {
520
521 m := make(map[int]bool)
522 for i := 0; i < n; i++ {
523 m[i] = true
524 }
525
526 ord := func() []int {
527 var s []int
528 for key := range m {
529 s = append(s, key)
530 }
531 return s
532 }
533 first := ord()
534 ok := false
535 for try := 0; try < 100; try++ {
536 if !reflect.DeepEqual(first, ord()) {
537 ok = true
538 break
539 }
540 }
541 if !ok {
542 t.Errorf("Map with n=%d elements had consistent iteration order: %v", n, first)
543 break
544 }
545 }
546 }
547 }
548
549
550 func TestMapSparseIterOrder(t *testing.T) {
551
552
553 NextRound:
554 for round := 0; round < 10; round++ {
555 m := make(map[int]bool)
556
557 for i := 0; i < 1000; i++ {
558 m[i] = true
559 }
560 for i := 20; i < 1000; i++ {
561 delete(m, i)
562 }
563
564 var first []int
565 for i := range m {
566 first = append(first, i)
567 }
568
569
570
571 for n := 0; n < 800; n++ {
572 idx := 0
573 for i := range m {
574 if i != first[idx] {
575
576 continue NextRound
577 }
578 idx++
579 }
580 }
581 t.Fatalf("constant iteration order on round %d: %v", round, first)
582 }
583 }
584
585 func TestMapStringBytesLookup(t *testing.T) {
586
587
588 m := map[string]int{
589 "1000000000000000000000000000000000000000000000000": 1,
590 "2000000000000000000000000000000000000000000000000": 2,
591 }
592 buf := []byte("1000000000000000000000000000000000000000000000000")
593 if x := m[string(buf)]; x != 1 {
594 t.Errorf(`m[string([]byte("1"))] = %d, want 1`, x)
595 }
596 buf[0] = '2'
597 if x := m[string(buf)]; x != 2 {
598 t.Errorf(`m[string([]byte("2"))] = %d, want 2`, x)
599 }
600
601 var x int
602 n := testing.AllocsPerRun(100, func() {
603 x += m[string(buf)]
604 })
605 if n != 0 {
606 t.Errorf("AllocsPerRun for m[string(buf)] = %v, want 0", n)
607 }
608
609 x = 0
610 n = testing.AllocsPerRun(100, func() {
611 y, ok := m[string(buf)]
612 if !ok {
613 panic("!ok")
614 }
615 x += y
616 })
617 if n != 0 {
618 t.Errorf("AllocsPerRun for x,ok = m[string(buf)] = %v, want 0", n)
619 }
620 }
621
622 func TestMapLargeKeyNoPointer(t *testing.T) {
623 const (
624 I = 1000
625 N = 64
626 )
627 type T [N]int
628 m := make(map[T]int)
629 for i := 0; i < I; i++ {
630 var v T
631 for j := 0; j < N; j++ {
632 v[j] = i + j
633 }
634 m[v] = i
635 }
636 runtime.GC()
637 for i := 0; i < I; i++ {
638 var v T
639 for j := 0; j < N; j++ {
640 v[j] = i + j
641 }
642 if m[v] != i {
643 t.Fatalf("corrupted map: want %+v, got %+v", i, m[v])
644 }
645 }
646 }
647
648 func TestMapLargeValNoPointer(t *testing.T) {
649 const (
650 I = 1000
651 N = 64
652 )
653 type T [N]int
654 m := make(map[int]T)
655 for i := 0; i < I; i++ {
656 var v T
657 for j := 0; j < N; j++ {
658 v[j] = i + j
659 }
660 m[i] = v
661 }
662 runtime.GC()
663 for i := 0; i < I; i++ {
664 var v T
665 for j := 0; j < N; j++ {
666 v[j] = i + j
667 }
668 v1 := m[i]
669 for j := 0; j < N; j++ {
670 if v1[j] != v[j] {
671 t.Fatalf("corrupted map: want %+v, got %+v", v, v1)
672 }
673 }
674 }
675 }
676
677
678
679 func TestIgnoreBogusMapHint(t *testing.T) {
680 for _, hint := range []int64{-1, 1 << 62} {
681 _ = make(map[int]int, hint)
682 }
683 }
684
685 const bs = abi.MapBucketCount
686
687
688
689
690
691 const (
692 belowOverflow = bs * 3 / 2
693 atOverflow = belowOverflow + bs/8
694 )
695
696 var mapBucketTests = [...]struct {
697 n int
698 noescape int
699 escape int
700 }{
701 {-(1 << 30), 1, 1},
702 {-1, 1, 1},
703 {0, 1, 1},
704 {1, 1, 1},
705 {bs, 1, 1},
706 {bs + 1, 2, 2},
707 {belowOverflow, 2, 2},
708 {atOverflow + 1, 4, 4},
709
710 {2 * belowOverflow, 4, 4},
711 {2*atOverflow + 1, 8, 8},
712
713 {4 * belowOverflow, 8, 8},
714 {4*atOverflow + 1, 16, 16},
715 }
716
717 func TestMapBuckets(t *testing.T) {
718
719
720
721
722
723
724 t.Run("mapliteral", func(t *testing.T) {
725 for _, tt := range mapBucketTests {
726 localMap := map[int]int{}
727 if runtime.MapBucketsPointerIsNil(localMap) {
728 t.Errorf("no escape: buckets pointer is nil for non-escaping map")
729 }
730 for i := 0; i < tt.n; i++ {
731 localMap[i] = i
732 }
733 if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
734 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
735 }
736 escapingMap := runtime.Escape(map[int]int{})
737 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
738 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
739 }
740 for i := 0; i < tt.n; i++ {
741 escapingMap[i] = i
742 }
743 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
744 t.Errorf("escape n=%d want %d buckets, got %d", tt.n, tt.escape, got)
745 }
746 }
747 })
748 t.Run("nohint", func(t *testing.T) {
749 for _, tt := range mapBucketTests {
750 localMap := make(map[int]int)
751 if runtime.MapBucketsPointerIsNil(localMap) {
752 t.Errorf("no escape: buckets pointer is nil for non-escaping map")
753 }
754 for i := 0; i < tt.n; i++ {
755 localMap[i] = i
756 }
757 if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
758 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
759 }
760 escapingMap := runtime.Escape(make(map[int]int))
761 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
762 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
763 }
764 for i := 0; i < tt.n; i++ {
765 escapingMap[i] = i
766 }
767 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
768 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
769 }
770 }
771 })
772 t.Run("makemap", func(t *testing.T) {
773 for _, tt := range mapBucketTests {
774 localMap := make(map[int]int, tt.n)
775 if runtime.MapBucketsPointerIsNil(localMap) {
776 t.Errorf("no escape: buckets pointer is nil for non-escaping map")
777 }
778 for i := 0; i < tt.n; i++ {
779 localMap[i] = i
780 }
781 if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
782 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
783 }
784 escapingMap := runtime.Escape(make(map[int]int, tt.n))
785 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
786 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
787 }
788 for i := 0; i < tt.n; i++ {
789 escapingMap[i] = i
790 }
791 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
792 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
793 }
794 }
795 })
796 t.Run("makemap64", func(t *testing.T) {
797 for _, tt := range mapBucketTests {
798 localMap := make(map[int]int, int64(tt.n))
799 if runtime.MapBucketsPointerIsNil(localMap) {
800 t.Errorf("no escape: buckets pointer is nil for non-escaping map")
801 }
802 for i := 0; i < tt.n; i++ {
803 localMap[i] = i
804 }
805 if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
806 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
807 }
808 escapingMap := runtime.Escape(make(map[int]int, tt.n))
809 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
810 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
811 }
812 for i := 0; i < tt.n; i++ {
813 escapingMap[i] = i
814 }
815 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
816 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
817 }
818 }
819 })
820
821 }
822
823 func benchmarkMapPop(b *testing.B, n int) {
824 m := map[int]int{}
825 for i := 0; i < b.N; i++ {
826 for j := 0; j < n; j++ {
827 m[j] = j
828 }
829 for j := 0; j < n; j++ {
830
831
832 for k := range m {
833 delete(m, k)
834 break
835 }
836 }
837 }
838 }
839
840 func BenchmarkMapPop100(b *testing.B) { benchmarkMapPop(b, 100) }
841 func BenchmarkMapPop1000(b *testing.B) { benchmarkMapPop(b, 1000) }
842 func BenchmarkMapPop10000(b *testing.B) { benchmarkMapPop(b, 10000) }
843
844 var testNonEscapingMapVariable int = 8
845
846 func TestNonEscapingMap(t *testing.T) {
847 n := testing.AllocsPerRun(1000, func() {
848 m := map[int]int{}
849 m[0] = 0
850 })
851 if n != 0 {
852 t.Fatalf("mapliteral: want 0 allocs, got %v", n)
853 }
854 n = testing.AllocsPerRun(1000, func() {
855 m := make(map[int]int)
856 m[0] = 0
857 })
858 if n != 0 {
859 t.Fatalf("no hint: want 0 allocs, got %v", n)
860 }
861 n = testing.AllocsPerRun(1000, func() {
862 m := make(map[int]int, 8)
863 m[0] = 0
864 })
865 if n != 0 {
866 t.Fatalf("with small hint: want 0 allocs, got %v", n)
867 }
868 n = testing.AllocsPerRun(1000, func() {
869 m := make(map[int]int, testNonEscapingMapVariable)
870 m[0] = 0
871 })
872 if n != 0 {
873 t.Fatalf("with variable hint: want 0 allocs, got %v", n)
874 }
875
876 }
877
878 func benchmarkMapAssignInt32(b *testing.B, n int) {
879 a := make(map[int32]int)
880 for i := 0; i < b.N; i++ {
881 a[int32(i&(n-1))] = i
882 }
883 }
884
885 func benchmarkMapOperatorAssignInt32(b *testing.B, n int) {
886 a := make(map[int32]int)
887 for i := 0; i < b.N; i++ {
888 a[int32(i&(n-1))] += i
889 }
890 }
891
892 func benchmarkMapAppendAssignInt32(b *testing.B, n int) {
893 a := make(map[int32][]int)
894 b.ReportAllocs()
895 b.ResetTimer()
896 for i := 0; i < b.N; i++ {
897 key := int32(i & (n - 1))
898 a[key] = append(a[key], i)
899 }
900 }
901
902 func benchmarkMapDeleteInt32(b *testing.B, n int) {
903 a := make(map[int32]int, n)
904 b.ResetTimer()
905 for i := 0; i < b.N; i++ {
906 if len(a) == 0 {
907 b.StopTimer()
908 for j := i; j < i+n; j++ {
909 a[int32(j)] = j
910 }
911 b.StartTimer()
912 }
913 delete(a, int32(i))
914 }
915 }
916
917 func benchmarkMapAssignInt64(b *testing.B, n int) {
918 a := make(map[int64]int)
919 for i := 0; i < b.N; i++ {
920 a[int64(i&(n-1))] = i
921 }
922 }
923
924 func benchmarkMapOperatorAssignInt64(b *testing.B, n int) {
925 a := make(map[int64]int)
926 for i := 0; i < b.N; i++ {
927 a[int64(i&(n-1))] += i
928 }
929 }
930
931 func benchmarkMapAppendAssignInt64(b *testing.B, n int) {
932 a := make(map[int64][]int)
933 b.ReportAllocs()
934 b.ResetTimer()
935 for i := 0; i < b.N; i++ {
936 key := int64(i & (n - 1))
937 a[key] = append(a[key], i)
938 }
939 }
940
941 func benchmarkMapDeleteInt64(b *testing.B, n int) {
942 a := make(map[int64]int, n)
943 b.ResetTimer()
944 for i := 0; i < b.N; i++ {
945 if len(a) == 0 {
946 b.StopTimer()
947 for j := i; j < i+n; j++ {
948 a[int64(j)] = j
949 }
950 b.StartTimer()
951 }
952 delete(a, int64(i))
953 }
954 }
955
956 func benchmarkMapAssignStr(b *testing.B, n int) {
957 k := make([]string, n)
958 for i := 0; i < len(k); i++ {
959 k[i] = strconv.Itoa(i)
960 }
961 b.ResetTimer()
962 a := make(map[string]int)
963 for i := 0; i < b.N; i++ {
964 a[k[i&(n-1)]] = i
965 }
966 }
967
968 func benchmarkMapOperatorAssignStr(b *testing.B, n int) {
969 k := make([]string, n)
970 for i := 0; i < len(k); i++ {
971 k[i] = strconv.Itoa(i)
972 }
973 b.ResetTimer()
974 a := make(map[string]string)
975 for i := 0; i < b.N; i++ {
976 key := k[i&(n-1)]
977 a[key] += key
978 }
979 }
980
981 func benchmarkMapAppendAssignStr(b *testing.B, n int) {
982 k := make([]string, n)
983 for i := 0; i < len(k); i++ {
984 k[i] = strconv.Itoa(i)
985 }
986 a := make(map[string][]string)
987 b.ReportAllocs()
988 b.ResetTimer()
989 for i := 0; i < b.N; i++ {
990 key := k[i&(n-1)]
991 a[key] = append(a[key], key)
992 }
993 }
994
995 func benchmarkMapDeleteStr(b *testing.B, n int) {
996 i2s := make([]string, n)
997 for i := 0; i < n; i++ {
998 i2s[i] = strconv.Itoa(i)
999 }
1000 a := make(map[string]int, n)
1001 b.ResetTimer()
1002 k := 0
1003 for i := 0; i < b.N; i++ {
1004 if len(a) == 0 {
1005 b.StopTimer()
1006 for j := 0; j < n; j++ {
1007 a[i2s[j]] = j
1008 }
1009 k = i
1010 b.StartTimer()
1011 }
1012 delete(a, i2s[i-k])
1013 }
1014 }
1015
1016 func benchmarkMapDeletePointer(b *testing.B, n int) {
1017 i2p := make([]*int, n)
1018 for i := 0; i < n; i++ {
1019 i2p[i] = new(int)
1020 }
1021 a := make(map[*int]int, n)
1022 b.ResetTimer()
1023 k := 0
1024 for i := 0; i < b.N; i++ {
1025 if len(a) == 0 {
1026 b.StopTimer()
1027 for j := 0; j < n; j++ {
1028 a[i2p[j]] = j
1029 }
1030 k = i
1031 b.StartTimer()
1032 }
1033 delete(a, i2p[i-k])
1034 }
1035 }
1036
1037 func runWith(f func(*testing.B, int), v ...int) func(*testing.B) {
1038 return func(b *testing.B) {
1039 for _, n := range v {
1040 b.Run(strconv.Itoa(n), func(b *testing.B) { f(b, n) })
1041 }
1042 }
1043 }
1044
1045 func BenchmarkMapAssign(b *testing.B) {
1046 b.Run("Int32", runWith(benchmarkMapAssignInt32, 1<<8, 1<<16))
1047 b.Run("Int64", runWith(benchmarkMapAssignInt64, 1<<8, 1<<16))
1048 b.Run("Str", runWith(benchmarkMapAssignStr, 1<<8, 1<<16))
1049 }
1050
1051 func BenchmarkMapOperatorAssign(b *testing.B) {
1052 b.Run("Int32", runWith(benchmarkMapOperatorAssignInt32, 1<<8, 1<<16))
1053 b.Run("Int64", runWith(benchmarkMapOperatorAssignInt64, 1<<8, 1<<16))
1054 b.Run("Str", runWith(benchmarkMapOperatorAssignStr, 1<<8, 1<<16))
1055 }
1056
1057 func BenchmarkMapAppendAssign(b *testing.B) {
1058 b.Run("Int32", runWith(benchmarkMapAppendAssignInt32, 1<<8, 1<<16))
1059 b.Run("Int64", runWith(benchmarkMapAppendAssignInt64, 1<<8, 1<<16))
1060 b.Run("Str", runWith(benchmarkMapAppendAssignStr, 1<<8, 1<<16))
1061 }
1062
1063 func BenchmarkMapDelete(b *testing.B) {
1064 b.Run("Int32", runWith(benchmarkMapDeleteInt32, 100, 1000, 10000))
1065 b.Run("Int64", runWith(benchmarkMapDeleteInt64, 100, 1000, 10000))
1066 b.Run("Str", runWith(benchmarkMapDeleteStr, 100, 1000, 10000))
1067 b.Run("Pointer", runWith(benchmarkMapDeletePointer, 100, 1000, 10000))
1068 }
1069
1070 func TestDeferDeleteSlow(t *testing.T) {
1071 ks := []complex128{0, 1, 2, 3}
1072
1073 m := make(map[any]int)
1074 for i, k := range ks {
1075 m[k] = i
1076 }
1077 if len(m) != len(ks) {
1078 t.Errorf("want %d elements, got %d", len(ks), len(m))
1079 }
1080
1081 func() {
1082 for _, k := range ks {
1083 defer delete(m, k)
1084 }
1085 }()
1086 if len(m) != 0 {
1087 t.Errorf("want 0 elements, got %d", len(m))
1088 }
1089 }
1090
1091
1092
1093
1094 func TestIncrementAfterDeleteValueInt(t *testing.T) {
1095 const key1 = 12
1096 const key2 = 13
1097
1098 m := make(map[int]int)
1099 m[key1] = 99
1100 delete(m, key1)
1101 m[key2]++
1102 if n2 := m[key2]; n2 != 1 {
1103 t.Errorf("incremented 0 to %d", n2)
1104 }
1105 }
1106
1107 func TestIncrementAfterDeleteValueInt32(t *testing.T) {
1108 const key1 = 12
1109 const key2 = 13
1110
1111 m := make(map[int]int32)
1112 m[key1] = 99
1113 delete(m, key1)
1114 m[key2]++
1115 if n2 := m[key2]; n2 != 1 {
1116 t.Errorf("incremented 0 to %d", n2)
1117 }
1118 }
1119
1120 func TestIncrementAfterDeleteValueInt64(t *testing.T) {
1121 const key1 = 12
1122 const key2 = 13
1123
1124 m := make(map[int]int64)
1125 m[key1] = 99
1126 delete(m, key1)
1127 m[key2]++
1128 if n2 := m[key2]; n2 != 1 {
1129 t.Errorf("incremented 0 to %d", n2)
1130 }
1131 }
1132
1133 func TestIncrementAfterDeleteKeyStringValueInt(t *testing.T) {
1134 const key1 = ""
1135 const key2 = "x"
1136
1137 m := make(map[string]int)
1138 m[key1] = 99
1139 delete(m, key1)
1140 m[key2] += 1
1141 if n2 := m[key2]; n2 != 1 {
1142 t.Errorf("incremented 0 to %d", n2)
1143 }
1144 }
1145
1146 func TestIncrementAfterDeleteKeyValueString(t *testing.T) {
1147 const key1 = ""
1148 const key2 = "x"
1149
1150 m := make(map[string]string)
1151 m[key1] = "99"
1152 delete(m, key1)
1153 m[key2] += "1"
1154 if n2 := m[key2]; n2 != "1" {
1155 t.Errorf("appended '1' to empty (nil) string, got %s", n2)
1156 }
1157 }
1158
1159
1160
1161
1162 func TestIncrementAfterBulkClearKeyStringValueInt(t *testing.T) {
1163 const key1 = ""
1164 const key2 = "x"
1165
1166 m := make(map[string]int)
1167 m[key1] = 99
1168 for k := range m {
1169 delete(m, k)
1170 }
1171 m[key2]++
1172 if n2 := m[key2]; n2 != 1 {
1173 t.Errorf("incremented 0 to %d", n2)
1174 }
1175 }
1176
1177 func TestMapTombstones(t *testing.T) {
1178 m := map[int]int{}
1179 const N = 10000
1180
1181 for i := 0; i < N; i++ {
1182 m[i] = i
1183 }
1184 runtime.MapTombstoneCheck(m)
1185
1186 for i := 0; i < N; i += 2 {
1187 delete(m, i)
1188 }
1189 runtime.MapTombstoneCheck(m)
1190
1191 for i := N; i < 3*N/2; i++ {
1192 m[i] = i
1193 }
1194 runtime.MapTombstoneCheck(m)
1195
1196 for i := 0; i < 3*N/2; i++ {
1197 delete(m, i)
1198 }
1199 runtime.MapTombstoneCheck(m)
1200 }
1201
1202 type canString int
1203
1204 func (c canString) String() string {
1205 return fmt.Sprintf("%d", int(c))
1206 }
1207
1208 func TestMapInterfaceKey(t *testing.T) {
1209
1210 type GrabBag struct {
1211 f32 float32
1212 f64 float64
1213 c64 complex64
1214 c128 complex128
1215 s string
1216 i0 any
1217 i1 interface {
1218 String() string
1219 }
1220 a [4]string
1221 }
1222
1223 m := map[any]bool{}
1224
1225
1226 for i := 0; i < 1000; i++ {
1227 m[i] = true
1228 }
1229 m[GrabBag{f32: 1.0}] = true
1230 if !m[GrabBag{f32: 1.0}] {
1231 panic("f32 not found")
1232 }
1233 m[GrabBag{f64: 1.0}] = true
1234 if !m[GrabBag{f64: 1.0}] {
1235 panic("f64 not found")
1236 }
1237 m[GrabBag{c64: 1.0i}] = true
1238 if !m[GrabBag{c64: 1.0i}] {
1239 panic("c64 not found")
1240 }
1241 m[GrabBag{c128: 1.0i}] = true
1242 if !m[GrabBag{c128: 1.0i}] {
1243 panic("c128 not found")
1244 }
1245 m[GrabBag{s: "foo"}] = true
1246 if !m[GrabBag{s: "foo"}] {
1247 panic("string not found")
1248 }
1249 m[GrabBag{i0: "foo"}] = true
1250 if !m[GrabBag{i0: "foo"}] {
1251 panic("interface{} not found")
1252 }
1253 m[GrabBag{i1: canString(5)}] = true
1254 if !m[GrabBag{i1: canString(5)}] {
1255 panic("interface{String() string} not found")
1256 }
1257 m[GrabBag{a: [4]string{"foo", "bar", "baz", "bop"}}] = true
1258 if !m[GrabBag{a: [4]string{"foo", "bar", "baz", "bop"}}] {
1259 panic("array not found")
1260 }
1261 }
1262
1263 type panicStructKey struct {
1264 sli []int
1265 }
1266
1267 func (p panicStructKey) String() string {
1268 return "panic"
1269 }
1270
1271 type structKey struct {
1272 }
1273
1274 func (structKey) String() string {
1275 return "structKey"
1276 }
1277
1278 func TestEmptyMapWithInterfaceKey(t *testing.T) {
1279 var (
1280 b bool
1281 i int
1282 i8 int8
1283 i16 int16
1284 i32 int32
1285 i64 int64
1286 ui uint
1287 ui8 uint8
1288 ui16 uint16
1289 ui32 uint32
1290 ui64 uint64
1291 uipt uintptr
1292 f32 float32
1293 f64 float64
1294 c64 complex64
1295 c128 complex128
1296 a [4]string
1297 s string
1298 p *int
1299 up unsafe.Pointer
1300 ch chan int
1301 i0 any
1302 i1 interface {
1303 String() string
1304 }
1305 structKey structKey
1306 i0Panic any = []int{}
1307 i1Panic interface {
1308 String() string
1309 } = panicStructKey{}
1310 panicStructKey = panicStructKey{}
1311 sli []int
1312 me = map[any]struct{}{}
1313 mi = map[interface {
1314 String() string
1315 }]struct{}{}
1316 )
1317 mustNotPanic := func(f func()) {
1318 f()
1319 }
1320 mustPanic := func(f func()) {
1321 defer func() {
1322 r := recover()
1323 if r == nil {
1324 t.Errorf("didn't panic")
1325 }
1326 }()
1327 f()
1328 }
1329 mustNotPanic(func() {
1330 _ = me[b]
1331 })
1332 mustNotPanic(func() {
1333 _ = me[i]
1334 })
1335 mustNotPanic(func() {
1336 _ = me[i8]
1337 })
1338 mustNotPanic(func() {
1339 _ = me[i16]
1340 })
1341 mustNotPanic(func() {
1342 _ = me[i32]
1343 })
1344 mustNotPanic(func() {
1345 _ = me[i64]
1346 })
1347 mustNotPanic(func() {
1348 _ = me[ui]
1349 })
1350 mustNotPanic(func() {
1351 _ = me[ui8]
1352 })
1353 mustNotPanic(func() {
1354 _ = me[ui16]
1355 })
1356 mustNotPanic(func() {
1357 _ = me[ui32]
1358 })
1359 mustNotPanic(func() {
1360 _ = me[ui64]
1361 })
1362 mustNotPanic(func() {
1363 _ = me[uipt]
1364 })
1365 mustNotPanic(func() {
1366 _ = me[f32]
1367 })
1368 mustNotPanic(func() {
1369 _ = me[f64]
1370 })
1371 mustNotPanic(func() {
1372 _ = me[c64]
1373 })
1374 mustNotPanic(func() {
1375 _ = me[c128]
1376 })
1377 mustNotPanic(func() {
1378 _ = me[a]
1379 })
1380 mustNotPanic(func() {
1381 _ = me[s]
1382 })
1383 mustNotPanic(func() {
1384 _ = me[p]
1385 })
1386 mustNotPanic(func() {
1387 _ = me[up]
1388 })
1389 mustNotPanic(func() {
1390 _ = me[ch]
1391 })
1392 mustNotPanic(func() {
1393 _ = me[i0]
1394 })
1395 mustNotPanic(func() {
1396 _ = me[i1]
1397 })
1398 mustNotPanic(func() {
1399 _ = me[structKey]
1400 })
1401 mustPanic(func() {
1402 _ = me[i0Panic]
1403 })
1404 mustPanic(func() {
1405 _ = me[i1Panic]
1406 })
1407 mustPanic(func() {
1408 _ = me[panicStructKey]
1409 })
1410 mustPanic(func() {
1411 _ = me[sli]
1412 })
1413 mustPanic(func() {
1414 _ = me[me]
1415 })
1416
1417 mustNotPanic(func() {
1418 _ = mi[structKey]
1419 })
1420 mustPanic(func() {
1421 _ = mi[panicStructKey]
1422 })
1423 }
1424
1425 func TestLoadFactor(t *testing.T) {
1426 for b := uint8(0); b < 20; b++ {
1427 count := 13 * (1 << b) / 2
1428 if b == 0 {
1429 count = 8
1430 }
1431 if runtime.OverLoadFactor(count, b) {
1432 t.Errorf("OverLoadFactor(%d,%d)=true, want false", count, b)
1433 }
1434 if !runtime.OverLoadFactor(count+1, b) {
1435 t.Errorf("OverLoadFactor(%d,%d)=false, want true", count+1, b)
1436 }
1437 }
1438 }
1439
1440 func TestMapKeys(t *testing.T) {
1441 type key struct {
1442 s string
1443 pad [128]byte
1444 }
1445 m := map[key]int{{s: "a"}: 1, {s: "b"}: 2}
1446 keys := make([]key, 0, len(m))
1447 runtime.MapKeys(m, unsafe.Pointer(&keys))
1448 for _, k := range keys {
1449 if len(k.s) != 1 {
1450 t.Errorf("len(k.s) == %d, want 1", len(k.s))
1451 }
1452 }
1453 }
1454
1455 func TestMapValues(t *testing.T) {
1456 type val struct {
1457 s string
1458 pad [128]byte
1459 }
1460 m := map[int]val{1: {s: "a"}, 2: {s: "b"}}
1461 vals := make([]val, 0, len(m))
1462 runtime.MapValues(m, unsafe.Pointer(&vals))
1463 for _, v := range vals {
1464 if len(v.s) != 1 {
1465 t.Errorf("len(v.s) == %d, want 1", len(v.s))
1466 }
1467 }
1468 }
1469
1470 func computeHash() uintptr {
1471 var v struct{}
1472 return runtime.MemHash(unsafe.Pointer(&v), 0, unsafe.Sizeof(v))
1473 }
1474
1475 func subprocessHash(t *testing.T, env string) uintptr {
1476 t.Helper()
1477
1478 cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestMemHashGlobalSeed$"))
1479 cmd.Env = append(cmd.Env, "GO_TEST_SUBPROCESS_HASH=1")
1480 if env != "" {
1481 cmd.Env = append(cmd.Env, env)
1482 }
1483
1484 out, err := cmd.Output()
1485 if err != nil {
1486 t.Fatalf("cmd.Output got err %v want nil", err)
1487 }
1488
1489 s := strings.TrimSpace(string(out))
1490 h, err := strconv.ParseUint(s, 10, 64)
1491 if err != nil {
1492 t.Fatalf("Parse output %q got err %v want nil", s, err)
1493 }
1494 return uintptr(h)
1495 }
1496
1497
1498
1499
1500
1501 func TestMemHashGlobalSeed(t *testing.T) {
1502 if os.Getenv("GO_TEST_SUBPROCESS_HASH") != "" {
1503 fmt.Println(computeHash())
1504 os.Exit(0)
1505 return
1506 }
1507
1508 testenv.MustHaveExec(t)
1509
1510
1511
1512 t.Run("aes", func(t *testing.T) {
1513 if !*runtime.UseAeshash {
1514 t.Skip("No AES")
1515 }
1516
1517 h1 := subprocessHash(t, "")
1518 t.Logf("%d", h1)
1519 h2 := subprocessHash(t, "")
1520 t.Logf("%d", h2)
1521 h3 := subprocessHash(t, "")
1522 t.Logf("%d", h3)
1523
1524 if h1 == h2 && h2 == h3 {
1525 t.Errorf("got duplicate hash %d want unique", h1)
1526 }
1527 })
1528
1529 t.Run("noaes", func(t *testing.T) {
1530 env := ""
1531 if *runtime.UseAeshash {
1532 env = "GODEBUG=cpu.aes=off"
1533 }
1534
1535 h1 := subprocessHash(t, env)
1536 t.Logf("%d", h1)
1537 h2 := subprocessHash(t, env)
1538 t.Logf("%d", h2)
1539 h3 := subprocessHash(t, env)
1540 t.Logf("%d", h3)
1541
1542 if h1 == h2 && h2 == h3 {
1543 t.Errorf("got duplicate hash %d want unique", h1)
1544 }
1545 })
1546 }
1547
View as plain text