github.com/puzpuzpuz/xsync/v3@v3.1.1-0.20240225193106-cbe4ec1e954f/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/v3"
    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 TestMapStoreThenParallelDelete_DoesNotShrinkBelowMinTableLen(t *testing.T) {
   474  	const numEntries = 1000
   475  	m := NewMap()
   476  	for i := 0; i < numEntries; i++ {
   477  		m.Store(strconv.Itoa(i), i)
   478  	}
   479  
   480  	cdone := make(chan bool)
   481  	go func() {
   482  		for i := 0; i < numEntries; i++ {
   483  			m.Delete(strconv.Itoa(int(i)))
   484  		}
   485  		cdone <- true
   486  	}()
   487  	go func() {
   488  		for i := 0; i < numEntries; i++ {
   489  			m.Delete(strconv.Itoa(int(i)))
   490  		}
   491  		cdone <- true
   492  	}()
   493  
   494  	// Wait for the goroutines to finish.
   495  	<-cdone
   496  	<-cdone
   497  
   498  	stats := CollectMapStats(m)
   499  	if stats.RootBuckets != DefaultMinMapTableLen {
   500  		t.Fatalf("table length was different from the minimum: %d", stats.RootBuckets)
   501  	}
   502  }
   503  
   504  func sizeBasedOnRange(m *Map) int {
   505  	size := 0
   506  	m.Range(func(key string, value interface{}) bool {
   507  		size++
   508  		return true
   509  	})
   510  	return size
   511  }
   512  
   513  func TestMapSize(t *testing.T) {
   514  	const numEntries = 1000
   515  	m := NewMap()
   516  	size := m.Size()
   517  	if size != 0 {
   518  		t.Fatalf("zero size expected: %d", size)
   519  	}
   520  	expectedSize := 0
   521  	for i := 0; i < numEntries; i++ {
   522  		m.Store(strconv.Itoa(i), i)
   523  		expectedSize++
   524  		size := m.Size()
   525  		if size != expectedSize {
   526  			t.Fatalf("size of %d was expected, got: %d", expectedSize, size)
   527  		}
   528  		rsize := sizeBasedOnRange(m)
   529  		if size != rsize {
   530  			t.Fatalf("size does not match number of entries in Range: %v, %v", size, rsize)
   531  		}
   532  	}
   533  	for i := 0; i < numEntries; i++ {
   534  		m.Delete(strconv.Itoa(i))
   535  		expectedSize--
   536  		size := m.Size()
   537  		if size != expectedSize {
   538  			t.Fatalf("size of %d was expected, got: %d", expectedSize, size)
   539  		}
   540  		rsize := sizeBasedOnRange(m)
   541  		if size != rsize {
   542  			t.Fatalf("size does not match number of entries in Range: %v, %v", size, rsize)
   543  		}
   544  	}
   545  }
   546  
   547  func TestMapClear(t *testing.T) {
   548  	const numEntries = 1000
   549  	m := NewMap()
   550  	for i := 0; i < numEntries; i++ {
   551  		m.Store(strconv.Itoa(i), i)
   552  	}
   553  	size := m.Size()
   554  	if size != numEntries {
   555  		t.Fatalf("size of %d was expected, got: %d", numEntries, size)
   556  	}
   557  	m.Clear()
   558  	size = m.Size()
   559  	if size != 0 {
   560  		t.Fatalf("zero size was expected, got: %d", size)
   561  	}
   562  	rsize := sizeBasedOnRange(m)
   563  	if rsize != 0 {
   564  		t.Fatalf("zero number of entries in Range was expected, got: %d", rsize)
   565  	}
   566  }
   567  
   568  func assertMapCapacity(t *testing.T, m *Map, expectedCap int) {
   569  	stats := CollectMapStats(m)
   570  	if stats.Capacity != expectedCap {
   571  		t.Fatalf("capacity was different from %d: %d", expectedCap, stats.Capacity)
   572  	}
   573  }
   574  
   575  func TestNewMapPresized(t *testing.T) {
   576  	assertMapCapacity(t, NewMap(), DefaultMinMapTableCap)
   577  	assertMapCapacity(t, NewMapPresized(1000), 1536)
   578  	assertMapCapacity(t, NewMapPresized(0), DefaultMinMapTableCap)
   579  	assertMapCapacity(t, NewMapPresized(-1), DefaultMinMapTableCap)
   580  }
   581  
   582  func TestNewMapPresized_DoesNotShrinkBelowMinTableLen(t *testing.T) {
   583  	const minTableLen = 1024
   584  	const numEntries = minTableLen * EntriesPerMapBucket
   585  	m := NewMapPresized(numEntries)
   586  	for i := 0; i < numEntries; i++ {
   587  		m.Store(strconv.Itoa(i), i)
   588  	}
   589  
   590  	stats := CollectMapStats(m)
   591  	if stats.RootBuckets <= minTableLen {
   592  		t.Fatalf("table did not grow: %d", stats.RootBuckets)
   593  	}
   594  
   595  	for i := 0; i < numEntries; i++ {
   596  		m.Delete(strconv.Itoa(int(i)))
   597  	}
   598  
   599  	stats = CollectMapStats(m)
   600  	if stats.RootBuckets != minTableLen {
   601  		t.Fatalf("table length was different from the minimum: %d", stats.RootBuckets)
   602  	}
   603  }
   604  
   605  func TestMapResize(t *testing.T) {
   606  	const numEntries = 100_000
   607  	m := NewMap()
   608  
   609  	for i := 0; i < numEntries; i++ {
   610  		m.Store(strconv.Itoa(i), i)
   611  	}
   612  	stats := CollectMapStats(m)
   613  	if stats.Size != numEntries {
   614  		t.Fatalf("size was too small: %d", stats.Size)
   615  	}
   616  	expectedCapacity := int(math.RoundToEven(MapLoadFactor+1)) * stats.RootBuckets * EntriesPerMapBucket
   617  	if stats.Capacity > expectedCapacity {
   618  		t.Fatalf("capacity was too large: %d, expected: %d", stats.Capacity, expectedCapacity)
   619  	}
   620  	if stats.RootBuckets <= DefaultMinMapTableLen {
   621  		t.Fatalf("table was too small: %d", stats.RootBuckets)
   622  	}
   623  	if stats.TotalGrowths == 0 {
   624  		t.Fatalf("non-zero total growths expected: %d", stats.TotalGrowths)
   625  	}
   626  	if stats.TotalShrinks > 0 {
   627  		t.Fatalf("zero total shrinks expected: %d", stats.TotalShrinks)
   628  	}
   629  	// This is useful when debugging table resize and occupancy.
   630  	// Use -v flag to see the output.
   631  	t.Log(stats.ToString())
   632  
   633  	for i := 0; i < numEntries; i++ {
   634  		m.Delete(strconv.Itoa(i))
   635  	}
   636  	stats = CollectMapStats(m)
   637  	if stats.Size > 0 {
   638  		t.Fatalf("zero size was expected: %d", stats.Size)
   639  	}
   640  	expectedCapacity = stats.RootBuckets * EntriesPerMapBucket
   641  	if stats.Capacity != expectedCapacity {
   642  		t.Fatalf("capacity was too large: %d, expected: %d", stats.Capacity, expectedCapacity)
   643  	}
   644  	if stats.RootBuckets != DefaultMinMapTableLen {
   645  		t.Fatalf("table was too large: %d", stats.RootBuckets)
   646  	}
   647  	if stats.TotalShrinks == 0 {
   648  		t.Fatalf("non-zero total shrinks expected: %d", stats.TotalShrinks)
   649  	}
   650  	t.Log(stats.ToString())
   651  }
   652  
   653  func TestMapResize_CounterLenLimit(t *testing.T) {
   654  	const numEntries = 1_000_000
   655  	m := NewMap()
   656  
   657  	for i := 0; i < numEntries; i++ {
   658  		m.Store("foo"+strconv.Itoa(i), "bar"+strconv.Itoa(i))
   659  	}
   660  	stats := CollectMapStats(m)
   661  	if stats.Size != numEntries {
   662  		t.Fatalf("size was too small: %d", stats.Size)
   663  	}
   664  	if stats.CounterLen != MaxMapCounterLen {
   665  		t.Fatalf("number of counter stripes was too large: %d, expected: %d",
   666  			stats.CounterLen, MaxMapCounterLen)
   667  	}
   668  }
   669  
   670  func parallelSeqResizer(t *testing.T, m *Map, numEntries int, positive bool, cdone chan bool) {
   671  	for i := 0; i < numEntries; i++ {
   672  		if positive {
   673  			m.Store(strconv.Itoa(i), i)
   674  		} else {
   675  			m.Store(strconv.Itoa(-i), -i)
   676  		}
   677  	}
   678  	cdone <- true
   679  }
   680  
   681  func TestMapParallelResize_GrowOnly(t *testing.T) {
   682  	const numEntries = 100_000
   683  	m := NewMap()
   684  	cdone := make(chan bool)
   685  	go parallelSeqResizer(t, m, numEntries, true, cdone)
   686  	go parallelSeqResizer(t, m, numEntries, false, cdone)
   687  	// Wait for the goroutines to finish.
   688  	<-cdone
   689  	<-cdone
   690  	// Verify map contents.
   691  	for i := -numEntries + 1; i < numEntries; i++ {
   692  		v, ok := m.Load(strconv.Itoa(i))
   693  		if !ok {
   694  			t.Fatalf("value not found for %d", i)
   695  		}
   696  		if vi, ok := v.(int); ok && vi != i {
   697  			t.Fatalf("values do not match for %d: %v", i, v)
   698  		}
   699  	}
   700  	if s := m.Size(); s != 2*numEntries-1 {
   701  		t.Fatalf("unexpected size: %v", s)
   702  	}
   703  }
   704  
   705  func parallelRandResizer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   706  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   707  	for i := 0; i < numIters; i++ {
   708  		coin := r.Int63n(2)
   709  		for j := 0; j < numEntries; j++ {
   710  			if coin == 1 {
   711  				m.Store(strconv.Itoa(j), j)
   712  			} else {
   713  				m.Delete(strconv.Itoa(j))
   714  			}
   715  		}
   716  	}
   717  	cdone <- true
   718  }
   719  
   720  func TestMapParallelResize(t *testing.T) {
   721  	const numIters = 1_000
   722  	const numEntries = 2 * EntriesPerMapBucket * DefaultMinMapTableLen
   723  	m := NewMap()
   724  	cdone := make(chan bool)
   725  	go parallelRandResizer(t, m, numIters, numEntries, cdone)
   726  	go parallelRandResizer(t, m, numIters, numEntries, cdone)
   727  	// Wait for the goroutines to finish.
   728  	<-cdone
   729  	<-cdone
   730  	// Verify map contents.
   731  	for i := 0; i < numEntries; i++ {
   732  		v, ok := m.Load(strconv.Itoa(i))
   733  		if !ok {
   734  			// The entry may be deleted and that's ok.
   735  			continue
   736  		}
   737  		if vi, ok := v.(int); ok && vi != i {
   738  			t.Fatalf("values do not match for %d: %v", i, v)
   739  		}
   740  	}
   741  	s := m.Size()
   742  	if s > numEntries {
   743  		t.Fatalf("unexpected size: %v", s)
   744  	}
   745  	rs := sizeBasedOnRange(m)
   746  	if s != rs {
   747  		t.Fatalf("size does not match number of entries in Range: %v, %v", s, rs)
   748  	}
   749  }
   750  
   751  func parallelRandClearer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   752  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   753  	for i := 0; i < numIters; i++ {
   754  		coin := r.Int63n(2)
   755  		for j := 0; j < numEntries; j++ {
   756  			if coin == 1 {
   757  				m.Store(strconv.Itoa(j), j)
   758  			} else {
   759  				m.Clear()
   760  			}
   761  		}
   762  	}
   763  	cdone <- true
   764  }
   765  
   766  func TestMapParallelClear(t *testing.T) {
   767  	const numIters = 100
   768  	const numEntries = 1_000
   769  	m := NewMap()
   770  	cdone := make(chan bool)
   771  	go parallelRandClearer(t, m, numIters, numEntries, cdone)
   772  	go parallelRandClearer(t, m, numIters, numEntries, cdone)
   773  	// Wait for the goroutines to finish.
   774  	<-cdone
   775  	<-cdone
   776  	// Verify map size.
   777  	s := m.Size()
   778  	if s > numEntries {
   779  		t.Fatalf("unexpected size: %v", s)
   780  	}
   781  	rs := sizeBasedOnRange(m)
   782  	if s != rs {
   783  		t.Fatalf("size does not match number of entries in Range: %v, %v", s, rs)
   784  	}
   785  }
   786  
   787  func parallelSeqStorer(t *testing.T, m *Map, storeEach, numIters, numEntries int, cdone chan bool) {
   788  	for i := 0; i < numIters; i++ {
   789  		for j := 0; j < numEntries; j++ {
   790  			if storeEach == 0 || j%storeEach == 0 {
   791  				m.Store(strconv.Itoa(j), j)
   792  				// Due to atomic snapshots we must see a "<j>"/j pair.
   793  				v, ok := m.Load(strconv.Itoa(j))
   794  				if !ok {
   795  					t.Errorf("value was not found for %d", j)
   796  					break
   797  				}
   798  				if vi, ok := v.(int); !ok || vi != j {
   799  					t.Errorf("value was not expected for %d: %d", j, vi)
   800  					break
   801  				}
   802  			}
   803  		}
   804  	}
   805  	cdone <- true
   806  }
   807  
   808  func TestMapParallelStores(t *testing.T) {
   809  	const numStorers = 4
   810  	const numIters = 10_000
   811  	const numEntries = 100
   812  	m := NewMap()
   813  	cdone := make(chan bool)
   814  	for i := 0; i < numStorers; i++ {
   815  		go parallelSeqStorer(t, m, i, numIters, numEntries, cdone)
   816  	}
   817  	// Wait for the goroutines to finish.
   818  	for i := 0; i < numStorers; i++ {
   819  		<-cdone
   820  	}
   821  	// Verify map contents.
   822  	for i := 0; i < numEntries; i++ {
   823  		v, ok := m.Load(strconv.Itoa(i))
   824  		if !ok {
   825  			t.Fatalf("value not found for %d", i)
   826  		}
   827  		if vi, ok := v.(int); ok && vi != i {
   828  			t.Fatalf("values do not match for %d: %v", i, v)
   829  		}
   830  	}
   831  }
   832  
   833  func parallelRandStorer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   834  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   835  	for i := 0; i < numIters; i++ {
   836  		j := r.Intn(numEntries)
   837  		if v, loaded := m.LoadOrStore(strconv.Itoa(j), j); loaded {
   838  			if vi, ok := v.(int); !ok || vi != j {
   839  				t.Errorf("value was not expected for %d: %d", j, vi)
   840  			}
   841  		}
   842  	}
   843  	cdone <- true
   844  }
   845  
   846  func parallelRandDeleter(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   847  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   848  	for i := 0; i < numIters; i++ {
   849  		j := r.Intn(numEntries)
   850  		if v, loaded := m.LoadAndDelete(strconv.Itoa(j)); loaded {
   851  			if vi, ok := v.(int); !ok || vi != j {
   852  				t.Errorf("value was not expected for %d: %d", j, vi)
   853  			}
   854  		}
   855  	}
   856  	cdone <- true
   857  }
   858  
   859  func parallelLoader(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   860  	for i := 0; i < numIters; i++ {
   861  		for j := 0; j < numEntries; j++ {
   862  			// Due to atomic snapshots we must either see no entry, or a "<j>"/j pair.
   863  			if v, ok := m.Load(strconv.Itoa(j)); ok {
   864  				if vi, ok := v.(int); !ok || vi != j {
   865  					t.Errorf("value was not expected for %d: %d", j, vi)
   866  				}
   867  			}
   868  		}
   869  	}
   870  	cdone <- true
   871  }
   872  
   873  func TestMapAtomicSnapshot(t *testing.T) {
   874  	const numIters = 100_000
   875  	const numEntries = 100
   876  	m := NewMap()
   877  	cdone := make(chan bool)
   878  	// Update or delete random entry in parallel with loads.
   879  	go parallelRandStorer(t, m, numIters, numEntries, cdone)
   880  	go parallelRandDeleter(t, m, numIters, numEntries, cdone)
   881  	go parallelLoader(t, m, numIters, numEntries, cdone)
   882  	// Wait for the goroutines to finish.
   883  	for i := 0; i < 3; i++ {
   884  		<-cdone
   885  	}
   886  }
   887  
   888  func TestMapParallelStoresAndDeletes(t *testing.T) {
   889  	const numWorkers = 2
   890  	const numIters = 100_000
   891  	const numEntries = 1000
   892  	m := NewMap()
   893  	cdone := make(chan bool)
   894  	// Update random entry in parallel with deletes.
   895  	for i := 0; i < numWorkers; i++ {
   896  		go parallelRandStorer(t, m, numIters, numEntries, cdone)
   897  		go parallelRandDeleter(t, m, numIters, numEntries, cdone)
   898  	}
   899  	// Wait for the goroutines to finish.
   900  	for i := 0; i < 2*numWorkers; i++ {
   901  		<-cdone
   902  	}
   903  }
   904  
   905  func parallelComputer(t *testing.T, m *Map, numIters, numEntries int, cdone chan bool) {
   906  	for i := 0; i < numIters; i++ {
   907  		for j := 0; j < numEntries; j++ {
   908  			m.Compute(strconv.Itoa(j), func(oldValue interface{}, loaded bool) (newValue interface{}, delete bool) {
   909  				if !loaded {
   910  					return uint64(1), false
   911  				}
   912  				return uint64(oldValue.(uint64) + 1), false
   913  			})
   914  		}
   915  	}
   916  	cdone <- true
   917  }
   918  
   919  func TestMapParallelComputes(t *testing.T) {
   920  	const numWorkers = 4 // Also stands for numEntries.
   921  	const numIters = 10_000
   922  	m := NewMap()
   923  	cdone := make(chan bool)
   924  	for i := 0; i < numWorkers; i++ {
   925  		go parallelComputer(t, m, numIters, numWorkers, cdone)
   926  	}
   927  	// Wait for the goroutines to finish.
   928  	for i := 0; i < numWorkers; i++ {
   929  		<-cdone
   930  	}
   931  	// Verify map contents.
   932  	for i := 0; i < numWorkers; i++ {
   933  		v, ok := m.Load(strconv.Itoa(i))
   934  		if !ok {
   935  			t.Fatalf("value not found for %d", i)
   936  		}
   937  		if v.(uint64) != numWorkers*numIters {
   938  			t.Fatalf("values do not match for %d: %v", i, v)
   939  		}
   940  	}
   941  }
   942  
   943  func parallelRangeStorer(t *testing.T, m *Map, numEntries int, stopFlag *int64, cdone chan bool) {
   944  	for {
   945  		for i := 0; i < numEntries; i++ {
   946  			m.Store(strconv.Itoa(i), i)
   947  		}
   948  		if atomic.LoadInt64(stopFlag) != 0 {
   949  			break
   950  		}
   951  	}
   952  	cdone <- true
   953  }
   954  
   955  func parallelRangeDeleter(t *testing.T, m *Map, numEntries int, stopFlag *int64, cdone chan bool) {
   956  	for {
   957  		for i := 0; i < numEntries; i++ {
   958  			m.Delete(strconv.Itoa(i))
   959  		}
   960  		if atomic.LoadInt64(stopFlag) != 0 {
   961  			break
   962  		}
   963  	}
   964  	cdone <- true
   965  }
   966  
   967  func TestMapParallelRange(t *testing.T) {
   968  	const numEntries = 10_000
   969  	m := NewMap()
   970  	for i := 0; i < numEntries; i++ {
   971  		m.Store(strconv.Itoa(i), i)
   972  	}
   973  	// Start goroutines that would be storing and deleting items in parallel.
   974  	cdone := make(chan bool)
   975  	stopFlag := int64(0)
   976  	go parallelRangeStorer(t, m, numEntries, &stopFlag, cdone)
   977  	go parallelRangeDeleter(t, m, numEntries, &stopFlag, cdone)
   978  	// Iterate the map and verify that no duplicate keys were met.
   979  	met := make(map[string]int)
   980  	m.Range(func(key string, value interface{}) bool {
   981  		if key != strconv.Itoa(value.(int)) {
   982  			t.Fatalf("got unexpected value for key %s: %v", key, value)
   983  			return false
   984  		}
   985  		met[key] += 1
   986  		return true
   987  	})
   988  	if len(met) == 0 {
   989  		t.Fatal("no entries were met when iterating")
   990  	}
   991  	for k, c := range met {
   992  		if c != 1 {
   993  			t.Fatalf("met key %s multiple times: %d", k, c)
   994  		}
   995  	}
   996  	// Make sure that both goroutines finish.
   997  	atomic.StoreInt64(&stopFlag, 1)
   998  	<-cdone
   999  	<-cdone
  1000  }
  1001  
  1002  func parallelShrinker(t *testing.T, m *Map, numIters, numEntries int, stopFlag *int64, cdone chan bool) {
  1003  	for i := 0; i < numIters; i++ {
  1004  		for j := 0; j < numEntries; j++ {
  1005  			if p, loaded := m.LoadOrStore(strconv.Itoa(j), &point{int32(j), int32(j)}); loaded {
  1006  				t.Errorf("value was present for %d: %v", j, p)
  1007  			}
  1008  		}
  1009  		for j := 0; j < numEntries; j++ {
  1010  			m.Delete(strconv.Itoa(j))
  1011  		}
  1012  	}
  1013  	atomic.StoreInt64(stopFlag, 1)
  1014  	cdone <- true
  1015  }
  1016  
  1017  func parallelUpdater(t *testing.T, m *Map, idx int, stopFlag *int64, cdone chan bool) {
  1018  	for atomic.LoadInt64(stopFlag) != 1 {
  1019  		sleepUs := int(Fastrand() % 10)
  1020  		if p, loaded := m.LoadOrStore(strconv.Itoa(idx), &point{int32(idx), int32(idx)}); loaded {
  1021  			t.Errorf("value was present for %d: %v", idx, p)
  1022  		}
  1023  		time.Sleep(time.Duration(sleepUs) * time.Microsecond)
  1024  		if _, ok := m.Load(strconv.Itoa(idx)); !ok {
  1025  			t.Errorf("value was not found for %d", idx)
  1026  		}
  1027  		m.Delete(strconv.Itoa(idx))
  1028  	}
  1029  	cdone <- true
  1030  }
  1031  
  1032  func TestMapDoesNotLoseEntriesOnResize(t *testing.T) {
  1033  	const numIters = 10_000
  1034  	const numEntries = 128
  1035  	m := NewMap()
  1036  	cdone := make(chan bool)
  1037  	stopFlag := int64(0)
  1038  	go parallelShrinker(t, m, numIters, numEntries, &stopFlag, cdone)
  1039  	go parallelUpdater(t, m, numEntries, &stopFlag, cdone)
  1040  	// Wait for the goroutines to finish.
  1041  	<-cdone
  1042  	<-cdone
  1043  	// Verify map contents.
  1044  	if s := m.Size(); s != 0 {
  1045  		t.Fatalf("map is not empty: %d", s)
  1046  	}
  1047  }
  1048  
  1049  func TestMapTopHashMutex(t *testing.T) {
  1050  	const (
  1051  		numLockers    = 4
  1052  		numIterations = 1000
  1053  	)
  1054  	var (
  1055  		activity int32
  1056  		mu       uint64
  1057  	)
  1058  	cdone := make(chan bool)
  1059  	for i := 0; i < numLockers; i++ {
  1060  		go func() {
  1061  			for i := 0; i < numIterations; i++ {
  1062  				LockBucket(&mu)
  1063  				n := atomic.AddInt32(&activity, 1)
  1064  				if n != 1 {
  1065  					UnlockBucket(&mu)
  1066  					panic(fmt.Sprintf("lock(%d)\n", n))
  1067  				}
  1068  				atomic.AddInt32(&activity, -1)
  1069  				UnlockBucket(&mu)
  1070  			}
  1071  			cdone <- true
  1072  		}()
  1073  	}
  1074  	// Wait for all lockers to finish.
  1075  	for i := 0; i < numLockers; i++ {
  1076  		<-cdone
  1077  	}
  1078  }
  1079  
  1080  func TestMapTopHashMutex_Store_NoLock(t *testing.T) {
  1081  	mu := uint64(0)
  1082  	testMapTopHashMutex_Store(t, &mu)
  1083  }
  1084  
  1085  func TestMapTopHashMutex_Store_WhileLocked(t *testing.T) {
  1086  	mu := uint64(0)
  1087  	LockBucket(&mu)
  1088  	defer UnlockBucket(&mu)
  1089  	testMapTopHashMutex_Store(t, &mu)
  1090  }
  1091  
  1092  func testMapTopHashMutex_Store(t *testing.T, topHashes *uint64) {
  1093  	hash := uint64(0b1101_0100_1010_1011_1101 << 44)
  1094  	for i := 0; i < EntriesPerMapBucket; i++ {
  1095  		if TopHashMatch(hash, *topHashes, i) {
  1096  			t.Fatalf("top hash match for all zeros for index %d", i)
  1097  		}
  1098  
  1099  		prevOnes := bits.OnesCount64(*topHashes)
  1100  		*topHashes = StoreTopHash(hash, *topHashes, i)
  1101  		newOnes := bits.OnesCount64(*topHashes)
  1102  		expectedInc := bits.OnesCount64(hash) + 1
  1103  		if newOnes != prevOnes+expectedInc {
  1104  			t.Fatalf("unexpected bits change after store for index %d: %d, %d, %#b",
  1105  				i, newOnes, prevOnes+expectedInc, *topHashes)
  1106  		}
  1107  
  1108  		if !TopHashMatch(hash, *topHashes, i) {
  1109  			t.Fatalf("top hash mismatch after store for index %d: %#b", i, *topHashes)
  1110  		}
  1111  	}
  1112  }
  1113  
  1114  func TestMapTopHashMutex_Erase_NoLock(t *testing.T) {
  1115  	mu := uint64(0)
  1116  	testMapTopHashMutex_Erase(t, &mu)
  1117  }
  1118  
  1119  func TestMapTopHashMutex_Erase_WhileLocked(t *testing.T) {
  1120  	mu := uint64(0)
  1121  	LockBucket(&mu)
  1122  	defer UnlockBucket(&mu)
  1123  	testMapTopHashMutex_Erase(t, &mu)
  1124  }
  1125  
  1126  func testMapTopHashMutex_Erase(t *testing.T, topHashes *uint64) {
  1127  	hash := uint64(0xababaaaaaaaaaaaa) // top hash is 1010_1011_1010_1011_1010
  1128  	for i := 0; i < EntriesPerMapBucket; i++ {
  1129  		*topHashes = StoreTopHash(hash, *topHashes, i)
  1130  		ones := bits.OnesCount64(*topHashes)
  1131  
  1132  		*topHashes = EraseTopHash(*topHashes, i)
  1133  		if TopHashMatch(hash, *topHashes, i) {
  1134  			t.Fatalf("top hash match after erase for index %d: %#b", i, *topHashes)
  1135  		}
  1136  
  1137  		erasedBits := ones - bits.OnesCount64(*topHashes)
  1138  		if erasedBits != 1 {
  1139  			t.Fatalf("more than one bit changed after erase: %d, %d", i, erasedBits)
  1140  		}
  1141  	}
  1142  }
  1143  
  1144  func TestMapTopHashMutex_StoreAfterErase_NoLock(t *testing.T) {
  1145  	mu := uint64(0)
  1146  	testMapTopHashMutex_StoreAfterErase(t, &mu)
  1147  }
  1148  
  1149  func TestMapTopHashMutex_StoreAfterErase_WhileLocked(t *testing.T) {
  1150  	mu := uint64(0)
  1151  	LockBucket(&mu)
  1152  	defer UnlockBucket(&mu)
  1153  	testMapTopHashMutex_StoreAfterErase(t, &mu)
  1154  }
  1155  
  1156  func testMapTopHashMutex_StoreAfterErase(t *testing.T, topHashes *uint64) {
  1157  	hashOne := uint64(0b1101_0100_1101_0100_1101_1111 << 40) // top hash is 1101_0100_1101_0100_1101
  1158  	hashTwo := uint64(0b1010_1011_1010_1011_1010_1111 << 40) // top hash is 1010_1011_1010_1011_1010
  1159  	idx := 2
  1160  
  1161  	*topHashes = StoreTopHash(hashOne, *topHashes, idx)
  1162  	if !TopHashMatch(hashOne, *topHashes, idx) {
  1163  		t.Fatalf("top hash mismatch for hash one: %#b, %#b", hashOne, *topHashes)
  1164  	}
  1165  	if TopHashMatch(hashTwo, *topHashes, idx) {
  1166  		t.Fatalf("top hash match for hash two: %#b, %#b", hashTwo, *topHashes)
  1167  	}
  1168  
  1169  	*topHashes = EraseTopHash(*topHashes, idx)
  1170  	*topHashes = StoreTopHash(hashTwo, *topHashes, idx)
  1171  	if TopHashMatch(hashOne, *topHashes, idx) {
  1172  		t.Fatalf("top hash match for hash one: %#b, %#b", hashOne, *topHashes)
  1173  	}
  1174  	if !TopHashMatch(hashTwo, *topHashes, idx) {
  1175  		t.Fatalf("top hash mismatch for hash two: %#b, %#b", hashTwo, *topHashes)
  1176  	}
  1177  }
  1178  
  1179  func BenchmarkMap_NoWarmUp(b *testing.B) {
  1180  	for _, bc := range benchmarkCases {
  1181  		if bc.readPercentage == 100 {
  1182  			// This benchmark doesn't make sense without a warm-up.
  1183  			continue
  1184  		}
  1185  		b.Run(bc.name, func(b *testing.B) {
  1186  			m := NewMap()
  1187  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1188  				return m.Load(k)
  1189  			}, func(k string, v interface{}) {
  1190  				m.Store(k, v)
  1191  			}, func(k string) {
  1192  				m.Delete(k)
  1193  			}, bc.readPercentage)
  1194  		})
  1195  	}
  1196  }
  1197  
  1198  func BenchmarkMapStandard_NoWarmUp(b *testing.B) {
  1199  	for _, bc := range benchmarkCases {
  1200  		if bc.readPercentage == 100 {
  1201  			// This benchmark doesn't make sense without a warm-up.
  1202  			continue
  1203  		}
  1204  		b.Run(bc.name, func(b *testing.B) {
  1205  			var m sync.Map
  1206  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1207  				return m.Load(k)
  1208  			}, func(k string, v interface{}) {
  1209  				m.Store(k, v)
  1210  			}, func(k string) {
  1211  				m.Delete(k)
  1212  			}, bc.readPercentage)
  1213  		})
  1214  	}
  1215  }
  1216  
  1217  func BenchmarkMap_WarmUp(b *testing.B) {
  1218  	for _, bc := range benchmarkCases {
  1219  		b.Run(bc.name, func(b *testing.B) {
  1220  			m := NewMapPresized(benchmarkNumEntries)
  1221  			for i := 0; i < benchmarkNumEntries; i++ {
  1222  				m.Store(benchmarkKeyPrefix+strconv.Itoa(i), i)
  1223  			}
  1224  			b.ResetTimer()
  1225  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1226  				return m.Load(k)
  1227  			}, func(k string, v interface{}) {
  1228  				m.Store(k, v)
  1229  			}, func(k string) {
  1230  				m.Delete(k)
  1231  			}, bc.readPercentage)
  1232  		})
  1233  	}
  1234  }
  1235  
  1236  // This is a nice scenario for sync.Map since a lot of updates
  1237  // will hit the readOnly part of the map.
  1238  func BenchmarkMapStandard_WarmUp(b *testing.B) {
  1239  	for _, bc := range benchmarkCases {
  1240  		b.Run(bc.name, func(b *testing.B) {
  1241  			var m sync.Map
  1242  			for i := 0; i < benchmarkNumEntries; i++ {
  1243  				m.Store(benchmarkKeyPrefix+strconv.Itoa(i), i)
  1244  			}
  1245  			b.ResetTimer()
  1246  			benchmarkMap(b, func(k string) (interface{}, bool) {
  1247  				return m.Load(k)
  1248  			}, func(k string, v interface{}) {
  1249  				m.Store(k, v)
  1250  			}, func(k string) {
  1251  				m.Delete(k)
  1252  			}, bc.readPercentage)
  1253  		})
  1254  	}
  1255  }
  1256  
  1257  func benchmarkMap(
  1258  	b *testing.B,
  1259  	loadFn func(k string) (interface{}, bool),
  1260  	storeFn func(k string, v interface{}),
  1261  	deleteFn func(k string),
  1262  	readPercentage int,
  1263  ) {
  1264  	runParallel(b, func(pb *testing.PB) {
  1265  		// convert percent to permille to support 99% case
  1266  		storeThreshold := 10 * readPercentage
  1267  		deleteThreshold := 10*readPercentage + ((1000 - 10*readPercentage) / 2)
  1268  		for pb.Next() {
  1269  			op := int(Fastrand() % 1000)
  1270  			i := int(Fastrand() % benchmarkNumEntries)
  1271  			if op >= deleteThreshold {
  1272  				deleteFn(benchmarkKeys[i])
  1273  			} else if op >= storeThreshold {
  1274  				storeFn(benchmarkKeys[i], i)
  1275  			} else {
  1276  				loadFn(benchmarkKeys[i])
  1277  			}
  1278  		}
  1279  	})
  1280  }
  1281  
  1282  func BenchmarkMapRange(b *testing.B) {
  1283  	m := NewMap()
  1284  	for i := 0; i < benchmarkNumEntries; i++ {
  1285  		m.Store(benchmarkKeys[i], i)
  1286  	}
  1287  	b.ResetTimer()
  1288  	runParallel(b, func(pb *testing.PB) {
  1289  		foo := 0
  1290  		for pb.Next() {
  1291  			m.Range(func(key string, value interface{}) bool {
  1292  				// Dereference the value to have an apple-to-apple
  1293  				// comparison with MapOf.Range.
  1294  				_ = value.(int)
  1295  				foo++
  1296  				return true
  1297  			})
  1298  			_ = foo
  1299  		}
  1300  	})
  1301  }
  1302  
  1303  func BenchmarkMapRangeStandard(b *testing.B) {
  1304  	var m sync.Map
  1305  	for i := 0; i < benchmarkNumEntries; i++ {
  1306  		m.Store(benchmarkKeys[i], i)
  1307  	}
  1308  	b.ResetTimer()
  1309  	runParallel(b, func(pb *testing.PB) {
  1310  		foo := 0
  1311  		for pb.Next() {
  1312  			m.Range(func(key interface{}, value interface{}) bool {
  1313  				// Dereference the key and the value to have an apple-to-apple
  1314  				// comparison with MapOf.Range.
  1315  				_, _ = key.(string), value.(int)
  1316  				foo++
  1317  				return true
  1318  			})
  1319  			_ = foo
  1320  		}
  1321  	})
  1322  }