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