github.com/puzpuzpuz/xsync/v3@v3.1.1-0.20240225193106-cbe4ec1e954f/mapof_test.go (about)

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