github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/map_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime_test 6 7 import ( 8 "fmt" 9 "internal/abi" 10 "internal/goarch" 11 "math" 12 "reflect" 13 "runtime" 14 "sort" 15 "strconv" 16 "strings" 17 "sync" 18 "testing" 19 ) 20 21 func TestHmapSize(t *testing.T) { 22 // The structure of hmap is defined in runtime/map.go 23 // and in cmd/compile/internal/gc/reflect.go and must be in sync. 24 // The size of hmap should be 48 bytes on 64 bit and 28 bytes on 32 bit platforms. 25 var hmapSize = uintptr(8 + 5*goarch.PtrSize) 26 if runtime.RuntimeHmapSize != hmapSize { 27 t.Errorf("sizeof(runtime.hmap{})==%d, want %d", runtime.RuntimeHmapSize, hmapSize) 28 } 29 30 } 31 32 // negative zero is a good test because: 33 // 1. 0 and -0 are equal, yet have distinct representations. 34 // 2. 0 is represented as all zeros, -0 isn't. 35 // 36 // I'm not sure the language spec actually requires this behavior, 37 // but it's what the current map implementation does. 38 func TestNegativeZero(t *testing.T) { 39 m := make(map[float64]bool, 0) 40 41 m[+0.0] = true 42 m[math.Copysign(0.0, -1.0)] = true // should overwrite +0 entry 43 44 if len(m) != 1 { 45 t.Error("length wrong") 46 } 47 48 for k := range m { 49 if math.Copysign(1.0, k) > 0 { 50 t.Error("wrong sign") 51 } 52 } 53 54 m = make(map[float64]bool, 0) 55 m[math.Copysign(0.0, -1.0)] = true 56 m[+0.0] = true // should overwrite -0.0 entry 57 58 if len(m) != 1 { 59 t.Error("length wrong") 60 } 61 62 for k := range m { 63 if math.Copysign(1.0, k) < 0 { 64 t.Error("wrong sign") 65 } 66 } 67 } 68 69 func testMapNan(t *testing.T, m map[float64]int) { 70 if len(m) != 3 { 71 t.Error("length wrong") 72 } 73 s := 0 74 for k, v := range m { 75 if k == k { 76 t.Error("nan disappeared") 77 } 78 if (v & (v - 1)) != 0 { 79 t.Error("value wrong") 80 } 81 s |= v 82 } 83 if s != 7 { 84 t.Error("values wrong") 85 } 86 } 87 88 // nan is a good test because nan != nan, and nan has 89 // a randomized hash value. 90 func TestMapAssignmentNan(t *testing.T) { 91 m := make(map[float64]int, 0) 92 nan := math.NaN() 93 94 // Test assignment. 95 m[nan] = 1 96 m[nan] = 2 97 m[nan] = 4 98 testMapNan(t, m) 99 } 100 101 // nan is a good test because nan != nan, and nan has 102 // a randomized hash value. 103 func TestMapOperatorAssignmentNan(t *testing.T) { 104 m := make(map[float64]int, 0) 105 nan := math.NaN() 106 107 // Test assignment operations. 108 m[nan] += 1 109 m[nan] += 2 110 m[nan] += 4 111 testMapNan(t, m) 112 } 113 114 func TestMapOperatorAssignment(t *testing.T) { 115 m := make(map[int]int, 0) 116 117 // "m[k] op= x" is rewritten into "m[k] = m[k] op x" 118 // differently when op is / or % than when it isn't. 119 // Simple test to make sure they all work as expected. 120 m[0] = 12345 121 m[0] += 67890 122 m[0] /= 123 123 m[0] %= 456 124 125 const want = (12345 + 67890) / 123 % 456 126 if got := m[0]; got != want { 127 t.Errorf("got %d, want %d", got, want) 128 } 129 } 130 131 var sinkAppend bool 132 133 func TestMapAppendAssignment(t *testing.T) { 134 m := make(map[int][]int, 0) 135 136 m[0] = nil 137 m[0] = append(m[0], 12345) 138 m[0] = append(m[0], 67890) 139 sinkAppend, m[0] = !sinkAppend, append(m[0], 123, 456) 140 a := []int{7, 8, 9, 0} 141 m[0] = append(m[0], a...) 142 143 want := []int{12345, 67890, 123, 456, 7, 8, 9, 0} 144 if got := m[0]; !reflect.DeepEqual(got, want) { 145 t.Errorf("got %v, want %v", got, want) 146 } 147 } 148 149 // Maps aren't actually copied on assignment. 150 func TestAlias(t *testing.T) { 151 m := make(map[int]int, 0) 152 m[0] = 5 153 n := m 154 n[0] = 6 155 if m[0] != 6 { 156 t.Error("alias didn't work") 157 } 158 } 159 160 func TestGrowWithNaN(t *testing.T) { 161 m := make(map[float64]int, 4) 162 nan := math.NaN() 163 164 // Use both assignment and assignment operations as they may 165 // behave differently. 166 m[nan] = 1 167 m[nan] = 2 168 m[nan] += 4 169 170 cnt := 0 171 s := 0 172 growflag := true 173 for k, v := range m { 174 if growflag { 175 // force a hashtable resize 176 for i := 0; i < 50; i++ { 177 m[float64(i)] = i 178 } 179 for i := 50; i < 100; i++ { 180 m[float64(i)] += i 181 } 182 growflag = false 183 } 184 if k != k { 185 cnt++ 186 s |= v 187 } 188 } 189 if cnt != 3 { 190 t.Error("NaN keys lost during grow") 191 } 192 if s != 7 { 193 t.Error("NaN values lost during grow") 194 } 195 } 196 197 type FloatInt struct { 198 x float64 199 y int 200 } 201 202 func TestGrowWithNegativeZero(t *testing.T) { 203 negzero := math.Copysign(0.0, -1.0) 204 m := make(map[FloatInt]int, 4) 205 m[FloatInt{0.0, 0}] = 1 206 m[FloatInt{0.0, 1}] += 2 207 m[FloatInt{0.0, 2}] += 4 208 m[FloatInt{0.0, 3}] = 8 209 growflag := true 210 s := 0 211 cnt := 0 212 negcnt := 0 213 // The first iteration should return the +0 key. 214 // The subsequent iterations should return the -0 key. 215 // I'm not really sure this is required by the spec, 216 // but it makes sense. 217 // TODO: are we allowed to get the first entry returned again??? 218 for k, v := range m { 219 if v == 0 { 220 continue 221 } // ignore entries added to grow table 222 cnt++ 223 if math.Copysign(1.0, k.x) < 0 { 224 if v&16 == 0 { 225 t.Error("key/value not updated together 1") 226 } 227 negcnt++ 228 s |= v & 15 229 } else { 230 if v&16 == 16 { 231 t.Error("key/value not updated together 2", k, v) 232 } 233 s |= v 234 } 235 if growflag { 236 // force a hashtable resize 237 for i := 0; i < 100; i++ { 238 m[FloatInt{3.0, i}] = 0 239 } 240 // then change all the entries 241 // to negative zero 242 m[FloatInt{negzero, 0}] = 1 | 16 243 m[FloatInt{negzero, 1}] = 2 | 16 244 m[FloatInt{negzero, 2}] = 4 | 16 245 m[FloatInt{negzero, 3}] = 8 | 16 246 growflag = false 247 } 248 } 249 if s != 15 { 250 t.Error("entry missing", s) 251 } 252 if cnt != 4 { 253 t.Error("wrong number of entries returned by iterator", cnt) 254 } 255 if negcnt != 3 { 256 t.Error("update to negzero missed by iteration", negcnt) 257 } 258 } 259 260 func TestIterGrowAndDelete(t *testing.T) { 261 m := make(map[int]int, 4) 262 for i := 0; i < 100; i++ { 263 m[i] = i 264 } 265 growflag := true 266 for k := range m { 267 if growflag { 268 // grow the table 269 for i := 100; i < 1000; i++ { 270 m[i] = i 271 } 272 // delete all odd keys 273 for i := 1; i < 1000; i += 2 { 274 delete(m, i) 275 } 276 growflag = false 277 } else { 278 if k&1 == 1 { 279 t.Error("odd value returned") 280 } 281 } 282 } 283 } 284 285 // make sure old bucket arrays don't get GCd while 286 // an iterator is still using them. 287 func TestIterGrowWithGC(t *testing.T) { 288 m := make(map[int]int, 4) 289 for i := 0; i < 8; i++ { 290 m[i] = i 291 } 292 for i := 8; i < 16; i++ { 293 m[i] += i 294 } 295 growflag := true 296 bitmask := 0 297 for k := range m { 298 if k < 16 { 299 bitmask |= 1 << uint(k) 300 } 301 if growflag { 302 // grow the table 303 for i := 100; i < 1000; i++ { 304 m[i] = i 305 } 306 // trigger a gc 307 runtime.GC() 308 growflag = false 309 } 310 } 311 if bitmask != 1<<16-1 { 312 t.Error("missing key", bitmask) 313 } 314 } 315 316 func testConcurrentReadsAfterGrowth(t *testing.T, useReflect bool) { 317 t.Parallel() 318 if runtime.GOMAXPROCS(-1) == 1 { 319 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16)) 320 } 321 numLoop := 10 322 numGrowStep := 250 323 numReader := 16 324 if testing.Short() { 325 numLoop, numGrowStep = 2, 100 326 } 327 for i := 0; i < numLoop; i++ { 328 m := make(map[int]int, 0) 329 for gs := 0; gs < numGrowStep; gs++ { 330 m[gs] = gs 331 var wg sync.WaitGroup 332 wg.Add(numReader * 2) 333 for nr := 0; nr < numReader; nr++ { 334 go func() { 335 defer wg.Done() 336 for range m { 337 } 338 }() 339 go func() { 340 defer wg.Done() 341 for key := 0; key < gs; key++ { 342 _ = m[key] 343 } 344 }() 345 if useReflect { 346 wg.Add(1) 347 go func() { 348 defer wg.Done() 349 mv := reflect.ValueOf(m) 350 keys := mv.MapKeys() 351 for _, k := range keys { 352 mv.MapIndex(k) 353 } 354 }() 355 } 356 } 357 wg.Wait() 358 } 359 } 360 } 361 362 func TestConcurrentReadsAfterGrowth(t *testing.T) { 363 testConcurrentReadsAfterGrowth(t, false) 364 } 365 366 func TestConcurrentReadsAfterGrowthReflect(t *testing.T) { 367 testConcurrentReadsAfterGrowth(t, true) 368 } 369 370 func TestBigItems(t *testing.T) { 371 var key [256]string 372 for i := 0; i < 256; i++ { 373 key[i] = "foo" 374 } 375 m := make(map[[256]string][256]string, 4) 376 for i := 0; i < 100; i++ { 377 key[37] = fmt.Sprintf("string%02d", i) 378 m[key] = key 379 } 380 var keys [100]string 381 var values [100]string 382 i := 0 383 for k, v := range m { 384 keys[i] = k[37] 385 values[i] = v[37] 386 i++ 387 } 388 sort.Strings(keys[:]) 389 sort.Strings(values[:]) 390 for i := 0; i < 100; i++ { 391 if keys[i] != fmt.Sprintf("string%02d", i) { 392 t.Errorf("#%d: missing key: %v", i, keys[i]) 393 } 394 if values[i] != fmt.Sprintf("string%02d", i) { 395 t.Errorf("#%d: missing value: %v", i, values[i]) 396 } 397 } 398 } 399 400 func TestMapHugeZero(t *testing.T) { 401 type T [4000]byte 402 m := map[int]T{} 403 x := m[0] 404 if x != (T{}) { 405 t.Errorf("map value not zero") 406 } 407 y, ok := m[0] 408 if ok { 409 t.Errorf("map value should be missing") 410 } 411 if y != (T{}) { 412 t.Errorf("map value not zero") 413 } 414 } 415 416 type empty struct { 417 } 418 419 func TestEmptyKeyAndValue(t *testing.T) { 420 a := make(map[int]empty, 4) 421 b := make(map[empty]int, 4) 422 c := make(map[empty]empty, 4) 423 a[0] = empty{} 424 b[empty{}] = 0 425 b[empty{}] = 1 426 c[empty{}] = empty{} 427 428 if len(a) != 1 { 429 t.Errorf("empty value insert problem") 430 } 431 if b[empty{}] != 1 { 432 t.Errorf("empty key returned wrong value") 433 } 434 } 435 436 // Tests a map with a single bucket, with same-lengthed short keys 437 // ("quick keys") as well as long keys. 438 func TestSingleBucketMapStringKeys_DupLen(t *testing.T) { 439 testMapLookups(t, map[string]string{ 440 "x": "x1val", 441 "xx": "x2val", 442 "foo": "fooval", 443 "bar": "barval", // same key length as "foo" 444 "xxxx": "x4val", 445 strings.Repeat("x", 128): "longval1", 446 strings.Repeat("y", 128): "longval2", 447 }) 448 } 449 450 // Tests a map with a single bucket, with all keys having different lengths. 451 func TestSingleBucketMapStringKeys_NoDupLen(t *testing.T) { 452 testMapLookups(t, map[string]string{ 453 "x": "x1val", 454 "xx": "x2val", 455 "foo": "fooval", 456 "xxxx": "x4val", 457 "xxxxx": "x5val", 458 "xxxxxx": "x6val", 459 strings.Repeat("x", 128): "longval", 460 }) 461 } 462 463 func testMapLookups(t *testing.T, m map[string]string) { 464 for k, v := range m { 465 if m[k] != v { 466 t.Fatalf("m[%q] = %q; want %q", k, m[k], v) 467 } 468 } 469 } 470 471 // Tests whether the iterator returns the right elements when 472 // started in the middle of a grow, when the keys are NaNs. 473 func TestMapNanGrowIterator(t *testing.T) { 474 m := make(map[float64]int) 475 nan := math.NaN() 476 const nBuckets = 16 477 // To fill nBuckets buckets takes LOAD * nBuckets keys. 478 nKeys := int(nBuckets * runtime.HashLoad) 479 480 // Get map to full point with nan keys. 481 for i := 0; i < nKeys; i++ { 482 m[nan] = i 483 } 484 // Trigger grow 485 m[1.0] = 1 486 delete(m, 1.0) 487 488 // Run iterator 489 found := make(map[int]struct{}) 490 for _, v := range m { 491 if v != -1 { 492 if _, repeat := found[v]; repeat { 493 t.Fatalf("repeat of value %d", v) 494 } 495 found[v] = struct{}{} 496 } 497 if len(found) == nKeys/2 { 498 // Halfway through iteration, finish grow. 499 for i := 0; i < nBuckets; i++ { 500 delete(m, 1.0) 501 } 502 } 503 } 504 if len(found) != nKeys { 505 t.Fatalf("missing value") 506 } 507 } 508 509 func TestMapIterOrder(t *testing.T) { 510 sizes := []int{3, 7, 9, 15} 511 if abi.MapBucketCountBits >= 5 { 512 // it gets flaky (often only one iteration order) at size 3 when abi.MapBucketCountBits >=5. 513 t.Fatalf("This test becomes flaky if abi.MapBucketCountBits(=%d) is 5 or larger", abi.MapBucketCountBits) 514 } 515 for _, n := range sizes { 516 for i := 0; i < 1000; i++ { 517 // Make m be {0: true, 1: true, ..., n-1: true}. 518 m := make(map[int]bool) 519 for i := 0; i < n; i++ { 520 m[i] = true 521 } 522 // Check that iterating over the map produces at least two different orderings. 523 ord := func() []int { 524 var s []int 525 for key := range m { 526 s = append(s, key) 527 } 528 return s 529 } 530 first := ord() 531 ok := false 532 for try := 0; try < 100; try++ { 533 if !reflect.DeepEqual(first, ord()) { 534 ok = true 535 break 536 } 537 } 538 if !ok { 539 t.Errorf("Map with n=%d elements had consistent iteration order: %v", n, first) 540 break 541 } 542 } 543 } 544 } 545 546 // Issue 8410 547 func TestMapSparseIterOrder(t *testing.T) { 548 // Run several rounds to increase the probability 549 // of failure. One is not enough. 550 NextRound: 551 for round := 0; round < 10; round++ { 552 m := make(map[int]bool) 553 // Add 1000 items, remove 980. 554 for i := 0; i < 1000; i++ { 555 m[i] = true 556 } 557 for i := 20; i < 1000; i++ { 558 delete(m, i) 559 } 560 561 var first []int 562 for i := range m { 563 first = append(first, i) 564 } 565 566 // 800 chances to get a different iteration order. 567 // See bug 8736 for why we need so many tries. 568 for n := 0; n < 800; n++ { 569 idx := 0 570 for i := range m { 571 if i != first[idx] { 572 // iteration order changed. 573 continue NextRound 574 } 575 idx++ 576 } 577 } 578 t.Fatalf("constant iteration order on round %d: %v", round, first) 579 } 580 } 581 582 func TestMapStringBytesLookup(t *testing.T) { 583 // Use large string keys to avoid small-allocation coalescing, 584 // which can cause AllocsPerRun to report lower counts than it should. 585 m := map[string]int{ 586 "1000000000000000000000000000000000000000000000000": 1, 587 "2000000000000000000000000000000000000000000000000": 2, 588 } 589 buf := []byte("1000000000000000000000000000000000000000000000000") 590 if x := m[string(buf)]; x != 1 { 591 t.Errorf(`m[string([]byte("1"))] = %d, want 1`, x) 592 } 593 buf[0] = '2' 594 if x := m[string(buf)]; x != 2 { 595 t.Errorf(`m[string([]byte("2"))] = %d, want 2`, x) 596 } 597 598 var x int 599 n := testing.AllocsPerRun(100, func() { 600 x += m[string(buf)] 601 }) 602 if n != 0 { 603 t.Errorf("AllocsPerRun for m[string(buf)] = %v, want 0", n) 604 } 605 606 x = 0 607 n = testing.AllocsPerRun(100, func() { 608 y, ok := m[string(buf)] 609 if !ok { 610 panic("!ok") 611 } 612 x += y 613 }) 614 if n != 0 { 615 t.Errorf("AllocsPerRun for x,ok = m[string(buf)] = %v, want 0", n) 616 } 617 } 618 619 func TestMapLargeKeyNoPointer(t *testing.T) { 620 const ( 621 I = 1000 622 N = 64 623 ) 624 type T [N]int 625 m := make(map[T]int) 626 for i := 0; i < I; i++ { 627 var v T 628 for j := 0; j < N; j++ { 629 v[j] = i + j 630 } 631 m[v] = i 632 } 633 runtime.GC() 634 for i := 0; i < I; i++ { 635 var v T 636 for j := 0; j < N; j++ { 637 v[j] = i + j 638 } 639 if m[v] != i { 640 t.Fatalf("corrupted map: want %+v, got %+v", i, m[v]) 641 } 642 } 643 } 644 645 func TestMapLargeValNoPointer(t *testing.T) { 646 const ( 647 I = 1000 648 N = 64 649 ) 650 type T [N]int 651 m := make(map[int]T) 652 for i := 0; i < I; i++ { 653 var v T 654 for j := 0; j < N; j++ { 655 v[j] = i + j 656 } 657 m[i] = v 658 } 659 runtime.GC() 660 for i := 0; i < I; i++ { 661 var v T 662 for j := 0; j < N; j++ { 663 v[j] = i + j 664 } 665 v1 := m[i] 666 for j := 0; j < N; j++ { 667 if v1[j] != v[j] { 668 t.Fatalf("corrupted map: want %+v, got %+v", v, v1) 669 } 670 } 671 } 672 } 673 674 // Test that making a map with a large or invalid hint 675 // doesn't panic. (Issue 19926). 676 func TestIgnoreBogusMapHint(t *testing.T) { 677 for _, hint := range []int64{-1, 1 << 62} { 678 _ = make(map[int]int, hint) 679 } 680 } 681 682 const bs = abi.MapBucketCount 683 684 // belowOverflow should be a pretty-full pair of buckets; 685 // atOverflow is 1/8 bs larger = 13/8 buckets or two buckets 686 // that are 13/16 full each, which is the overflow boundary. 687 // Adding one to that should ensure overflow to the next higher size. 688 const ( 689 belowOverflow = bs * 3 / 2 // 1.5 bs = 2 buckets @ 75% 690 atOverflow = belowOverflow + bs/8 // 2 buckets at 13/16 fill. 691 ) 692 693 var mapBucketTests = [...]struct { 694 n int // n is the number of map elements 695 noescape int // number of expected buckets for non-escaping map 696 escape int // number of expected buckets for escaping map 697 }{ 698 {-(1 << 30), 1, 1}, 699 {-1, 1, 1}, 700 {0, 1, 1}, 701 {1, 1, 1}, 702 {bs, 1, 1}, 703 {bs + 1, 2, 2}, 704 {belowOverflow, 2, 2}, // 1.5 bs = 2 buckets @ 75% 705 {atOverflow + 1, 4, 4}, // 13/8 bs + 1 == overflow to 4 706 707 {2 * belowOverflow, 4, 4}, // 3 bs = 4 buckets @75% 708 {2*atOverflow + 1, 8, 8}, // 13/4 bs + 1 = overflow to 8 709 710 {4 * belowOverflow, 8, 8}, // 6 bs = 8 buckets @ 75% 711 {4*atOverflow + 1, 16, 16}, // 13/2 bs + 1 = overflow to 16 712 } 713 714 func TestMapBuckets(t *testing.T) { 715 // Test that maps of different sizes have the right number of buckets. 716 // Non-escaping maps with small buckets (like map[int]int) never 717 // have a nil bucket pointer due to starting with preallocated buckets 718 // on the stack. Escaping maps start with a non-nil bucket pointer if 719 // hint size is above bucketCnt and thereby have more than one bucket. 720 // These tests depend on bucketCnt and loadFactor* in map.go. 721 t.Run("mapliteral", func(t *testing.T) { 722 for _, tt := range mapBucketTests { 723 localMap := map[int]int{} 724 if runtime.MapBucketsPointerIsNil(localMap) { 725 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 726 } 727 for i := 0; i < tt.n; i++ { 728 localMap[i] = i 729 } 730 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 731 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 732 } 733 escapingMap := runtime.Escape(map[int]int{}) 734 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 735 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 736 } 737 for i := 0; i < tt.n; i++ { 738 escapingMap[i] = i 739 } 740 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 741 t.Errorf("escape n=%d want %d buckets, got %d", tt.n, tt.escape, got) 742 } 743 } 744 }) 745 t.Run("nohint", func(t *testing.T) { 746 for _, tt := range mapBucketTests { 747 localMap := make(map[int]int) 748 if runtime.MapBucketsPointerIsNil(localMap) { 749 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 750 } 751 for i := 0; i < tt.n; i++ { 752 localMap[i] = i 753 } 754 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 755 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 756 } 757 escapingMap := runtime.Escape(make(map[int]int)) 758 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 759 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 760 } 761 for i := 0; i < tt.n; i++ { 762 escapingMap[i] = i 763 } 764 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 765 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) 766 } 767 } 768 }) 769 t.Run("makemap", func(t *testing.T) { 770 for _, tt := range mapBucketTests { 771 localMap := make(map[int]int, tt.n) 772 if runtime.MapBucketsPointerIsNil(localMap) { 773 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 774 } 775 for i := 0; i < tt.n; i++ { 776 localMap[i] = i 777 } 778 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 779 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 780 } 781 escapingMap := runtime.Escape(make(map[int]int, tt.n)) 782 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 783 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 784 } 785 for i := 0; i < tt.n; i++ { 786 escapingMap[i] = i 787 } 788 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 789 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) 790 } 791 } 792 }) 793 t.Run("makemap64", func(t *testing.T) { 794 for _, tt := range mapBucketTests { 795 localMap := make(map[int]int, int64(tt.n)) 796 if runtime.MapBucketsPointerIsNil(localMap) { 797 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 798 } 799 for i := 0; i < tt.n; i++ { 800 localMap[i] = i 801 } 802 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 803 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 804 } 805 escapingMap := runtime.Escape(make(map[int]int, tt.n)) 806 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 807 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 808 } 809 for i := 0; i < tt.n; i++ { 810 escapingMap[i] = i 811 } 812 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 813 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) 814 } 815 } 816 }) 817 818 } 819 820 func benchmarkMapPop(b *testing.B, n int) { 821 m := map[int]int{} 822 for i := 0; i < b.N; i++ { 823 for j := 0; j < n; j++ { 824 m[j] = j 825 } 826 for j := 0; j < n; j++ { 827 // Use iterator to pop an element. 828 // We want this to be fast, see issue 8412. 829 for k := range m { 830 delete(m, k) 831 break 832 } 833 } 834 } 835 } 836 837 func BenchmarkMapPop100(b *testing.B) { benchmarkMapPop(b, 100) } 838 func BenchmarkMapPop1000(b *testing.B) { benchmarkMapPop(b, 1000) } 839 func BenchmarkMapPop10000(b *testing.B) { benchmarkMapPop(b, 10000) } 840 841 var testNonEscapingMapVariable int = 8 842 843 func TestNonEscapingMap(t *testing.T) { 844 n := testing.AllocsPerRun(1000, func() { 845 m := map[int]int{} 846 m[0] = 0 847 }) 848 if n != 0 { 849 t.Fatalf("mapliteral: want 0 allocs, got %v", n) 850 } 851 n = testing.AllocsPerRun(1000, func() { 852 m := make(map[int]int) 853 m[0] = 0 854 }) 855 if n != 0 { 856 t.Fatalf("no hint: want 0 allocs, got %v", n) 857 } 858 n = testing.AllocsPerRun(1000, func() { 859 m := make(map[int]int, 8) 860 m[0] = 0 861 }) 862 if n != 0 { 863 t.Fatalf("with small hint: want 0 allocs, got %v", n) 864 } 865 n = testing.AllocsPerRun(1000, func() { 866 m := make(map[int]int, testNonEscapingMapVariable) 867 m[0] = 0 868 }) 869 if n != 0 { 870 t.Fatalf("with variable hint: want 0 allocs, got %v", n) 871 } 872 873 } 874 875 func benchmarkMapAssignInt32(b *testing.B, n int) { 876 a := make(map[int32]int) 877 for i := 0; i < b.N; i++ { 878 a[int32(i&(n-1))] = i 879 } 880 } 881 882 func benchmarkMapOperatorAssignInt32(b *testing.B, n int) { 883 a := make(map[int32]int) 884 for i := 0; i < b.N; i++ { 885 a[int32(i&(n-1))] += i 886 } 887 } 888 889 func benchmarkMapAppendAssignInt32(b *testing.B, n int) { 890 a := make(map[int32][]int) 891 b.ReportAllocs() 892 b.ResetTimer() 893 for i := 0; i < b.N; i++ { 894 key := int32(i & (n - 1)) 895 a[key] = append(a[key], i) 896 } 897 } 898 899 func benchmarkMapDeleteInt32(b *testing.B, n int) { 900 a := make(map[int32]int, n) 901 b.ResetTimer() 902 for i := 0; i < b.N; i++ { 903 if len(a) == 0 { 904 b.StopTimer() 905 for j := i; j < i+n; j++ { 906 a[int32(j)] = j 907 } 908 b.StartTimer() 909 } 910 delete(a, int32(i)) 911 } 912 } 913 914 func benchmarkMapAssignInt64(b *testing.B, n int) { 915 a := make(map[int64]int) 916 for i := 0; i < b.N; i++ { 917 a[int64(i&(n-1))] = i 918 } 919 } 920 921 func benchmarkMapOperatorAssignInt64(b *testing.B, n int) { 922 a := make(map[int64]int) 923 for i := 0; i < b.N; i++ { 924 a[int64(i&(n-1))] += i 925 } 926 } 927 928 func benchmarkMapAppendAssignInt64(b *testing.B, n int) { 929 a := make(map[int64][]int) 930 b.ReportAllocs() 931 b.ResetTimer() 932 for i := 0; i < b.N; i++ { 933 key := int64(i & (n - 1)) 934 a[key] = append(a[key], i) 935 } 936 } 937 938 func benchmarkMapDeleteInt64(b *testing.B, n int) { 939 a := make(map[int64]int, n) 940 b.ResetTimer() 941 for i := 0; i < b.N; i++ { 942 if len(a) == 0 { 943 b.StopTimer() 944 for j := i; j < i+n; j++ { 945 a[int64(j)] = j 946 } 947 b.StartTimer() 948 } 949 delete(a, int64(i)) 950 } 951 } 952 953 func benchmarkMapAssignStr(b *testing.B, n int) { 954 k := make([]string, n) 955 for i := 0; i < len(k); i++ { 956 k[i] = strconv.Itoa(i) 957 } 958 b.ResetTimer() 959 a := make(map[string]int) 960 for i := 0; i < b.N; i++ { 961 a[k[i&(n-1)]] = i 962 } 963 } 964 965 func benchmarkMapOperatorAssignStr(b *testing.B, n int) { 966 k := make([]string, n) 967 for i := 0; i < len(k); i++ { 968 k[i] = strconv.Itoa(i) 969 } 970 b.ResetTimer() 971 a := make(map[string]string) 972 for i := 0; i < b.N; i++ { 973 key := k[i&(n-1)] 974 a[key] += key 975 } 976 } 977 978 func benchmarkMapAppendAssignStr(b *testing.B, n int) { 979 k := make([]string, n) 980 for i := 0; i < len(k); i++ { 981 k[i] = strconv.Itoa(i) 982 } 983 a := make(map[string][]string) 984 b.ReportAllocs() 985 b.ResetTimer() 986 for i := 0; i < b.N; i++ { 987 key := k[i&(n-1)] 988 a[key] = append(a[key], key) 989 } 990 } 991 992 func benchmarkMapDeleteStr(b *testing.B, n int) { 993 i2s := make([]string, n) 994 for i := 0; i < n; i++ { 995 i2s[i] = strconv.Itoa(i) 996 } 997 a := make(map[string]int, n) 998 b.ResetTimer() 999 k := 0 1000 for i := 0; i < b.N; i++ { 1001 if len(a) == 0 { 1002 b.StopTimer() 1003 for j := 0; j < n; j++ { 1004 a[i2s[j]] = j 1005 } 1006 k = i 1007 b.StartTimer() 1008 } 1009 delete(a, i2s[i-k]) 1010 } 1011 } 1012 1013 func benchmarkMapDeletePointer(b *testing.B, n int) { 1014 i2p := make([]*int, n) 1015 for i := 0; i < n; i++ { 1016 i2p[i] = new(int) 1017 } 1018 a := make(map[*int]int, n) 1019 b.ResetTimer() 1020 k := 0 1021 for i := 0; i < b.N; i++ { 1022 if len(a) == 0 { 1023 b.StopTimer() 1024 for j := 0; j < n; j++ { 1025 a[i2p[j]] = j 1026 } 1027 k = i 1028 b.StartTimer() 1029 } 1030 delete(a, i2p[i-k]) 1031 } 1032 } 1033 1034 func runWith(f func(*testing.B, int), v ...int) func(*testing.B) { 1035 return func(b *testing.B) { 1036 for _, n := range v { 1037 b.Run(strconv.Itoa(n), func(b *testing.B) { f(b, n) }) 1038 } 1039 } 1040 } 1041 1042 func BenchmarkMapAssign(b *testing.B) { 1043 b.Run("Int32", runWith(benchmarkMapAssignInt32, 1<<8, 1<<16)) 1044 b.Run("Int64", runWith(benchmarkMapAssignInt64, 1<<8, 1<<16)) 1045 b.Run("Str", runWith(benchmarkMapAssignStr, 1<<8, 1<<16)) 1046 } 1047 1048 func BenchmarkMapOperatorAssign(b *testing.B) { 1049 b.Run("Int32", runWith(benchmarkMapOperatorAssignInt32, 1<<8, 1<<16)) 1050 b.Run("Int64", runWith(benchmarkMapOperatorAssignInt64, 1<<8, 1<<16)) 1051 b.Run("Str", runWith(benchmarkMapOperatorAssignStr, 1<<8, 1<<16)) 1052 } 1053 1054 func BenchmarkMapAppendAssign(b *testing.B) { 1055 b.Run("Int32", runWith(benchmarkMapAppendAssignInt32, 1<<8, 1<<16)) 1056 b.Run("Int64", runWith(benchmarkMapAppendAssignInt64, 1<<8, 1<<16)) 1057 b.Run("Str", runWith(benchmarkMapAppendAssignStr, 1<<8, 1<<16)) 1058 } 1059 1060 func BenchmarkMapDelete(b *testing.B) { 1061 b.Run("Int32", runWith(benchmarkMapDeleteInt32, 100, 1000, 10000)) 1062 b.Run("Int64", runWith(benchmarkMapDeleteInt64, 100, 1000, 10000)) 1063 b.Run("Str", runWith(benchmarkMapDeleteStr, 100, 1000, 10000)) 1064 b.Run("Pointer", runWith(benchmarkMapDeletePointer, 100, 1000, 10000)) 1065 } 1066 1067 func TestDeferDeleteSlow(t *testing.T) { 1068 ks := []complex128{0, 1, 2, 3} 1069 1070 m := make(map[any]int) 1071 for i, k := range ks { 1072 m[k] = i 1073 } 1074 if len(m) != len(ks) { 1075 t.Errorf("want %d elements, got %d", len(ks), len(m)) 1076 } 1077 1078 func() { 1079 for _, k := range ks { 1080 defer delete(m, k) 1081 } 1082 }() 1083 if len(m) != 0 { 1084 t.Errorf("want 0 elements, got %d", len(m)) 1085 } 1086 } 1087 1088 // TestIncrementAfterDeleteValueInt and other test Issue 25936. 1089 // Value types int, int32, int64 are affected. Value type string 1090 // works as expected. 1091 func TestIncrementAfterDeleteValueInt(t *testing.T) { 1092 const key1 = 12 1093 const key2 = 13 1094 1095 m := make(map[int]int) 1096 m[key1] = 99 1097 delete(m, key1) 1098 m[key2]++ 1099 if n2 := m[key2]; n2 != 1 { 1100 t.Errorf("incremented 0 to %d", n2) 1101 } 1102 } 1103 1104 func TestIncrementAfterDeleteValueInt32(t *testing.T) { 1105 const key1 = 12 1106 const key2 = 13 1107 1108 m := make(map[int]int32) 1109 m[key1] = 99 1110 delete(m, key1) 1111 m[key2]++ 1112 if n2 := m[key2]; n2 != 1 { 1113 t.Errorf("incremented 0 to %d", n2) 1114 } 1115 } 1116 1117 func TestIncrementAfterDeleteValueInt64(t *testing.T) { 1118 const key1 = 12 1119 const key2 = 13 1120 1121 m := make(map[int]int64) 1122 m[key1] = 99 1123 delete(m, key1) 1124 m[key2]++ 1125 if n2 := m[key2]; n2 != 1 { 1126 t.Errorf("incremented 0 to %d", n2) 1127 } 1128 } 1129 1130 func TestIncrementAfterDeleteKeyStringValueInt(t *testing.T) { 1131 const key1 = "" 1132 const key2 = "x" 1133 1134 m := make(map[string]int) 1135 m[key1] = 99 1136 delete(m, key1) 1137 m[key2] += 1 1138 if n2 := m[key2]; n2 != 1 { 1139 t.Errorf("incremented 0 to %d", n2) 1140 } 1141 } 1142 1143 func TestIncrementAfterDeleteKeyValueString(t *testing.T) { 1144 const key1 = "" 1145 const key2 = "x" 1146 1147 m := make(map[string]string) 1148 m[key1] = "99" 1149 delete(m, key1) 1150 m[key2] += "1" 1151 if n2 := m[key2]; n2 != "1" { 1152 t.Errorf("appended '1' to empty (nil) string, got %s", n2) 1153 } 1154 } 1155 1156 // TestIncrementAfterBulkClearKeyStringValueInt tests that map bulk 1157 // deletion (mapclear) still works as expected. Note that it was not 1158 // affected by Issue 25936. 1159 func TestIncrementAfterBulkClearKeyStringValueInt(t *testing.T) { 1160 const key1 = "" 1161 const key2 = "x" 1162 1163 m := make(map[string]int) 1164 m[key1] = 99 1165 for k := range m { 1166 delete(m, k) 1167 } 1168 m[key2]++ 1169 if n2 := m[key2]; n2 != 1 { 1170 t.Errorf("incremented 0 to %d", n2) 1171 } 1172 } 1173 1174 func TestMapTombstones(t *testing.T) { 1175 m := map[int]int{} 1176 const N = 10000 1177 // Fill a map. 1178 for i := 0; i < N; i++ { 1179 m[i] = i 1180 } 1181 runtime.MapTombstoneCheck(m) 1182 // Delete half of the entries. 1183 for i := 0; i < N; i += 2 { 1184 delete(m, i) 1185 } 1186 runtime.MapTombstoneCheck(m) 1187 // Add new entries to fill in holes. 1188 for i := N; i < 3*N/2; i++ { 1189 m[i] = i 1190 } 1191 runtime.MapTombstoneCheck(m) 1192 // Delete everything. 1193 for i := 0; i < 3*N/2; i++ { 1194 delete(m, i) 1195 } 1196 runtime.MapTombstoneCheck(m) 1197 } 1198 1199 type canString int 1200 1201 func (c canString) String() string { 1202 return fmt.Sprintf("%d", int(c)) 1203 } 1204 1205 func TestMapInterfaceKey(t *testing.T) { 1206 // Test all the special cases in runtime.typehash. 1207 type GrabBag struct { 1208 f32 float32 1209 f64 float64 1210 c64 complex64 1211 c128 complex128 1212 s string 1213 i0 any 1214 i1 interface { 1215 String() string 1216 } 1217 a [4]string 1218 } 1219 1220 m := map[any]bool{} 1221 // Put a bunch of data in m, so that a bad hash is likely to 1222 // lead to a bad bucket, which will lead to a missed lookup. 1223 for i := 0; i < 1000; i++ { 1224 m[i] = true 1225 } 1226 m[GrabBag{f32: 1.0}] = true 1227 if !m[GrabBag{f32: 1.0}] { 1228 panic("f32 not found") 1229 } 1230 m[GrabBag{f64: 1.0}] = true 1231 if !m[GrabBag{f64: 1.0}] { 1232 panic("f64 not found") 1233 } 1234 m[GrabBag{c64: 1.0i}] = true 1235 if !m[GrabBag{c64: 1.0i}] { 1236 panic("c64 not found") 1237 } 1238 m[GrabBag{c128: 1.0i}] = true 1239 if !m[GrabBag{c128: 1.0i}] { 1240 panic("c128 not found") 1241 } 1242 m[GrabBag{s: "foo"}] = true 1243 if !m[GrabBag{s: "foo"}] { 1244 panic("string not found") 1245 } 1246 m[GrabBag{i0: "foo"}] = true 1247 if !m[GrabBag{i0: "foo"}] { 1248 panic("interface{} not found") 1249 } 1250 m[GrabBag{i1: canString(5)}] = true 1251 if !m[GrabBag{i1: canString(5)}] { 1252 panic("interface{String() string} not found") 1253 } 1254 m[GrabBag{a: [4]string{"foo", "bar", "baz", "bop"}}] = true 1255 if !m[GrabBag{a: [4]string{"foo", "bar", "baz", "bop"}}] { 1256 panic("array not found") 1257 } 1258 }