Home | History | Annotate | Download | only in runtime
      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 }
    878