github.com/fufuok/utils@v1.0.10/xsync/mapof_test.go (about)

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