github.com/puzpuzpuz/xsync/v2@v2.5.2-0.20231021165734-92b8269e19a9/map_test.go (about)

     1  package xsync_test
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"math/bits"
     7  	"math/rand"
     8  	"strconv"
     9  	"sync"
    10  	"sync/atomic"
    11  	"testing"
    12  	"time"
    13  	"unsafe"
    14  
    15  	. "github.com/puzpuzpuz/xsync/v2"
    16  )
    17  
    18  const (
    19  	// number of entries to use in benchmarks
    20  	benchmarkNumEntries = 1_000
    21  	// key prefix used in benchmarks
    22  	benchmarkKeyPrefix = "what_a_looooooooooooooooooooooong_key_prefix_"
    23  )
    24  
    25  var benchmarkCases = []struct {
    26  	name           string
    27  	readPercentage int
    28  }{
    29  	{"reads=100%", 100}, // 100% loads,    0% stores,    0% deletes
    30  	{"reads=99%", 99},   //  99% loads,  0.5% stores,  0.5% deletes
    31  	{"reads=90%", 90},   //  90% loads,    5% stores,    5% deletes
    32  	{"reads=75%", 75},   //  75% loads, 12.5% stores, 12.5% deletes
    33  }
    34  
    35  var benchmarkKeys []string
    36  
    37  func init() {
    38  	benchmarkKeys = make([]string, benchmarkNumEntries)
    39  	for i := 0; i < benchmarkNumEntries; i++ {
    40  		benchmarkKeys[i] = benchmarkKeyPrefix + strconv.Itoa(i)
    41  	}
    42  }
    43  
    44  func runParallel(b *testing.B, benchFn func(pb *testing.PB)) {
    45  	b.ResetTimer()
    46  	start := time.Now()
    47  	b.RunParallel(benchFn)
    48  	opsPerSec := float64(b.N) / float64(time.Since(start).Seconds())
    49  	b.ReportMetric(opsPerSec, "ops/s")
    50  }
    51  
    52  func TestMap_BucketStructSize(t *testing.T) {
    53  	size := unsafe.Sizeof(BucketPadded{})
    54  	if size != 64 {
    55  		t.Fatalf("size of 64B (one cache line) is expected, got: %d", size)
    56  	}
    57  }
    58  
    59  func TestMap_UniqueValuePointers_Int(t *testing.T) {
    60  	EnableAssertions()
    61  	m := NewMap()
    62  	v := 42
    63  	m.Store("foo", v)
    64  	m.Store("foo", v)
    65  	DisableAssertions()
    66  }
    67  
    68  func TestMap_UniqueValuePointers_Struct(t *testing.T) {
    69  	type foo struct{}
    70  	EnableAssertions()
    71  	m := NewMap()
    72  	v := foo{}
    73  	m.Store("foo", v)
    74  	m.Store("foo", v)
    75  	DisableAssertions()
    76  }
    77  
    78  func TestMap_UniqueValuePointers_Pointer(t *testing.T) {
    79  	type foo struct{}
    80  	EnableAssertions()
    81  	m := NewMap()
    82  	v := &foo{}
    83  	m.Store("foo", v)
    84  	m.Store("foo", v)
    85  	DisableAssertions()
    86  }
    87  
    88  func TestMap_UniqueValuePointers_Slice(t *testing.T) {
    89  	EnableAssertions()
    90  	m := NewMap()
    91  	v := make([]int, 13)
    92  	m.Store("foo", v)
    93  	m.Store("foo", v)
    94  	DisableAssertions()
    95  }
    96  
    97  func TestMap_UniqueValuePointers_String(t *testing.T) {
    98  	EnableAssertions()
    99  	m := NewMap()
   100  	v := "bar"
   101  	m.Store("foo", v)
   102  	m.Store("foo", v)
   103  	DisableAssertions()
   104  }
   105  
   106  func TestMap_UniqueValuePointers_Nil(t *testing.T) {
   107  	EnableAssertions()
   108  	m := NewMap()
   109  	m.Store("foo", nil)
   110  	m.Store("foo", nil)
   111  	DisableAssertions()
   112  }
   113  
   114  func TestMap_MissingEntry(t *testing.T) {
   115  	m := NewMap()
   116  	v, ok := m.Load("foo")
   117  	if ok {
   118  		t.Fatalf("value was not expected: %v", v)
   119  	}
   120  	if deleted, loaded := m.LoadAndDelete("foo"); loaded {
   121  		t.Fatalf("value was not expected %v", deleted)
   122  	}
   123  	if actual, loaded := m.LoadOrStore("foo", "bar"); loaded {
   124  		t.Fatalf("value was not expected %v", actual)
   125  	}
   126  }
   127  
   128  func TestMap_EmptyStringKey(t *testing.T) {
   129  	m := NewMap()
   130  	m.Store("", "foobar")
   131  	v, ok := m.Load("")
   132  	if !ok {
   133  		t.Fatal("value was expected")
   134  	}
   135  	if vs, ok := v.(string); ok && vs != "foobar" {
   136  		t.Fatalf("value does not match: %v", v)
   137  	}
   138  }
   139  
   140  func TestMapStore_NilValue(t *testing.T) {
   141  	m := NewMap()
   142  	m.Store("foo", nil)
   143  	v, ok := m.Load("foo")
   144  	if !ok {
   145  		t.Fatal("nil value was expected")
   146  	}
   147  	if v != nil {
   148  		t.Fatalf("value was not nil: %v", v)
   149  	}
   150  }
   151  
   152  func TestMapLoadOrStore_NilValue(t *testing.T) {
   153  	m := NewMap()
   154  	m.LoadOrStore("foo", nil)
   155  	v, loaded := m.LoadOrStore("foo", nil)
   156  	if !loaded {
   157  		t.Fatal("nil value was expected")
   158  	}
   159  	if v != nil {
   160  		t.Fatalf("value was not nil: %v", v)
   161  	}
   162  }
   163  
   164  func TestMapLoadOrStore_NonNilValue(t *testing.T) {
   165  	type foo struct{}
   166  	m := NewMap()
   167  	newv := &foo{}
   168  	v, loaded := m.LoadOrStore("foo", newv)
   169  	if loaded {
   170  		t.Fatal("no value was expected")
   171  	}
   172  	if v != newv {
   173  		t.Fatalf("value does not match: %v", v)
   174  	}
   175  	newv2 := &foo{}
   176  	v, loaded = m.LoadOrStore("foo", newv2)
   177  	if !loaded {
   178  		t.Fatal("value was expected")
   179  	}
   180  	if v != newv {
   181  		t.Fatalf("value does not match: %v", v)
   182  	}
   183  }
   184  
   185  func TestMapLoadAndStore_NilValue(t *testing.T) {
   186  	m := NewMap()
   187  	m.LoadAndStore("foo", nil)
   188  	v, loaded := m.LoadAndStore("foo", nil)
   189  	if !loaded {
   190  		t.Fatal("nil value was expected")
   191  	}
   192  	if v != nil {
   193  		t.Fatalf("value was not nil: %v", v)
   194  	}
   195  	v, loaded = m.Load("foo")
   196  	if !loaded {
   197  		t.Fatal("nil value was expected")
   198  	}
   199  	if v != nil {
   200  		t.Fatalf("value was not nil: %v", v)
   201  	}
   202  }
   203  
   204  func TestMapLoadAndStore_NonNilValue(t *testing.T) {
   205  	type foo struct{}
   206  	m := NewMap()
   207  	v1 := &foo{}
   208  	v, loaded := m.LoadAndStore("foo", v1)
   209  	if loaded {
   210  		t.Fatal("no value was expected")
   211  	}
   212  	if v != v1 {
   213  		t.Fatalf("value does not match: %v", v)
   214  	}
   215  	v2 := 2
   216  	v, loaded = m.LoadAndStore("foo", v2)
   217  	if !loaded {
   218  		t.Fatal("value was expected")
   219  	}
   220  	if v != v1 {
   221  		t.Fatalf("value does not match: %v", v)
   222  	}
   223  	v, loaded = m.Load("foo")
   224  	if !loaded {
   225  		t.Fatal("value was expected")
   226  	}
   227  	if v != v2 {
   228  		t.Fatalf("value does not match: %v", v)
   229  	}
   230  }
   231  
   232  func TestMapRange(t *testing.T) {
   233  	const numEntries = 1000
   234  	m := NewMap()
   235  	for i := 0; i < numEntries; i++ {
   236  		m.Store(strconv.Itoa(i), i)
   237  	}
   238  	iters := 0
   239  	met := make(map[string]int)
   240  	m.Range(func(key string, value interface{}) bool {
   241  		if key != strconv.Itoa(value.(int)) {
   242  			t.Fatalf("got unexpected key/value for iteration %d: %v/%v", iters, key, value)
   243  			return false
   244  		}
   245  		met[key] += 1
   246  		iters++
   247  		return true
   248  	})
   249  	if iters != numEntries {
   250  		t.Fatalf("got unexpected number of iterations: %d", iters)
   251  	}
   252  	for i := 0; i < numEntries; i++ {
   253  		if c := met[strconv.Itoa(i)]; c != 1 {
   254  			t.Fatalf("met key %d multiple times: %d", i, c)
   255  		}
   256  	}
   257  }
   258  
   259  func TestMapRange_FalseReturned(t *testing.T) {
   260  	m := NewMap()
   261  	for i := 0; i < 100; i++ {
   262  		m.Store(strconv.Itoa(i), i)
   263  	}
   264  	iters := 0
   265  	m.Range(func(key string, value interface{}) bool {
   266  		iters++
   267  		return iters != 13
   268  	})
   269  	if iters != 13 {
   270  		t.Fatalf("got unexpected number of iterations: %d", iters)
   271  	}
   272  }
   273  
   274  func TestMapRange_NestedDelete(t *testing.T) {
   275  	const numEntries = 256
   276  	m := NewMap()
   277  	for i := 0; i < numEntries; i++ {
   278  		m.Store(strconv.Itoa(i), i)
   279  	}
   280  	m.Range(func(key string, value interface{}) bool {
   281  		m.Delete(key)
   282  		return true
   283  	})
   284  	for i := 0; i < numEntries; i++ {
   285  		if _, ok := m.Load(strconv.Itoa(i)); ok {
   286  			t.Fatalf("value found for %d", i)
   287  		}
   288  	}
   289  }
   290  
   291  func TestMapStore(t *testing.T) {
   292  	const numEntries = 128
   293  	m := NewMap()
   294  	for i := 0; i < numEntries; i++ {
   295  		m.Store(strconv.Itoa(i), i)
   296  	}
   297  	for i := 0; i < numEntries; i++ {
   298  		v, ok := m.Load(strconv.Itoa(i))
   299  		if !ok {
   300  			t.Fatalf("value not found for %d", i)
   301  		}
   302  		if vi, ok := v.(int); ok && vi != i {
   303  			t.Fatalf("values do not match for %d: %v", i, v)
   304  		}
   305  	}
   306  }
   307  
   308  func TestMapLoadOrStore(t *testing.T) {
   309  	const numEntries = 1000
   310  	m := NewMap()
   311  	for i := 0; i < numEntries; i++ {
   312  		m.Store(strconv.Itoa(i), i)
   313  	}
   314  	for i := 0; i < numEntries; i++ {
   315  		if _, loaded := m.LoadOrStore(strconv.Itoa(i), i); !loaded {
   316  			t.Fatalf("value not found for %d", i)
   317  		}
   318  	}
   319  }
   320  
   321  func TestMapLoadOrCompute(t *testing.T) {
   322  	const numEntries = 1000
   323  	m := NewMap()
   324  	for i := 0; i < numEntries; i++ {
   325  		v, loaded := m.LoadOrCompute(strconv.Itoa(i), func() interface{} {
   326  			return i
   327  		})
   328  		if loaded {
   329  			t.Fatalf("value not computed for %d", i)
   330  		}
   331  		if vi, ok := v.(int); ok && vi != i {
   332  			t.Fatalf("values do not match for %d: %v", i, v)
   333  		}
   334  	}
   335  	for i := 0; i < numEntries; i++ {
   336  		v, loaded := m.LoadOrCompute(strconv.Itoa(i), func() interface{} {
   337  			return i
   338  		})
   339  		if !loaded {
   340  			t.Fatalf("value not loaded for %d", i)
   341  		}
   342  		if vi, ok := v.(int); ok && vi != i {
   343  			t.Fatalf("values do not match for %d: %v", i, v)
   344  		}
   345  	}
   346  }
   347  
   348  func TestMapLoadOrCompute_FunctionCalledOnce(t *testing.T) {
   349  	m := NewMap()
   350  	for i := 0; i < 100; {
   351  		m.LoadOrCompute(strconv.Itoa(i), func() (v interface{}) {
   352  			v, i = i, i+1
   353  			return v
   354  		})
   355  	}
   356  
   357  	m.Range(func(k string, v interface{}) bool {
   358  		if vi, ok := v.(int); !ok || strconv.Itoa(vi) != k {
   359  			t.Fatalf("%sth key is not equal to value %d", k, v)
   360  		}
   361  		return true
   362  	})
   363  }
   364  
   365  func TestMapCompute(t *testing.T) {
   366  	var zeroedV interface{}
   367  	m := NewMap()
   368  	// Store a new value.
   369  	v, ok := m.Compute("foobar", func(oldValue interface{}, loaded bool) (newValue interface{}, delete bool) {
   370  		if oldValue != zeroedV {
   371  			t.Fatalf("oldValue should be empty interface{} when computing a new value: %d", oldValue)
   372  		}
   373  		if loaded {
   374  			t.Fatal("loaded should be false when computing a new value")
   375  		}
   376  		newValue = 42
   377  		delete = false
   378  		return
   379  	})
   380  	if v.(int) != 42 {
   381  		t.Fatalf("v should be 42 when computing a new value: %d", v)
   382  	}
   383  	if !ok {
   384  		t.Fatal("ok should be true when computing a new value")
   385  	}
   386  	// Update an existing value.
   387  	v, ok = m.Compute("foobar", func(oldValue interface{}, loaded bool) (newValue interface{}, delete bool) {
   388  		if oldValue.(int) != 42 {
   389  			t.Fatalf("oldValue should be 42 when updating the value: %d", oldValue)
   390  		}
   391  		if !loaded {
   392  			t.Fatal("loaded should be true when updating the value")
   393  		}
   394  		newValue = oldValue.(int) + 42
   395  		delete = false
   396  		return
   397  	})
   398  	if v.(int) != 84 {
   399  		t.Fatalf("v should be 84 when updating the value: %d", v)
   400  	}
   401  	if !ok {
   402  		t.Fatal("ok should be true when updating the value")
   403  	}
   404  	// Delete an existing value.
   405  	v, ok = m.Compute("foobar", func(oldValue interface{}, loaded bool) (newValue interface{}, delete bool) {
   406  		if oldValue != 84 {
   407  			t.Fatalf("oldValue should be 84 when deleting the value: %d", oldValue)
   408  		}
   409  		if !loaded {
   410  			t.Fatal("loaded should be true when deleting the value")
   411  		}
   412  		delete = true
   413  		return
   414  	})
   415  	if v.(int) != 84 {
   416  		t.Fatalf("v should be 84 when deleting the value: %d", v)
   417  	}
   418  	if ok {
   419  		t.Fatal("ok should be false when deleting the value")
   420  	}
   421  	// Try to delete a non-existing value. Notice different key.
   422  	v, ok = m.Compute("barbaz", func(oldValue interface{}, loaded bool) (newValue interface{}, delete bool) {
   423  		var zeroedV interface{}
   424  		if oldValue != zeroedV {
   425  			t.Fatalf("oldValue should be empty interface{} when trying to delete a non-existing value: %d", oldValue)
   426  		}
   427  		if loaded {
   428  			t.Fatal("loaded should be false when trying to delete a non-existing value")
   429  		}
   430  		// We're returning a non-zero value, but the map should ignore it.
   431  		newValue = 42
   432  		delete = true
   433  		return
   434  	})
   435  	if v != zeroedV {
   436  		t.Fatalf("v should be empty interface{} when trying to delete a non-existing value: %d", v)
   437  	}
   438  	if ok {
   439  		t.Fatal("ok should be false when trying to delete a non-existing value")
   440  	}
   441  }
   442  
   443  func TestMapStoreThenDelete(t *testing.T) {
   444  	const numEntries = 1000
   445  	m := NewMap()
   446  	for i := 0; i < numEntries; i++ {
   447  		m.Store(strconv.Itoa(i), i)
   448  	}
   449  	for i := 0; i < numEntries; i++ {
   450  		m.Delete(strconv.Itoa(i))
   451  		if _, ok := m.Load(strconv.Itoa(i)); ok {
   452  			t.Fatalf("value was not expected for %d", i)
   453  		}
   454  	}
   455  }
   456  
   457  func TestMapStoreThenLoadAndDelete(t *testing.T) {
   458  	const numEntries = 1000
   459  	m := NewMap()
   460  	for i := 0; i < numEntries; i++ {
   461  		m.Store(strconv.Itoa(i), i)
   462  	}
   463  	for i := 0; i < numEntries; i++ {
   464  		if v, loaded := m.LoadAndDelete(strconv.Itoa(i)); !loaded || v.(int) != i {
   465  			t.Fatalf("value was not found or different for %d: %v", i, v)
   466  		}
   467  		if _, ok := m.Load(strconv.Itoa(i)); ok {
   468  			t.Fatalf("value was not expected for %d", i)
   469  		}
   470  	}
   471  }
   472  
   473  func sizeBasedOnRange(m *Map) int {
   474  	size := 0
   475  	m.Range(func(key string, value interface{}) bool {
   476  		size++
   477  		return true
   478  	})
   479  	return size
   480  }
   481  
   482  func TestMapSize(t *testing.T) {
   483  	const numEntries = 1000
   484  	m := NewMap()
   485  	size := m.Size()
   486  	if size != 0 {
   487  		t.Fatalf("zero size expected: %d", size)
   488  	}
   489  	expectedSize := 0
   490  	for i := 0; i < numEntries; i++ {
   491  		m.Store(strconv.Itoa(i), i)
   492  		expectedSize++
   493  		size := m.Size()
   494  		if size != expectedSize {
   495  			t.Fatalf("size of %d was expected, got: %d", expectedSize, size)
   496  		}
   497  		rsize := sizeBasedOnRange(m)
   498  		if size != rsize {
   499  			t.Fatalf("size does not match number of entries in Range: %v, %v", size, rsize)
   500  		}
   501  	}
   502  	for i := 0; i < numEntries; i++ {
   503  		m.Delete(strconv.Itoa(i))
   504  		expectedSize--
   505  		size := m.Size()
   506  		if size != expectedSize {
   507  			t.Fatalf("size of %d was expected, got: %d", expectedSize, size)
   508  		}
   509  		rsize := sizeBasedOnRange(m)
   510  		if size != rsize {
   511  			t.Fatalf("size does not match number of entries in Range: %v, %v", size, rsize)
   512  		}
   513  	}
   514  }
   515  
   516  func TestMapClear(t *testing.T) {
   517  	const numEntries = 1000
   518  	m := NewMap()
   519  	for i := 0; i < numEntries; i++ {
   520  		m.Store(strconv.Itoa(i), i)
   521  	}
   522  	size := m.Size()
   523  	if size != numEntries {
   524  		t.Fatalf("size of %d was expected, got: %d", numEntries, size)
   525  	}
   526  	m.Clear()
   527  	size = m.Size()
   528  	if size != 0 {
   529  		t.Fatalf("zero size was expected, got: %d", size)
   530  	}
   531  	rsize := sizeBasedOnRange(m)
   532  	if rsize != 0 {
   533  		t.Fatalf("zero number of entries in Range was expected, got: %d", rsize)
   534  	}
   535  }
   536  
   537  func assertMapCapacity(t *testing.T, m *Map, expectedCap int) {
   538  	stats := CollectMapStats(m)
   539  	if stats.Capacity != expectedCap {
   540  		t.Fatalf("capacity was different from %d: %d", expectedCap, stats.Capacity)
   541  	}
   542  }
   543  
   544  func TestNewMapPresized(t *testing.T) {
   545  	assertMapCapacity(t, NewMap(), MinMapTableCap)
   546  	assertMapCapacity(t, NewMapPresized(1000), 1536)
   547  	assertMapCapacity(t, NewMapPresized(0), MinMapTableCap)
   548  	assertMapCapacity(t, NewMapPresized(-1), MinMapTableCap)
   549  }
   550  
   551  func TestMapResize(t *testing.T) {
   552  	const numEntries = 100_000
   553  	m := NewMap()
   554  
   555  	for i := 0; i < numEntries; i++ {
   556  		m.Store(strconv.Itoa(i), i)
   557  	}
   558  	stats := CollectMapStats(m)
   559  	if stats.Size != numEntries {
   560  		t.Fatalf("size was too small: %d", stats.Size)
   561  	}
   562  	expectedCapacity := int(math.RoundToEven(MapLoadFactor+1)) * stats.RootBuckets * EntriesPerMapBucket
   563  	if stats.Capacity > expectedCapacity {
   564  		t.Fatalf("capacity was too large: %d, expected: %d", stats.Capacity, expectedCapacity)
   565  	}
   566  	if stats.RootBuckets <= MinMapTableLen {
   567  		t.Fatalf("table was too small: %d", stats.RootBuckets)
   568  	}
   569  	if stats.TotalGrowths == 0 {
   570  		t.Fatalf("non-zero total growths expected: %d", stats.TotalGrowths)
   571  	}
   572  	if stats.TotalShrinks > 0 {
   573  		t.Fatalf("zero total shrinks expected: %d", stats.TotalShrinks)
   574  	}
   575  	// This is useful when debugging table resize and occupancy.
   576  	// Use -v flag to see the output.
   577  	t.Log(stats.ToString())
   578  
   579  	for i := 0; i < numEntries; i++ {
   580  		m.Delete(strconv.Itoa(i))
   581  	}
   582  	stats = CollectMapStats(m)
   583  	if stats.Size > 0 {
   584  		t.Fatalf("zero size was expected: %d", stats.Size)
   585  	}
   586  	expectedCapacity = stats.RootBuckets * EntriesPerMapBucket
   587  	if stats.Capacity != expectedCapacity {
   588  		t.Fatalf("capacity was too large: %d, expected: %d", stats.Capacity, expectedCapacity)
   589  	}
   590  	if stats.RootBuckets != MinMapTableLen {
   591  		t.Fatalf("table was too large: %d", stats.RootBuckets)
   592  	}
   593  	if stats.TotalShrinks == 0 {
   594  		t.Fatalf("non-zero total shrinks expected: %d", stats.TotalShrinks)
   595  	}
   596  	t.Log(stats.ToString())
   597  }
   598  
   599  func TestMapResize_CounterLenLimit(t *testing.T) {
   600  	const numEntries = 1_000_000
   601  	m := NewMap()
   602  
   603  	for i := 0; i < numEntries; i++ {
   604  		m.Store("foo"+strconv.Itoa(i), "bar"+strconv.Itoa(i))
   605  	}
   606  	stats := CollectMapStats(m)
   607  	if stats.Size != numEntries {
   608  		t.Fatalf("size was too small: %d", stats.Size)
   609  	}
   610  	if stats.CounterLen != MaxMapCounterLen {
   611  		t.Fatalf("number of counter stripes was too large: %d, expected: %d",
   612  			stats.CounterLen, MaxMapCounterLen)
   613  	}
   614  }
   615  
   616  func parallelSeqResizer(t *testing.T, m *Map, numEntries int, positive bool, cdone chan bool) {
   617  	for i := 0; i < numEntries; i++ {
   618  		if positive {
   619  			m.Store(strconv.Itoa(i), i)
   620  		} else {
   621  			m.Store(strconv.Itoa(-i), -i)
   622  		}
   623  	}
   624  	cdone <- true
   625  }
   626  
   627  func TestMapParallelResize_GrowOnly(t *testing.T) {
   628  	const numEntries = 100_000
   629  	m := NewMap()
   630  	cdone := make(chan bool)
   631  	go parallelSeqResizer(t, m, numEntries, true, cdone)
   632  	go parallelSeqResizer(t, m, numEntries, false, cdone)
   633  	// Wait for the goroutines to finish.
   634  	<-cdone
   635  	<-cdone
   636  	// Verify map contents.
   637  	for i := -numEntries + 1; i < numEntries; i++ {
   638  		v, ok := m.Load(strconv.Itoa(i))
   639  		if !ok {
   640  			t.Fatalf("value not found for %d", i)
   641  		}
   642  		if vi, ok := v.(int); ok && vi != i {
   643  			t.Fatalf("values do not match for %d: %v", i, v)
   644  		}
   645  	}
   646  	if s := m.Size(); s != 2*numEntries-1 {
   647  		t.Fatalf("unexpected size: %v", s)
   648  	}
   649  }
   650  
   651  func parallelRandResizer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   652  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   653  	for i := 0; i < numIters; i++ {
   654  		coin := r.Int63n(2)
   655  		for j := 0; j < numEntries; j++ {
   656  			if coin == 1 {
   657  				m.Store(strconv.Itoa(j), j)
   658  			} else {
   659  				m.Delete(strconv.Itoa(j))
   660  			}
   661  		}
   662  	}
   663  	cdone <- true
   664  }
   665  
   666  func TestMapParallelResize(t *testing.T) {
   667  	const numIters = 1_000
   668  	const numEntries = 2 * EntriesPerMapBucket * MinMapTableLen
   669  	m := NewMap()
   670  	cdone := make(chan bool)
   671  	go parallelRandResizer(t, m, numIters, numEntries, cdone)
   672  	go parallelRandResizer(t, m, numIters, numEntries, cdone)
   673  	// Wait for the goroutines to finish.
   674  	<-cdone
   675  	<-cdone
   676  	// Verify map contents.
   677  	for i := 0; i < numEntries; i++ {
   678  		v, ok := m.Load(strconv.Itoa(i))
   679  		if !ok {
   680  			// The entry may be deleted and that's ok.
   681  			continue
   682  		}
   683  		if vi, ok := v.(int); ok && vi != i {
   684  			t.Fatalf("values do not match for %d: %v", i, v)
   685  		}
   686  	}
   687  	s := m.Size()
   688  	if s > numEntries {
   689  		t.Fatalf("unexpected size: %v", s)
   690  	}
   691  	rs := sizeBasedOnRange(m)
   692  	if s != rs {
   693  		t.Fatalf("size does not match number of entries in Range: %v, %v", s, rs)
   694  	}
   695  }
   696  
   697  func parallelRandClearer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   698  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   699  	for i := 0; i < numIters; i++ {
   700  		coin := r.Int63n(2)
   701  		for j := 0; j < numEntries; j++ {
   702  			if coin == 1 {
   703  				m.Store(strconv.Itoa(j), j)
   704  			} else {
   705  				m.Clear()
   706  			}
   707  		}
   708  	}
   709  	cdone <- true
   710  }
   711  
   712  func TestMapParallelClear(t *testing.T) {
   713  	const numIters = 100
   714  	const numEntries = 1_000
   715  	m := NewMap()
   716  	cdone := make(chan bool)
   717  	go parallelRandClearer(t, m, numIters, numEntries, cdone)
   718  	go parallelRandClearer(t, m, numIters, numEntries, cdone)
   719  	// Wait for the goroutines to finish.
   720  	<-cdone
   721  	<-cdone
   722  	// Verify map size.
   723  	s := m.Size()
   724  	if s > numEntries {
   725  		t.Fatalf("unexpected size: %v", s)
   726  	}
   727  	rs := sizeBasedOnRange(m)
   728  	if s != rs {
   729  		t.Fatalf("size does not match number of entries in Range: %v, %v", s, rs)
   730  	}
   731  }
   732  
   733  func parallelSeqStorer(t *testing.T, m *Map, storeEach, numIters, numEntries int, cdone chan bool) {
   734  	for i := 0; i < numIters; i++ {
   735  		for j := 0; j < numEntries; j++ {
   736  			if storeEach == 0 || j%storeEach == 0 {
   737  				m.Store(strconv.Itoa(j), j)
   738  				// Due to atomic snapshots we must see a "<j>"/j pair.
   739  				v, ok := m.Load(strconv.Itoa(j))
   740  				if !ok {
   741  					t.Errorf("value was not found for %d", j)
   742  					break
   743  				}
   744  				if vi, ok := v.(int); !ok || vi != j {
   745  					t.Errorf("value was not expected for %d: %d", j, vi)
   746  					break
   747  				}
   748  			}
   749  		}
   750  	}
   751  	cdone <- true
   752  }
   753  
   754  func TestMapParallelStores(t *testing.T) {
   755  	const numStorers = 4
   756  	const numIters = 10_000
   757  	const numEntries = 100
   758  	m := NewMap()
   759  	cdone := make(chan bool)
   760  	for i := 0; i < numStorers; i++ {
   761  		go parallelSeqStorer(t, m, i, numIters, numEntries, cdone)
   762  	}
   763  	// Wait for the goroutines to finish.
   764  	for i := 0; i < numStorers; i++ {
   765  		<-cdone
   766  	}
   767  	// Verify map contents.
   768  	for i := 0; i < numEntries; i++ {
   769  		v, ok := m.Load(strconv.Itoa(i))
   770  		if !ok {
   771  			t.Fatalf("value not found for %d", i)
   772  		}
   773  		if vi, ok := v.(int); ok && vi != i {
   774  			t.Fatalf("values do not match for %d: %v", i, v)
   775  		}
   776  	}
   777  }
   778  
   779  func parallelRandStorer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   780  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   781  	for i := 0; i < numIters; i++ {
   782  		j := r.Intn(numEntries)
   783  		if v, loaded := m.LoadOrStore(strconv.Itoa(j), j); loaded {
   784  			if vi, ok := v.(int); !ok || vi != j {
   785  				t.Errorf("value was not expected for %d: %d", j, vi)
   786  			}
   787  		}
   788  	}
   789  	cdone <- true
   790  }
   791  
   792  func parallelRandDeleter(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   793  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   794  	for i := 0; i < numIters; i++ {
   795  		j := r.Intn(numEntries)
   796  		if v, loaded := m.LoadAndDelete(strconv.Itoa(j)); loaded {
   797  			if vi, ok := v.(int); !ok || vi != j {
   798  				t.Errorf("value was not expected for %d: %d", j, vi)
   799  			}
   800  		}
   801  	}
   802  	cdone <- true
   803  }
   804  
   805  func parallelLoader(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   806  	for i := 0; i < numIters; i++ {
   807  		for j := 0; j < numEntries; j++ {
   808  			// Due to atomic snapshots we must either see no entry, or a "<j>"/j pair.
   809  			if v, ok := m.Load(strconv.Itoa(j)); ok {
   810  				if vi, ok := v.(int); !ok || vi != j {
   811  					t.Errorf("value was not expected for %d: %d", j, vi)
   812  				}
   813  			}
   814  		}
   815  	}
   816  	cdone <- true
   817  }
   818  
   819  func TestMapAtomicSnapshot(t *testing.T) {
   820  	const numIters = 100_000
   821  	const numEntries = 100
   822  	m := NewMap()
   823  	cdone := make(chan bool)
   824  	// Update or delete random entry in parallel with loads.
   825  	go parallelRandStorer(t, m, numIters, numEntries, cdone)
   826  	go parallelRandDeleter(t, m, numIters, numEntries, cdone)
   827  	go parallelLoader(t, m, numIters, numEntries, cdone)
   828  	// Wait for the goroutines to finish.
   829  	for i := 0; i < 3; i++ {
   830  		<-cdone
   831  	}
   832  }
   833  
   834  func TestMapParallelStoresAndDeletes(t *testing.T) {
   835  	const numWorkers = 2
   836  	const numIters = 100_000
   837  	const numEntries = 1000
   838  	m := NewMap()
   839  	cdone := make(chan bool)
   840  	// Update random entry in parallel with deletes.
   841  	for i := 0; i < numWorkers; i++ {
   842  		go parallelRandStorer(t, m, numIters, numEntries, cdone)
   843  		go parallelRandDeleter(t, m, numIters, numEntries, cdone)
   844  	}
   845  	// Wait for the goroutines to finish.
   846  	for i := 0; i < 2*numWorkers; i++ {
   847  		<-cdone
   848  	}
   849  }
   850  
   851  func parallelComputer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   852  	for i := 0; i < numIters; i++ {
   853  		for j := 0; j < numEntries; j++ {
   854  			m.Compute(strconv.Itoa(j), func(oldValue interface{}, loaded bool) (newValue interface{}, delete bool) {
   855  				if !loaded {
   856  					return uint64(1), false
   857  				}
   858  				return uint64(oldValue.(uint64) + 1), false
   859  			})
   860  		}
   861  	}
   862  	cdone <- true
   863  }
   864  
   865  func TestMapParallelComputes(t *testing.T) {
   866  	const numWorkers = 4 // Also stands for numEntries.
   867  	const numIters = 10_000
   868  	m := NewMap()
   869  	cdone := make(chan bool)
   870  	for i := 0; i < numWorkers; i++ {
   871  		go parallelComputer(t, m, numIters, numWorkers, cdone)
   872  	}
   873  	// Wait for the goroutines to finish.
   874  	for i := 0; i < numWorkers; i++ {
   875  		<-cdone
   876  	}
   877  	// Verify map contents.
   878  	for i := 0; i < numWorkers; i++ {
   879  		v, ok := m.Load(strconv.Itoa(i))
   880  		if !ok {
   881  			t.Fatalf("value not found for %d", i)
   882  		}
   883  		if v.(uint64) != numWorkers*numIters {
   884  			t.Fatalf("values do not match for %d: %v", i, v)
   885  		}
   886  	}
   887  }
   888  
   889  func parallelRangeStorer(t *testing.T, m *Map, numEntries int, stopFlag *int64, cdone chan bool) {
   890  	for {
   891  		for i := 0; i < numEntries; i++ {
   892  			m.Store(strconv.Itoa(i), i)
   893  		}
   894  		if atomic.LoadInt64(stopFlag) != 0 {
   895  			break
   896  		}
   897  	}
   898  	cdone <- true
   899  }
   900  
   901  func parallelRangeDeleter(t *testing.T, m *Map, numEntries int, stopFlag *int64, cdone chan bool) {
   902  	for {
   903  		for i := 0; i < numEntries; i++ {
   904  			m.Delete(strconv.Itoa(i))
   905  		}
   906  		if atomic.LoadInt64(stopFlag) != 0 {
   907  			break
   908  		}
   909  	}
   910  	cdone <- true
   911  }
   912  
   913  func TestMapParallelRange(t *testing.T) {
   914  	const numEntries = 10_000
   915  	m := NewMap()
   916  	for i := 0; i < numEntries; i++ {
   917  		m.Store(strconv.Itoa(i), i)
   918  	}
   919  	// Start goroutines that would be storing and deleting items in parallel.
   920  	cdone := make(chan bool)
   921  	stopFlag := int64(0)
   922  	go parallelRangeStorer(t, m, numEntries, &stopFlag, cdone)
   923  	go parallelRangeDeleter(t, m, numEntries, &stopFlag, cdone)
   924  	// Iterate the map and verify that no duplicate keys were met.
   925  	met := make(map[string]int)
   926  	m.Range(func(key string, value interface{}) bool {
   927  		if key != strconv.Itoa(value.(int)) {
   928  			t.Fatalf("got unexpected value for key %s: %v", key, value)
   929  			return false
   930  		}
   931  		met[key] += 1
   932  		return true
   933  	})
   934  	if len(met) == 0 {
   935  		t.Fatal("no entries were met when iterating")
   936  	}
   937  	for k, c := range met {
   938  		if c != 1 {
   939  			t.Fatalf("met key %s multiple times: %d", k, c)
   940  		}
   941  	}
   942  	// Make sure that both goroutines finish.
   943  	atomic.StoreInt64(&stopFlag, 1)
   944  	<-cdone
   945  	<-cdone
   946  }
   947  
   948  func TestMapTopHashMutex(t *testing.T) {
   949  	const (
   950  		numLockers    = 4
   951  		numIterations = 1000
   952  	)
   953  	var (
   954  		activity int32
   955  		mu       uint64
   956  	)
   957  	cdone := make(chan bool)
   958  	for i := 0; i < numLockers; i++ {
   959  		go func() {
   960  			for i := 0; i < numIterations; i++ {
   961  				LockBucket(&mu)
   962  				n := atomic.AddInt32(&activity, 1)
   963  				if n != 1 {
   964  					UnlockBucket(&mu)
   965  					panic(fmt.Sprintf("lock(%d)\n", n))
   966  				}
   967  				atomic.AddInt32(&activity, -1)
   968  				UnlockBucket(&mu)
   969  			}
   970  			cdone <- true
   971  		}()
   972  	}
   973  	// Wait for all lockers to finish.
   974  	for i := 0; i < numLockers; i++ {
   975  		<-cdone
   976  	}
   977  }
   978  
   979  func TestMapTopHashMutex_Store_NoLock(t *testing.T) {
   980  	mu := uint64(0)
   981  	testMapTopHashMutex_Store(t, &mu)
   982  }
   983  
   984  func TestMapTopHashMutex_Store_WhileLocked(t *testing.T) {
   985  	mu := uint64(0)
   986  	LockBucket(&mu)
   987  	defer UnlockBucket(&mu)
   988  	testMapTopHashMutex_Store(t, &mu)
   989  }
   990  
   991  func testMapTopHashMutex_Store(t *testing.T, topHashes *uint64) {
   992  	hash := uint64(0b1101_0100_1010_1011_1101 << 44)
   993  	for i := 0; i < EntriesPerMapBucket; i++ {
   994  		if TopHashMatch(hash, *topHashes, i) {
   995  			t.Fatalf("top hash match for all zeros for index %d", i)
   996  		}
   997  
   998  		prevOnes := bits.OnesCount64(*topHashes)
   999  		*topHashes = StoreTopHash(hash, *topHashes, i)
  1000  		newOnes := bits.OnesCount64(*topHashes)
  1001  		expectedInc := bits.OnesCount64(hash) + 1
  1002  		if newOnes != prevOnes+expectedInc {
  1003  			t.Fatalf("unexpected bits change after store for index %d: %d, %d, %#b",
  1004  				i, newOnes, prevOnes+expectedInc, *topHashes)
  1005  		}
  1006  
  1007  		if !TopHashMatch(hash, *topHashes, i) {
  1008  			t.Fatalf("top hash mismatch after store for index %d: %#b", i, *topHashes)
  1009  		}
  1010  	}
  1011  }
  1012  
  1013  func TestMapTopHashMutex_Erase_NoLock(t *testing.T) {
  1014  	mu := uint64(0)
  1015  	testMapTopHashMutex_Erase(t, &mu)
  1016  }
  1017  
  1018  func TestMapTopHashMutex_Erase_WhileLocked(t *testing.T) {
  1019  	mu := uint64(0)
  1020  	LockBucket(&mu)
  1021  	defer UnlockBucket(&mu)
  1022  	testMapTopHashMutex_Erase(t, &mu)
  1023  }
  1024  
  1025  func testMapTopHashMutex_Erase(t *testing.T, topHashes *uint64) {
  1026  	hash := uint64(0xababaaaaaaaaaaaa) // top hash is 1010_1011_1010_1011_1010
  1027  	for i := 0; i < EntriesPerMapBucket; i++ {
  1028  		*topHashes = StoreTopHash(hash, *topHashes, i)
  1029  		ones := bits.OnesCount64(*topHashes)
  1030  
  1031  		*topHashes = EraseTopHash(*topHashes, i)
  1032  		if TopHashMatch(hash, *topHashes, i) {
  1033  			t.Fatalf("top hash match after erase for index %d: %#b", i, *topHashes)
  1034  		}
  1035  
  1036  		erasedBits := ones - bits.OnesCount64(*topHashes)
  1037  		if erasedBits != 1 {
  1038  			t.Fatalf("more than one bit changed after erase: %d, %d", i, erasedBits)
  1039  		}
  1040  	}
  1041  }
  1042  
  1043  func TestMapTopHashMutex_StoreAfterErase_NoLock(t *testing.T) {
  1044  	mu := uint64(0)
  1045  	testMapTopHashMutex_StoreAfterErase(t, &mu)
  1046  }
  1047  
  1048  func TestMapTopHashMutex_StoreAfterErase_WhileLocked(t *testing.T) {
  1049  	mu := uint64(0)
  1050  	LockBucket(&mu)
  1051  	defer UnlockBucket(&mu)
  1052  	testMapTopHashMutex_StoreAfterErase(t, &mu)
  1053  }
  1054  
  1055  func testMapTopHashMutex_StoreAfterErase(t *testing.T, topHashes *uint64) {
  1056  	hashOne := uint64(0b1101_0100_1101_0100_1101_1111 << 40) // top hash is 1101_0100_1101_0100_1101
  1057  	hashTwo := uint64(0b1010_1011_1010_1011_1010_1111 << 40) // top hash is 1010_1011_1010_1011_1010
  1058  	idx := 2
  1059  
  1060  	*topHashes = StoreTopHash(hashOne, *topHashes, idx)
  1061  	if !TopHashMatch(hashOne, *topHashes, idx) {
  1062  		t.Fatalf("top hash mismatch for hash one: %#b, %#b", hashOne, *topHashes)
  1063  	}
  1064  	if TopHashMatch(hashTwo, *topHashes, idx) {
  1065  		t.Fatalf("top hash match for hash two: %#b, %#b", hashTwo, *topHashes)
  1066  	}
  1067  
  1068  	*topHashes = EraseTopHash(*topHashes, idx)
  1069  	*topHashes = StoreTopHash(hashTwo, *topHashes, idx)
  1070  	if TopHashMatch(hashOne, *topHashes, idx) {
  1071  		t.Fatalf("top hash match for hash one: %#b, %#b", hashOne, *topHashes)
  1072  	}
  1073  	if !TopHashMatch(hashTwo, *topHashes, idx) {
  1074  		t.Fatalf("top hash mismatch for hash two: %#b, %#b", hashTwo, *topHashes)
  1075  	}
  1076  }
  1077  
  1078  func BenchmarkMap_NoWarmUp(b *testing.B) {
  1079  	for _, bc := range benchmarkCases {
  1080  		if bc.readPercentage == 100 {
  1081  			// This benchmark doesn't make sense without a warm-up.
  1082  			continue
  1083  		}
  1084  		b.Run(bc.name, func(b *testing.B) {
  1085  			m := NewMap()
  1086  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1087  				return m.Load(k)
  1088  			}, func(k string, v interface{}) {
  1089  				m.Store(k, v)
  1090  			}, func(k string) {
  1091  				m.Delete(k)
  1092  			}, bc.readPercentage)
  1093  		})
  1094  	}
  1095  }
  1096  
  1097  func BenchmarkMapStandard_NoWarmUp(b *testing.B) {
  1098  	for _, bc := range benchmarkCases {
  1099  		if bc.readPercentage == 100 {
  1100  			// This benchmark doesn't make sense without a warm-up.
  1101  			continue
  1102  		}
  1103  		b.Run(bc.name, func(b *testing.B) {
  1104  			var m sync.Map
  1105  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1106  				return m.Load(k)
  1107  			}, func(k string, v interface{}) {
  1108  				m.Store(k, v)
  1109  			}, func(k string) {
  1110  				m.Delete(k)
  1111  			}, bc.readPercentage)
  1112  		})
  1113  	}
  1114  }
  1115  
  1116  func BenchmarkMap_WarmUp(b *testing.B) {
  1117  	for _, bc := range benchmarkCases {
  1118  		b.Run(bc.name, func(b *testing.B) {
  1119  			m := NewMapPresized(benchmarkNumEntries)
  1120  			for i := 0; i < benchmarkNumEntries; i++ {
  1121  				m.Store(benchmarkKeyPrefix+strconv.Itoa(i), i)
  1122  			}
  1123  			b.ResetTimer()
  1124  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1125  				return m.Load(k)
  1126  			}, func(k string, v interface{}) {
  1127  				m.Store(k, v)
  1128  			}, func(k string) {
  1129  				m.Delete(k)
  1130  			}, bc.readPercentage)
  1131  		})
  1132  	}
  1133  }
  1134  
  1135  // This is a nice scenario for sync.Map since a lot of updates
  1136  // will hit the readOnly part of the map.
  1137  func BenchmarkMapStandard_WarmUp(b *testing.B) {
  1138  	for _, bc := range benchmarkCases {
  1139  		b.Run(bc.name, func(b *testing.B) {
  1140  			var m sync.Map
  1141  			for i := 0; i < benchmarkNumEntries; i++ {
  1142  				m.Store(benchmarkKeyPrefix+strconv.Itoa(i), i)
  1143  			}
  1144  			b.ResetTimer()
  1145  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1146  				return m.Load(k)
  1147  			}, func(k string, v interface{}) {
  1148  				m.Store(k, v)
  1149  			}, func(k string) {
  1150  				m.Delete(k)
  1151  			}, bc.readPercentage)
  1152  		})
  1153  	}
  1154  }
  1155  
  1156  func benchmarkMap(
  1157  	b *testing.B,
  1158  	loadFn func(k string) (interface{}, bool),
  1159  	storeFn func(k string, v interface{}),
  1160  	deleteFn func(k string),
  1161  	readPercentage int,
  1162  ) {
  1163  	runParallel(b, func(pb *testing.PB) {
  1164  		// convert percent to permille to support 99% case
  1165  		storeThreshold := 10 * readPercentage
  1166  		deleteThreshold := 10*readPercentage + ((1000 - 10*readPercentage) / 2)
  1167  		for pb.Next() {
  1168  			op := int(Fastrand() % 1000)
  1169  			i := int(Fastrand() % benchmarkNumEntries)
  1170  			if op >= deleteThreshold {
  1171  				deleteFn(benchmarkKeys[i])
  1172  			} else if op >= storeThreshold {
  1173  				storeFn(benchmarkKeys[i], i)
  1174  			} else {
  1175  				loadFn(benchmarkKeys[i])
  1176  			}
  1177  		}
  1178  	})
  1179  }
  1180  
  1181  func BenchmarkMapRange(b *testing.B) {
  1182  	m := NewMap()
  1183  	for i := 0; i < benchmarkNumEntries; i++ {
  1184  		m.Store(benchmarkKeys[i], i)
  1185  	}
  1186  	b.ResetTimer()
  1187  	runParallel(b, func(pb *testing.PB) {
  1188  		foo := 0
  1189  		for pb.Next() {
  1190  			m.Range(func(key string, value interface{}) bool {
  1191  				// Dereference the value to have an apple-to-apple
  1192  				// comparison with MapOf.Range.
  1193  				_ = value.(int)
  1194  				foo++
  1195  				return true
  1196  			})
  1197  			_ = foo
  1198  		}
  1199  	})
  1200  }
  1201  
  1202  func BenchmarkMapRangeStandard(b *testing.B) {
  1203  	var m sync.Map
  1204  	for i := 0; i < benchmarkNumEntries; i++ {
  1205  		m.Store(benchmarkKeys[i], i)
  1206  	}
  1207  	b.ResetTimer()
  1208  	runParallel(b, func(pb *testing.PB) {
  1209  		foo := 0
  1210  		for pb.Next() {
  1211  			m.Range(func(key interface{}, value interface{}) bool {
  1212  				// Dereference the key and the value to have an apple-to-apple
  1213  				// comparison with MapOf.Range.
  1214  				_, _ = key.(string), value.(int)
  1215  				foo++
  1216  				return true
  1217  			})
  1218  			_ = foo
  1219  		}
  1220  	})
  1221  }