github.com/eun/go@v0.0.0-20170811110501-92cfd07a6cfd/src/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 "math" 10 "reflect" 11 "runtime" 12 "sort" 13 "strconv" 14 "strings" 15 "sync" 16 "testing" 17 ) 18 19 // negative zero is a good test because: 20 // 1) 0 and -0 are equal, yet have distinct representations. 21 // 2) 0 is represented as all zeros, -0 isn't. 22 // I'm not sure the language spec actually requires this behavior, 23 // but it's what the current map implementation does. 24 func TestNegativeZero(t *testing.T) { 25 m := make(map[float64]bool, 0) 26 27 m[+0.0] = true 28 m[math.Copysign(0.0, -1.0)] = true // should overwrite +0 entry 29 30 if len(m) != 1 { 31 t.Error("length wrong") 32 } 33 34 for k := range m { 35 if math.Copysign(1.0, k) > 0 { 36 t.Error("wrong sign") 37 } 38 } 39 40 m = make(map[float64]bool, 0) 41 m[math.Copysign(0.0, -1.0)] = true 42 m[+0.0] = true // should overwrite -0.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 55 // nan is a good test because nan != nan, and nan has 56 // a randomized hash value. 57 func TestNan(t *testing.T) { 58 m := make(map[float64]int, 0) 59 nan := math.NaN() 60 m[nan] = 1 61 m[nan] = 2 62 m[nan] = 4 63 if len(m) != 3 { 64 t.Error("length wrong") 65 } 66 s := 0 67 for k, v := range m { 68 if k == k { 69 t.Error("nan disappeared") 70 } 71 if (v & (v - 1)) != 0 { 72 t.Error("value wrong") 73 } 74 s |= v 75 } 76 if s != 7 { 77 t.Error("values wrong") 78 } 79 } 80 81 // Maps aren't actually copied on assignment. 82 func TestAlias(t *testing.T) { 83 m := make(map[int]int, 0) 84 m[0] = 5 85 n := m 86 n[0] = 6 87 if m[0] != 6 { 88 t.Error("alias didn't work") 89 } 90 } 91 92 func TestGrowWithNaN(t *testing.T) { 93 m := make(map[float64]int, 4) 94 nan := math.NaN() 95 m[nan] = 1 96 m[nan] = 2 97 m[nan] = 4 98 cnt := 0 99 s := 0 100 growflag := true 101 for k, v := range m { 102 if growflag { 103 // force a hashtable resize 104 for i := 0; i < 100; i++ { 105 m[float64(i)] = i 106 } 107 growflag = false 108 } 109 if k != k { 110 cnt++ 111 s |= v 112 } 113 } 114 if cnt != 3 { 115 t.Error("NaN keys lost during grow") 116 } 117 if s != 7 { 118 t.Error("NaN values lost during grow") 119 } 120 } 121 122 type FloatInt struct { 123 x float64 124 y int 125 } 126 127 func TestGrowWithNegativeZero(t *testing.T) { 128 negzero := math.Copysign(0.0, -1.0) 129 m := make(map[FloatInt]int, 4) 130 m[FloatInt{0.0, 0}] = 1 131 m[FloatInt{0.0, 1}] = 2 132 m[FloatInt{0.0, 2}] = 4 133 m[FloatInt{0.0, 3}] = 8 134 growflag := true 135 s := 0 136 cnt := 0 137 negcnt := 0 138 // The first iteration should return the +0 key. 139 // The subsequent iterations should return the -0 key. 140 // I'm not really sure this is required by the spec, 141 // but it makes sense. 142 // TODO: are we allowed to get the first entry returned again??? 143 for k, v := range m { 144 if v == 0 { 145 continue 146 } // ignore entries added to grow table 147 cnt++ 148 if math.Copysign(1.0, k.x) < 0 { 149 if v&16 == 0 { 150 t.Error("key/value not updated together 1") 151 } 152 negcnt++ 153 s |= v & 15 154 } else { 155 if v&16 == 16 { 156 t.Error("key/value not updated together 2", k, v) 157 } 158 s |= v 159 } 160 if growflag { 161 // force a hashtable resize 162 for i := 0; i < 100; i++ { 163 m[FloatInt{3.0, i}] = 0 164 } 165 // then change all the entries 166 // to negative zero 167 m[FloatInt{negzero, 0}] = 1 | 16 168 m[FloatInt{negzero, 1}] = 2 | 16 169 m[FloatInt{negzero, 2}] = 4 | 16 170 m[FloatInt{negzero, 3}] = 8 | 16 171 growflag = false 172 } 173 } 174 if s != 15 { 175 t.Error("entry missing", s) 176 } 177 if cnt != 4 { 178 t.Error("wrong number of entries returned by iterator", cnt) 179 } 180 if negcnt != 3 { 181 t.Error("update to negzero missed by iteration", negcnt) 182 } 183 } 184 185 func TestIterGrowAndDelete(t *testing.T) { 186 m := make(map[int]int, 4) 187 for i := 0; i < 100; i++ { 188 m[i] = i 189 } 190 growflag := true 191 for k := range m { 192 if growflag { 193 // grow the table 194 for i := 100; i < 1000; i++ { 195 m[i] = i 196 } 197 // delete all odd keys 198 for i := 1; i < 1000; i += 2 { 199 delete(m, i) 200 } 201 growflag = false 202 } else { 203 if k&1 == 1 { 204 t.Error("odd value returned") 205 } 206 } 207 } 208 } 209 210 // make sure old bucket arrays don't get GCd while 211 // an iterator is still using them. 212 func TestIterGrowWithGC(t *testing.T) { 213 m := make(map[int]int, 4) 214 for i := 0; i < 16; i++ { 215 m[i] = i 216 } 217 growflag := true 218 bitmask := 0 219 for k := range m { 220 if k < 16 { 221 bitmask |= 1 << uint(k) 222 } 223 if growflag { 224 // grow the table 225 for i := 100; i < 1000; i++ { 226 m[i] = i 227 } 228 // trigger a gc 229 runtime.GC() 230 growflag = false 231 } 232 } 233 if bitmask != 1<<16-1 { 234 t.Error("missing key", bitmask) 235 } 236 } 237 238 func testConcurrentReadsAfterGrowth(t *testing.T, useReflect bool) { 239 t.Parallel() 240 if runtime.GOMAXPROCS(-1) == 1 { 241 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16)) 242 } 243 numLoop := 10 244 numGrowStep := 250 245 numReader := 16 246 if testing.Short() { 247 numLoop, numGrowStep = 2, 500 248 } 249 for i := 0; i < numLoop; i++ { 250 m := make(map[int]int, 0) 251 for gs := 0; gs < numGrowStep; gs++ { 252 m[gs] = gs 253 var wg sync.WaitGroup 254 wg.Add(numReader * 2) 255 for nr := 0; nr < numReader; nr++ { 256 go func() { 257 defer wg.Done() 258 for range m { 259 } 260 }() 261 go func() { 262 defer wg.Done() 263 for key := 0; key < gs; key++ { 264 _ = m[key] 265 } 266 }() 267 if useReflect { 268 wg.Add(1) 269 go func() { 270 defer wg.Done() 271 mv := reflect.ValueOf(m) 272 keys := mv.MapKeys() 273 for _, k := range keys { 274 mv.MapIndex(k) 275 } 276 }() 277 } 278 } 279 wg.Wait() 280 } 281 } 282 } 283 284 func TestConcurrentReadsAfterGrowth(t *testing.T) { 285 testConcurrentReadsAfterGrowth(t, false) 286 } 287 288 func TestConcurrentReadsAfterGrowthReflect(t *testing.T) { 289 testConcurrentReadsAfterGrowth(t, true) 290 } 291 292 func TestBigItems(t *testing.T) { 293 var key [256]string 294 for i := 0; i < 256; i++ { 295 key[i] = "foo" 296 } 297 m := make(map[[256]string][256]string, 4) 298 for i := 0; i < 100; i++ { 299 key[37] = fmt.Sprintf("string%02d", i) 300 m[key] = key 301 } 302 var keys [100]string 303 var values [100]string 304 i := 0 305 for k, v := range m { 306 keys[i] = k[37] 307 values[i] = v[37] 308 i++ 309 } 310 sort.Strings(keys[:]) 311 sort.Strings(values[:]) 312 for i := 0; i < 100; i++ { 313 if keys[i] != fmt.Sprintf("string%02d", i) { 314 t.Errorf("#%d: missing key: %v", i, keys[i]) 315 } 316 if values[i] != fmt.Sprintf("string%02d", i) { 317 t.Errorf("#%d: missing value: %v", i, values[i]) 318 } 319 } 320 } 321 322 func TestMapHugeZero(t *testing.T) { 323 type T [4000]byte 324 m := map[int]T{} 325 x := m[0] 326 if x != (T{}) { 327 t.Errorf("map value not zero") 328 } 329 y, ok := m[0] 330 if ok { 331 t.Errorf("map value should be missing") 332 } 333 if y != (T{}) { 334 t.Errorf("map value not zero") 335 } 336 } 337 338 type empty struct { 339 } 340 341 func TestEmptyKeyAndValue(t *testing.T) { 342 a := make(map[int]empty, 4) 343 b := make(map[empty]int, 4) 344 c := make(map[empty]empty, 4) 345 a[0] = empty{} 346 b[empty{}] = 0 347 b[empty{}] = 1 348 c[empty{}] = empty{} 349 350 if len(a) != 1 { 351 t.Errorf("empty value insert problem") 352 } 353 if b[empty{}] != 1 { 354 t.Errorf("empty key returned wrong value") 355 } 356 } 357 358 // Tests a map with a single bucket, with same-lengthed short keys 359 // ("quick keys") as well as long keys. 360 func TestSingleBucketMapStringKeys_DupLen(t *testing.T) { 361 testMapLookups(t, map[string]string{ 362 "x": "x1val", 363 "xx": "x2val", 364 "foo": "fooval", 365 "bar": "barval", // same key length as "foo" 366 "xxxx": "x4val", 367 strings.Repeat("x", 128): "longval1", 368 strings.Repeat("y", 128): "longval2", 369 }) 370 } 371 372 // Tests a map with a single bucket, with all keys having different lengths. 373 func TestSingleBucketMapStringKeys_NoDupLen(t *testing.T) { 374 testMapLookups(t, map[string]string{ 375 "x": "x1val", 376 "xx": "x2val", 377 "foo": "fooval", 378 "xxxx": "x4val", 379 "xxxxx": "x5val", 380 "xxxxxx": "x6val", 381 strings.Repeat("x", 128): "longval", 382 }) 383 } 384 385 func testMapLookups(t *testing.T, m map[string]string) { 386 for k, v := range m { 387 if m[k] != v { 388 t.Fatalf("m[%q] = %q; want %q", k, m[k], v) 389 } 390 } 391 } 392 393 // Tests whether the iterator returns the right elements when 394 // started in the middle of a grow, when the keys are NaNs. 395 func TestMapNanGrowIterator(t *testing.T) { 396 m := make(map[float64]int) 397 nan := math.NaN() 398 const nBuckets = 16 399 // To fill nBuckets buckets takes LOAD * nBuckets keys. 400 nKeys := int(nBuckets * *runtime.HashLoad) 401 402 // Get map to full point with nan keys. 403 for i := 0; i < nKeys; i++ { 404 m[nan] = i 405 } 406 // Trigger grow 407 m[1.0] = 1 408 delete(m, 1.0) 409 410 // Run iterator 411 found := make(map[int]struct{}) 412 for _, v := range m { 413 if v != -1 { 414 if _, repeat := found[v]; repeat { 415 t.Fatalf("repeat of value %d", v) 416 } 417 found[v] = struct{}{} 418 } 419 if len(found) == nKeys/2 { 420 // Halfway through iteration, finish grow. 421 for i := 0; i < nBuckets; i++ { 422 delete(m, 1.0) 423 } 424 } 425 } 426 if len(found) != nKeys { 427 t.Fatalf("missing value") 428 } 429 } 430 431 func TestMapIterOrder(t *testing.T) { 432 for _, n := range [...]int{3, 7, 9, 15} { 433 for i := 0; i < 1000; i++ { 434 // Make m be {0: true, 1: true, ..., n-1: true}. 435 m := make(map[int]bool) 436 for i := 0; i < n; i++ { 437 m[i] = true 438 } 439 // Check that iterating over the map produces at least two different orderings. 440 ord := func() []int { 441 var s []int 442 for key := range m { 443 s = append(s, key) 444 } 445 return s 446 } 447 first := ord() 448 ok := false 449 for try := 0; try < 100; try++ { 450 if !reflect.DeepEqual(first, ord()) { 451 ok = true 452 break 453 } 454 } 455 if !ok { 456 t.Errorf("Map with n=%d elements had consistent iteration order: %v", n, first) 457 break 458 } 459 } 460 } 461 } 462 463 // Issue 8410 464 func TestMapSparseIterOrder(t *testing.T) { 465 // Run several rounds to increase the probability 466 // of failure. One is not enough. 467 NextRound: 468 for round := 0; round < 10; round++ { 469 m := make(map[int]bool) 470 // Add 1000 items, remove 980. 471 for i := 0; i < 1000; i++ { 472 m[i] = true 473 } 474 for i := 20; i < 1000; i++ { 475 delete(m, i) 476 } 477 478 var first []int 479 for i := range m { 480 first = append(first, i) 481 } 482 483 // 800 chances to get a different iteration order. 484 // See bug 8736 for why we need so many tries. 485 for n := 0; n < 800; n++ { 486 idx := 0 487 for i := range m { 488 if i != first[idx] { 489 // iteration order changed. 490 continue NextRound 491 } 492 idx++ 493 } 494 } 495 t.Fatalf("constant iteration order on round %d: %v", round, first) 496 } 497 } 498 499 func TestMapStringBytesLookup(t *testing.T) { 500 // Use large string keys to avoid small-allocation coalescing, 501 // which can cause AllocsPerRun to report lower counts than it should. 502 m := map[string]int{ 503 "1000000000000000000000000000000000000000000000000": 1, 504 "2000000000000000000000000000000000000000000000000": 2, 505 } 506 buf := []byte("1000000000000000000000000000000000000000000000000") 507 if x := m[string(buf)]; x != 1 { 508 t.Errorf(`m[string([]byte("1"))] = %d, want 1`, x) 509 } 510 buf[0] = '2' 511 if x := m[string(buf)]; x != 2 { 512 t.Errorf(`m[string([]byte("2"))] = %d, want 2`, x) 513 } 514 515 var x int 516 n := testing.AllocsPerRun(100, func() { 517 x += m[string(buf)] 518 }) 519 if n != 0 { 520 t.Errorf("AllocsPerRun for m[string(buf)] = %v, want 0", n) 521 } 522 523 x = 0 524 n = testing.AllocsPerRun(100, func() { 525 y, ok := m[string(buf)] 526 if !ok { 527 panic("!ok") 528 } 529 x += y 530 }) 531 if n != 0 { 532 t.Errorf("AllocsPerRun for x,ok = m[string(buf)] = %v, want 0", n) 533 } 534 } 535 536 func TestMapLargeKeyNoPointer(t *testing.T) { 537 const ( 538 I = 1000 539 N = 64 540 ) 541 type T [N]int 542 m := make(map[T]int) 543 for i := 0; i < I; i++ { 544 var v T 545 for j := 0; j < N; j++ { 546 v[j] = i + j 547 } 548 m[v] = i 549 } 550 runtime.GC() 551 for i := 0; i < I; i++ { 552 var v T 553 for j := 0; j < N; j++ { 554 v[j] = i + j 555 } 556 if m[v] != i { 557 t.Fatalf("corrupted map: want %+v, got %+v", i, m[v]) 558 } 559 } 560 } 561 562 func TestMapLargeValNoPointer(t *testing.T) { 563 const ( 564 I = 1000 565 N = 64 566 ) 567 type T [N]int 568 m := make(map[int]T) 569 for i := 0; i < I; i++ { 570 var v T 571 for j := 0; j < N; j++ { 572 v[j] = i + j 573 } 574 m[i] = v 575 } 576 runtime.GC() 577 for i := 0; i < I; i++ { 578 var v T 579 for j := 0; j < N; j++ { 580 v[j] = i + j 581 } 582 v1 := m[i] 583 for j := 0; j < N; j++ { 584 if v1[j] != v[j] { 585 t.Fatalf("corrupted map: want %+v, got %+v", v, v1) 586 } 587 } 588 } 589 } 590 591 // Test that making a map with a large or invalid hint 592 // doesn't panic. (Issue 19926). 593 func TestIgnoreBogusMapHint(t *testing.T) { 594 for _, hint := range []int64{-1, 1 << 62} { 595 _ = make(map[int]int, hint) 596 } 597 } 598 599 func benchmarkMapPop(b *testing.B, n int) { 600 m := map[int]int{} 601 for i := 0; i < b.N; i++ { 602 for j := 0; j < n; j++ { 603 m[j] = j 604 } 605 for j := 0; j < n; j++ { 606 // Use iterator to pop an element. 607 // We want this to be fast, see issue 8412. 608 for k := range m { 609 delete(m, k) 610 break 611 } 612 } 613 } 614 } 615 616 func BenchmarkMapPop100(b *testing.B) { benchmarkMapPop(b, 100) } 617 func BenchmarkMapPop1000(b *testing.B) { benchmarkMapPop(b, 1000) } 618 func BenchmarkMapPop10000(b *testing.B) { benchmarkMapPop(b, 10000) } 619 620 func TestNonEscapingMap(t *testing.T) { 621 n := testing.AllocsPerRun(1000, func() { 622 m := make(map[int]int) 623 m[0] = 0 624 }) 625 if n != 0 { 626 t.Fatalf("want 0 allocs, got %v", n) 627 } 628 } 629 630 func benchmarkMapAssignInt32(b *testing.B, n int) { 631 a := make(map[int32]int) 632 for i := 0; i < b.N; i++ { 633 a[int32(i&(n-1))] = i 634 } 635 } 636 637 func benchmarkMapDeleteInt32(b *testing.B, n int) { 638 a := make(map[int32]int) 639 for i := 0; i < n*b.N; i++ { 640 a[int32(i)] = i 641 } 642 b.ResetTimer() 643 for i := 0; i < n*b.N; i = i + n { 644 delete(a, int32(i)) 645 } 646 } 647 648 func benchmarkMapAssignInt64(b *testing.B, n int) { 649 a := make(map[int64]int) 650 for i := 0; i < b.N; i++ { 651 a[int64(i&(n-1))] = i 652 } 653 } 654 655 func benchmarkMapDeleteInt64(b *testing.B, n int) { 656 a := make(map[int64]int) 657 for i := 0; i < n*b.N; i++ { 658 a[int64(i)] = i 659 } 660 b.ResetTimer() 661 for i := 0; i < n*b.N; i = i + n { 662 delete(a, int64(i)) 663 } 664 } 665 666 func benchmarkMapAssignStr(b *testing.B, n int) { 667 k := make([]string, n) 668 for i := 0; i < len(k); i++ { 669 k[i] = strconv.Itoa(i) 670 } 671 b.ResetTimer() 672 a := make(map[string]int) 673 for i := 0; i < b.N; i++ { 674 a[k[i&(n-1)]] = i 675 } 676 } 677 678 func benchmarkMapDeleteStr(b *testing.B, n int) { 679 k := make([]string, n*b.N) 680 for i := 0; i < n*b.N; i++ { 681 k[i] = strconv.Itoa(i) 682 } 683 a := make(map[string]int) 684 for i := 0; i < n*b.N; i++ { 685 a[k[i]] = i 686 } 687 b.ResetTimer() 688 for i := 0; i < n*b.N; i = i + n { 689 delete(a, k[i]) 690 } 691 } 692 693 func runWith(f func(*testing.B, int), v ...int) func(*testing.B) { 694 return func(b *testing.B) { 695 for _, n := range v { 696 b.Run(strconv.Itoa(n), func(b *testing.B) { f(b, n) }) 697 } 698 } 699 } 700 701 func BenchmarkMapAssign(b *testing.B) { 702 b.Run("Int32", runWith(benchmarkMapAssignInt32, 1<<8, 1<<16)) 703 b.Run("Int64", runWith(benchmarkMapAssignInt64, 1<<8, 1<<16)) 704 b.Run("Str", runWith(benchmarkMapAssignStr, 1<<8, 1<<16)) 705 } 706 707 func BenchmarkMapDelete(b *testing.B) { 708 b.Run("Int32", runWith(benchmarkMapDeleteInt32, 1, 2, 4)) 709 b.Run("Int64", runWith(benchmarkMapDeleteInt64, 1, 2, 4)) 710 b.Run("Str", runWith(benchmarkMapDeleteStr, 1, 2, 4)) 711 }