github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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, 100 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 var mapSink map[int]int 600 601 var mapBucketTests = [...]struct { 602 n int // n is the number of map elements 603 noescape int // number of expected buckets for non-escaping map 604 escape int // number of expected buckets for escaping map 605 }{ 606 {-(1 << 30), 1, 1}, 607 {-1, 1, 1}, 608 {0, 1, 1}, 609 {1, 1, 1}, 610 {8, 1, 1}, 611 {9, 2, 2}, 612 {13, 2, 2}, 613 {14, 4, 4}, 614 {26, 4, 4}, 615 } 616 617 func TestMapBuckets(t *testing.T) { 618 // Test that maps of different sizes have the right number of buckets. 619 // Non-escaping maps with small buckets (like map[int]int) never 620 // have a nil bucket pointer due to starting with preallocated buckets 621 // on the stack. Escaping maps start with a non-nil bucket pointer if 622 // hint size is above bucketCnt and thereby have more than one bucket. 623 // These tests depend on bucketCnt and loadFactor* in hashmap.go. 624 t.Run("mapliteral", func(t *testing.T) { 625 for _, tt := range mapBucketTests { 626 localMap := map[int]int{} 627 if runtime.MapBucketsPointerIsNil(localMap) { 628 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 629 } 630 for i := 0; i < tt.n; i++ { 631 localMap[i] = i 632 } 633 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 634 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 635 } 636 escapingMap := map[int]int{} 637 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 638 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 639 } 640 for i := 0; i < tt.n; i++ { 641 escapingMap[i] = i 642 } 643 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 644 t.Errorf("escape n=%d want %d buckets, got %d", tt.n, tt.escape, got) 645 } 646 mapSink = escapingMap 647 } 648 }) 649 t.Run("nohint", func(t *testing.T) { 650 for _, tt := range mapBucketTests { 651 localMap := make(map[int]int) 652 if runtime.MapBucketsPointerIsNil(localMap) { 653 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 654 } 655 for i := 0; i < tt.n; i++ { 656 localMap[i] = i 657 } 658 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 659 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 660 } 661 escapingMap := make(map[int]int) 662 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 663 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 664 } 665 for i := 0; i < tt.n; i++ { 666 escapingMap[i] = i 667 } 668 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 669 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) 670 } 671 mapSink = escapingMap 672 } 673 }) 674 t.Run("makemap", func(t *testing.T) { 675 for _, tt := range mapBucketTests { 676 localMap := make(map[int]int, tt.n) 677 if runtime.MapBucketsPointerIsNil(localMap) { 678 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 679 } 680 for i := 0; i < tt.n; i++ { 681 localMap[i] = i 682 } 683 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 684 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 685 } 686 escapingMap := make(map[int]int, tt.n) 687 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 688 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 689 } 690 for i := 0; i < tt.n; i++ { 691 escapingMap[i] = i 692 } 693 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 694 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) 695 } 696 mapSink = escapingMap 697 } 698 }) 699 t.Run("makemap64", func(t *testing.T) { 700 for _, tt := range mapBucketTests { 701 localMap := make(map[int]int, int64(tt.n)) 702 if runtime.MapBucketsPointerIsNil(localMap) { 703 t.Errorf("no escape: buckets pointer is nil for non-escaping map") 704 } 705 for i := 0; i < tt.n; i++ { 706 localMap[i] = i 707 } 708 if got := runtime.MapBucketsCount(localMap); got != tt.noescape { 709 t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) 710 } 711 escapingMap := make(map[int]int, tt.n) 712 if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { 713 t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) 714 } 715 for i := 0; i < tt.n; i++ { 716 escapingMap[i] = i 717 } 718 if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { 719 t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) 720 } 721 mapSink = escapingMap 722 } 723 }) 724 725 } 726 727 func benchmarkMapPop(b *testing.B, n int) { 728 m := map[int]int{} 729 for i := 0; i < b.N; i++ { 730 for j := 0; j < n; j++ { 731 m[j] = j 732 } 733 for j := 0; j < n; j++ { 734 // Use iterator to pop an element. 735 // We want this to be fast, see issue 8412. 736 for k := range m { 737 delete(m, k) 738 break 739 } 740 } 741 } 742 } 743 744 func BenchmarkMapPop100(b *testing.B) { benchmarkMapPop(b, 100) } 745 func BenchmarkMapPop1000(b *testing.B) { benchmarkMapPop(b, 1000) } 746 func BenchmarkMapPop10000(b *testing.B) { benchmarkMapPop(b, 10000) } 747 748 var testNonEscapingMapVariable int = 8 749 750 func TestNonEscapingMap(t *testing.T) { 751 n := testing.AllocsPerRun(1000, func() { 752 m := map[int]int{} 753 m[0] = 0 754 }) 755 if n != 0 { 756 t.Fatalf("mapliteral: want 0 allocs, got %v", n) 757 } 758 n = testing.AllocsPerRun(1000, func() { 759 m := make(map[int]int) 760 m[0] = 0 761 }) 762 if n != 0 { 763 t.Fatalf("no hint: want 0 allocs, got %v", n) 764 } 765 n = testing.AllocsPerRun(1000, func() { 766 m := make(map[int]int, 8) 767 m[0] = 0 768 }) 769 if n != 0 { 770 t.Fatalf("with small hint: want 0 allocs, got %v", n) 771 } 772 n = testing.AllocsPerRun(1000, func() { 773 m := make(map[int]int, testNonEscapingMapVariable) 774 m[0] = 0 775 }) 776 if n != 0 { 777 t.Fatalf("with variable hint: want 0 allocs, got %v", n) 778 } 779 780 } 781 782 func benchmarkMapAssignInt32(b *testing.B, n int) { 783 a := make(map[int32]int) 784 for i := 0; i < b.N; i++ { 785 a[int32(i&(n-1))] = i 786 } 787 } 788 789 func benchmarkMapDeleteInt32(b *testing.B, n int) { 790 a := make(map[int32]int, n) 791 b.ResetTimer() 792 for i := 0; i < b.N; i++ { 793 if len(a) == 0 { 794 b.StopTimer() 795 for j := i; j < i+n; j++ { 796 a[int32(j)] = j 797 } 798 b.StartTimer() 799 } 800 delete(a, int32(i)) 801 } 802 } 803 804 func benchmarkMapAssignInt64(b *testing.B, n int) { 805 a := make(map[int64]int) 806 for i := 0; i < b.N; i++ { 807 a[int64(i&(n-1))] = i 808 } 809 } 810 811 func benchmarkMapDeleteInt64(b *testing.B, n int) { 812 a := make(map[int64]int, n) 813 b.ResetTimer() 814 for i := 0; i < b.N; i++ { 815 if len(a) == 0 { 816 b.StopTimer() 817 for j := i; j < i+n; j++ { 818 a[int64(j)] = j 819 } 820 b.StartTimer() 821 } 822 delete(a, int64(i)) 823 } 824 } 825 826 func benchmarkMapAssignStr(b *testing.B, n int) { 827 k := make([]string, n) 828 for i := 0; i < len(k); i++ { 829 k[i] = strconv.Itoa(i) 830 } 831 b.ResetTimer() 832 a := make(map[string]int) 833 for i := 0; i < b.N; i++ { 834 a[k[i&(n-1)]] = i 835 } 836 } 837 838 func benchmarkMapDeleteStr(b *testing.B, n int) { 839 i2s := make([]string, n) 840 for i := 0; i < n; i++ { 841 i2s[i] = strconv.Itoa(i) 842 } 843 a := make(map[string]int, n) 844 b.ResetTimer() 845 k := 0 846 for i := 0; i < b.N; i++ { 847 if len(a) == 0 { 848 b.StopTimer() 849 for j := 0; j < n; j++ { 850 a[i2s[j]] = j 851 } 852 k = i 853 b.StartTimer() 854 } 855 delete(a, i2s[i-k]) 856 } 857 } 858 859 func runWith(f func(*testing.B, int), v ...int) func(*testing.B) { 860 return func(b *testing.B) { 861 for _, n := range v { 862 b.Run(strconv.Itoa(n), func(b *testing.B) { f(b, n) }) 863 } 864 } 865 } 866 867 func BenchmarkMapAssign(b *testing.B) { 868 b.Run("Int32", runWith(benchmarkMapAssignInt32, 1<<8, 1<<16)) 869 b.Run("Int64", runWith(benchmarkMapAssignInt64, 1<<8, 1<<16)) 870 b.Run("Str", runWith(benchmarkMapAssignStr, 1<<8, 1<<16)) 871 } 872 873 func BenchmarkMapDelete(b *testing.B) { 874 b.Run("Int32", runWith(benchmarkMapDeleteInt32, 100, 1000, 10000)) 875 b.Run("Int64", runWith(benchmarkMapDeleteInt64, 100, 1000, 10000)) 876 b.Run("Str", runWith(benchmarkMapDeleteStr, 100, 1000, 10000)) 877 }