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