Source file src/runtime/map_benchmark_test.go

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"encoding/binary"
     9  	"flag"
    10  	"fmt"
    11  	"math/rand"
    12  	"runtime"
    13  	"slices"
    14  	"strconv"
    15  	"strings"
    16  	"testing"
    17  	"unsafe"
    18  )
    19  
    20  var mapbench = flag.Bool("mapbench", false, "enable the full set of map benchmark variants")
    21  
    22  const size = 10
    23  
    24  func BenchmarkHashStringSpeed(b *testing.B) {
    25  	strings := make([]string, size)
    26  	for i := 0; i < size; i++ {
    27  		strings[i] = fmt.Sprintf("string#%d", i)
    28  	}
    29  	sum := 0
    30  	m := make(map[string]int, size)
    31  	for i := 0; i < size; i++ {
    32  		m[strings[i]] = 0
    33  	}
    34  	idx := 0
    35  	b.ResetTimer()
    36  	for i := 0; i < b.N; i++ {
    37  		sum += m[strings[idx]]
    38  		idx++
    39  		if idx == size {
    40  			idx = 0
    41  		}
    42  	}
    43  }
    44  
    45  type chunk [17]byte
    46  
    47  func BenchmarkHashBytesSpeed(b *testing.B) {
    48  	// a bunch of chunks, each with a different alignment mod 16
    49  	var chunks [size]chunk
    50  	// initialize each to a different value
    51  	for i := 0; i < size; i++ {
    52  		chunks[i][0] = byte(i)
    53  	}
    54  	// put into a map
    55  	m := make(map[chunk]int, size)
    56  	for i, c := range chunks {
    57  		m[c] = i
    58  	}
    59  	idx := 0
    60  	b.ResetTimer()
    61  	for i := 0; i < b.N; i++ {
    62  		if m[chunks[idx]] != idx {
    63  			b.Error("bad map entry for chunk")
    64  		}
    65  		idx++
    66  		if idx == size {
    67  			idx = 0
    68  		}
    69  	}
    70  }
    71  
    72  func BenchmarkHashInt32Speed(b *testing.B) {
    73  	ints := make([]int32, size)
    74  	for i := 0; i < size; i++ {
    75  		ints[i] = int32(i)
    76  	}
    77  	sum := 0
    78  	m := make(map[int32]int, size)
    79  	for i := 0; i < size; i++ {
    80  		m[ints[i]] = 0
    81  	}
    82  	idx := 0
    83  	b.ResetTimer()
    84  	for i := 0; i < b.N; i++ {
    85  		sum += m[ints[idx]]
    86  		idx++
    87  		if idx == size {
    88  			idx = 0
    89  		}
    90  	}
    91  }
    92  
    93  func BenchmarkHashInt64Speed(b *testing.B) {
    94  	ints := make([]int64, size)
    95  	for i := 0; i < size; i++ {
    96  		ints[i] = int64(i)
    97  	}
    98  	sum := 0
    99  	m := make(map[int64]int, size)
   100  	for i := 0; i < size; i++ {
   101  		m[ints[i]] = 0
   102  	}
   103  	idx := 0
   104  	b.ResetTimer()
   105  	for i := 0; i < b.N; i++ {
   106  		sum += m[ints[idx]]
   107  		idx++
   108  		if idx == size {
   109  			idx = 0
   110  		}
   111  	}
   112  }
   113  func BenchmarkHashStringArraySpeed(b *testing.B) {
   114  	stringpairs := make([][2]string, size)
   115  	for i := 0; i < size; i++ {
   116  		for j := 0; j < 2; j++ {
   117  			stringpairs[i][j] = fmt.Sprintf("string#%d/%d", i, j)
   118  		}
   119  	}
   120  	sum := 0
   121  	m := make(map[[2]string]int, size)
   122  	for i := 0; i < size; i++ {
   123  		m[stringpairs[i]] = 0
   124  	}
   125  	idx := 0
   126  	b.ResetTimer()
   127  	for i := 0; i < b.N; i++ {
   128  		sum += m[stringpairs[idx]]
   129  		idx++
   130  		if idx == size {
   131  			idx = 0
   132  		}
   133  	}
   134  }
   135  
   136  func BenchmarkMegMap(b *testing.B) {
   137  	m := make(map[string]bool)
   138  	for suffix := 'A'; suffix <= 'G'; suffix++ {
   139  		m[strings.Repeat("X", 1<<20-1)+fmt.Sprint(suffix)] = true
   140  	}
   141  	key := strings.Repeat("X", 1<<20-1) + "k"
   142  	b.ResetTimer()
   143  	for i := 0; i < b.N; i++ {
   144  		_, _ = m[key]
   145  	}
   146  }
   147  
   148  func BenchmarkMegOneMap(b *testing.B) {
   149  	m := make(map[string]bool)
   150  	m[strings.Repeat("X", 1<<20)] = true
   151  	key := strings.Repeat("Y", 1<<20)
   152  	b.ResetTimer()
   153  	for i := 0; i < b.N; i++ {
   154  		_, _ = m[key]
   155  	}
   156  }
   157  
   158  func BenchmarkMegEqMap(b *testing.B) {
   159  	m := make(map[string]bool)
   160  	key1 := strings.Repeat("X", 1<<20)
   161  	key2 := strings.Repeat("X", 1<<20) // equal but different instance
   162  	m[key1] = true
   163  	b.ResetTimer()
   164  	for i := 0; i < b.N; i++ {
   165  		_, _ = m[key2]
   166  	}
   167  }
   168  
   169  func BenchmarkMegEmptyMap(b *testing.B) {
   170  	m := make(map[string]bool)
   171  	key := strings.Repeat("X", 1<<20)
   172  	b.ResetTimer()
   173  	for i := 0; i < b.N; i++ {
   174  		_, _ = m[key]
   175  	}
   176  }
   177  
   178  func BenchmarkMegEmptyMapWithInterfaceKey(b *testing.B) {
   179  	m := make(map[any]bool)
   180  	key := strings.Repeat("X", 1<<20)
   181  	b.ResetTimer()
   182  	for i := 0; i < b.N; i++ {
   183  		_, _ = m[key]
   184  	}
   185  }
   186  
   187  func BenchmarkSmallStrMap(b *testing.B) {
   188  	m := make(map[string]bool)
   189  	for suffix := 'A'; suffix <= 'G'; suffix++ {
   190  		m[fmt.Sprint(suffix)] = true
   191  	}
   192  	key := "k"
   193  	b.ResetTimer()
   194  	for i := 0; i < b.N; i++ {
   195  		_, _ = m[key]
   196  	}
   197  }
   198  
   199  func BenchmarkMapStringKeysEight_16(b *testing.B) { benchmarkMapStringKeysEight(b, 16) }
   200  func BenchmarkMapStringKeysEight_32(b *testing.B) { benchmarkMapStringKeysEight(b, 32) }
   201  func BenchmarkMapStringKeysEight_64(b *testing.B) { benchmarkMapStringKeysEight(b, 64) }
   202  func BenchmarkMapStringKeysEight_1M(b *testing.B) { benchmarkMapStringKeysEight(b, 1<<20) }
   203  
   204  func benchmarkMapStringKeysEight(b *testing.B, keySize int) {
   205  	m := make(map[string]bool)
   206  	for i := 0; i < 8; i++ {
   207  		m[strings.Repeat("K", i+1)] = true
   208  	}
   209  	key := strings.Repeat("K", keySize)
   210  	b.ResetTimer()
   211  	for i := 0; i < b.N; i++ {
   212  		_ = m[key]
   213  	}
   214  }
   215  
   216  func BenchmarkMapFirst(b *testing.B) {
   217  	for n := 1; n <= 16; n++ {
   218  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   219  			m := make(map[int]bool)
   220  			for i := 0; i < n; i++ {
   221  				m[i] = true
   222  			}
   223  			b.ResetTimer()
   224  			for i := 0; i < b.N; i++ {
   225  				_ = m[0]
   226  			}
   227  		})
   228  	}
   229  }
   230  func BenchmarkMapMid(b *testing.B) {
   231  	for n := 1; n <= 16; n++ {
   232  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   233  			m := make(map[int]bool)
   234  			for i := 0; i < n; i++ {
   235  				m[i] = true
   236  			}
   237  			b.ResetTimer()
   238  			for i := 0; i < b.N; i++ {
   239  				_ = m[n>>1]
   240  			}
   241  		})
   242  	}
   243  }
   244  func BenchmarkMapLast(b *testing.B) {
   245  	for n := 1; n <= 16; n++ {
   246  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   247  			m := make(map[int]bool)
   248  			for i := 0; i < n; i++ {
   249  				m[i] = true
   250  			}
   251  			b.ResetTimer()
   252  			for i := 0; i < b.N; i++ {
   253  				_ = m[n-1]
   254  			}
   255  		})
   256  	}
   257  }
   258  
   259  func BenchmarkMapCycle(b *testing.B) {
   260  	// Arrange map entries to be a permutation, so that
   261  	// we hit all entries, and one lookup is data dependent
   262  	// on the previous lookup.
   263  	const N = 3127
   264  	p := rand.New(rand.NewSource(1)).Perm(N)
   265  	m := map[int]int{}
   266  	for i := 0; i < N; i++ {
   267  		m[i] = p[i]
   268  	}
   269  	b.ResetTimer()
   270  	j := 0
   271  	for i := 0; i < b.N; i++ {
   272  		j = m[j]
   273  	}
   274  	sink = uint64(j)
   275  }
   276  
   277  // Accessing the same keys in a row.
   278  func benchmarkRepeatedLookup(b *testing.B, lookupKeySize int) {
   279  	m := make(map[string]bool)
   280  	// At least bigger than a single bucket:
   281  	for i := 0; i < 64; i++ {
   282  		m[fmt.Sprintf("some key %d", i)] = true
   283  	}
   284  	base := strings.Repeat("x", lookupKeySize-1)
   285  	key1 := base + "1"
   286  	key2 := base + "2"
   287  	b.ResetTimer()
   288  	for i := 0; i < b.N/4; i++ {
   289  		_ = m[key1]
   290  		_ = m[key1]
   291  		_ = m[key2]
   292  		_ = m[key2]
   293  	}
   294  }
   295  
   296  func BenchmarkRepeatedLookupStrMapKey32(b *testing.B) { benchmarkRepeatedLookup(b, 32) }
   297  func BenchmarkRepeatedLookupStrMapKey1M(b *testing.B) { benchmarkRepeatedLookup(b, 1<<20) }
   298  
   299  func BenchmarkMakeMap(b *testing.B) {
   300  	b.Run("[Byte]Byte", func(b *testing.B) {
   301  		var m map[byte]byte
   302  		for i := 0; i < b.N; i++ {
   303  			m = make(map[byte]byte, 10)
   304  		}
   305  		hugeSink = m
   306  	})
   307  	b.Run("[Int]Int", func(b *testing.B) {
   308  		var m map[int]int
   309  		for i := 0; i < b.N; i++ {
   310  			m = make(map[int]int, 10)
   311  		}
   312  		hugeSink = m
   313  	})
   314  }
   315  
   316  func BenchmarkNewEmptyMap(b *testing.B) {
   317  	b.ReportAllocs()
   318  	for i := 0; i < b.N; i++ {
   319  		_ = make(map[int]int)
   320  	}
   321  }
   322  
   323  func BenchmarkNewSmallMap(b *testing.B) {
   324  	b.ReportAllocs()
   325  	for i := 0; i < b.N; i++ {
   326  		m := make(map[int]int)
   327  		m[0] = 0
   328  		m[1] = 1
   329  	}
   330  }
   331  
   332  func BenchmarkSameLengthMap(b *testing.B) {
   333  	// long strings, same length, differ in first few
   334  	// and last few bytes.
   335  	m := make(map[string]bool)
   336  	s1 := "foo" + strings.Repeat("-", 100) + "bar"
   337  	s2 := "goo" + strings.Repeat("-", 100) + "ber"
   338  	m[s1] = true
   339  	m[s2] = true
   340  	b.ResetTimer()
   341  	for i := 0; i < b.N; i++ {
   342  		_ = m[s1]
   343  	}
   344  }
   345  
   346  func BenchmarkSmallKeyMap(b *testing.B) {
   347  	m := make(map[int16]bool)
   348  	m[5] = true
   349  	for i := 0; i < b.N; i++ {
   350  		_ = m[5]
   351  	}
   352  }
   353  
   354  func BenchmarkMapPopulate(b *testing.B) {
   355  	for size := 1; size < 1000000; size *= 10 {
   356  		b.Run(strconv.Itoa(size), func(b *testing.B) {
   357  			b.ReportAllocs()
   358  			for i := 0; i < b.N; i++ {
   359  				m := make(map[int]bool)
   360  				for j := 0; j < size; j++ {
   361  					m[j] = true
   362  				}
   363  			}
   364  		})
   365  	}
   366  }
   367  
   368  type ComplexAlgKey struct {
   369  	a, b, c int64
   370  	_       int
   371  	d       int32
   372  	_       int
   373  	e       string
   374  	_       int
   375  	f, g, h int64
   376  }
   377  
   378  func BenchmarkComplexAlgMap(b *testing.B) {
   379  	m := make(map[ComplexAlgKey]bool)
   380  	var k ComplexAlgKey
   381  	m[k] = true
   382  	for i := 0; i < b.N; i++ {
   383  		_ = m[k]
   384  	}
   385  }
   386  
   387  func BenchmarkGoMapClear(b *testing.B) {
   388  	b.Run("Reflexive", func(b *testing.B) {
   389  		for size := 1; size < 100000; size *= 10 {
   390  			b.Run(strconv.Itoa(size), func(b *testing.B) {
   391  				m := make(map[int]int, size)
   392  				for i := 0; i < b.N; i++ {
   393  					m[0] = size // Add one element so len(m) != 0 avoiding fast paths.
   394  					clear(m)
   395  				}
   396  			})
   397  		}
   398  	})
   399  	b.Run("NonReflexive", func(b *testing.B) {
   400  		for size := 1; size < 100000; size *= 10 {
   401  			b.Run(strconv.Itoa(size), func(b *testing.B) {
   402  				m := make(map[float64]int, size)
   403  				for i := 0; i < b.N; i++ {
   404  					m[1.0] = size // Add one element so len(m) != 0 avoiding fast paths.
   405  					clear(m)
   406  				}
   407  			})
   408  		}
   409  	})
   410  }
   411  
   412  func BenchmarkMapStringConversion(b *testing.B) {
   413  	for _, length := range []int{32, 64} {
   414  		b.Run(strconv.Itoa(length), func(b *testing.B) {
   415  			bytes := make([]byte, length)
   416  			b.Run("simple", func(b *testing.B) {
   417  				b.ReportAllocs()
   418  				m := make(map[string]int)
   419  				m[string(bytes)] = 0
   420  				for i := 0; i < b.N; i++ {
   421  					_ = m[string(bytes)]
   422  				}
   423  			})
   424  			b.Run("struct", func(b *testing.B) {
   425  				b.ReportAllocs()
   426  				type stringstruct struct{ s string }
   427  				m := make(map[stringstruct]int)
   428  				m[stringstruct{string(bytes)}] = 0
   429  				for i := 0; i < b.N; i++ {
   430  					_ = m[stringstruct{string(bytes)}]
   431  				}
   432  			})
   433  			b.Run("array", func(b *testing.B) {
   434  				b.ReportAllocs()
   435  				type stringarray [1]string
   436  				m := make(map[stringarray]int)
   437  				m[stringarray{string(bytes)}] = 0
   438  				for i := 0; i < b.N; i++ {
   439  					_ = m[stringarray{string(bytes)}]
   440  				}
   441  			})
   442  		})
   443  	}
   444  }
   445  
   446  var BoolSink bool
   447  
   448  func BenchmarkMapInterfaceString(b *testing.B) {
   449  	m := map[any]bool{}
   450  
   451  	for i := 0; i < 100; i++ {
   452  		m[fmt.Sprintf("%d", i)] = true
   453  	}
   454  
   455  	key := (any)("A")
   456  	b.ResetTimer()
   457  	for i := 0; i < b.N; i++ {
   458  		BoolSink = m[key]
   459  	}
   460  }
   461  func BenchmarkMapInterfacePtr(b *testing.B) {
   462  	m := map[any]bool{}
   463  
   464  	for i := 0; i < 100; i++ {
   465  		i := i
   466  		m[&i] = true
   467  	}
   468  
   469  	key := new(int)
   470  	b.ResetTimer()
   471  	for i := 0; i < b.N; i++ {
   472  		BoolSink = m[key]
   473  	}
   474  }
   475  
   476  var (
   477  	hintLessThan8    = 7
   478  	hintGreaterThan8 = 32
   479  )
   480  
   481  func BenchmarkNewEmptyMapHintLessThan8(b *testing.B) {
   482  	b.ReportAllocs()
   483  	for i := 0; i < b.N; i++ {
   484  		_ = make(map[int]int, hintLessThan8)
   485  	}
   486  }
   487  
   488  func BenchmarkNewEmptyMapHintGreaterThan8(b *testing.B) {
   489  	b.ReportAllocs()
   490  	for i := 0; i < b.N; i++ {
   491  		_ = make(map[int]int, hintGreaterThan8)
   492  	}
   493  }
   494  
   495  func benchSizes(f func(b *testing.B, n int)) func(*testing.B) {
   496  	var cases = []int{
   497  		0,
   498  		6,
   499  		12,
   500  		18,
   501  		24,
   502  		30,
   503  		64,
   504  		128,
   505  		256,
   506  		512,
   507  		1024,
   508  		2048,
   509  		4096,
   510  		8192,
   511  		1 << 16,
   512  		1 << 18,
   513  		1 << 20,
   514  		1 << 22,
   515  	}
   516  
   517  	// Cases enabled by default. Set -mapbench for the remainder.
   518  	//
   519  	// With the other type combinations, there are literally thousands of
   520  	// variations. It take too long to run all of these as part of
   521  	// builders.
   522  	byDefault := map[int]bool{
   523  		6:       true,
   524  		64:      true,
   525  		1 << 16: true,
   526  	}
   527  
   528  	return func(b *testing.B) {
   529  		for _, n := range cases {
   530  			b.Run("len="+strconv.Itoa(n), func(b *testing.B) {
   531  				if !*mapbench && !byDefault[n] {
   532  					b.Skip("Skipped because -mapbench=false")
   533  				}
   534  
   535  				f(b, n)
   536  			})
   537  		}
   538  	}
   539  }
   540  
   541  // A 16 byte type.
   542  type smallType [16]byte
   543  
   544  // A 512 byte type.
   545  type mediumType [1 << 9]byte
   546  
   547  // A 4KiB type.
   548  type bigType [1 << 12]byte
   549  
   550  type mapBenchmarkKeyType interface {
   551  	int32 | int64 | string | smallType | mediumType | bigType | *int32
   552  }
   553  
   554  type mapBenchmarkElemType interface {
   555  	mapBenchmarkKeyType | []int32
   556  }
   557  
   558  func genIntValues[T int | int32 | int64](start, end int) []T {
   559  	vals := make([]T, 0, end-start)
   560  	for i := start; i < end; i++ {
   561  		vals = append(vals, T(i))
   562  	}
   563  	return vals
   564  }
   565  
   566  func genStringValues(start, end int) []string {
   567  	vals := make([]string, 0, end-start)
   568  	for i := start; i < end; i++ {
   569  		vals = append(vals, strconv.Itoa(i))
   570  	}
   571  	return vals
   572  }
   573  
   574  func genSmallValues(start, end int) []smallType {
   575  	vals := make([]smallType, 0, end-start)
   576  	for i := start; i < end; i++ {
   577  		var v smallType
   578  		binary.NativeEndian.PutUint64(v[:], uint64(i))
   579  		vals = append(vals, v)
   580  	}
   581  	return vals
   582  }
   583  
   584  func genMediumValues(start, end int) []mediumType {
   585  	vals := make([]mediumType, 0, end-start)
   586  	for i := start; i < end; i++ {
   587  		var v mediumType
   588  		binary.NativeEndian.PutUint64(v[:], uint64(i))
   589  		vals = append(vals, v)
   590  	}
   591  	return vals
   592  }
   593  
   594  func genBigValues(start, end int) []bigType {
   595  	vals := make([]bigType, 0, end-start)
   596  	for i := start; i < end; i++ {
   597  		var v bigType
   598  		binary.NativeEndian.PutUint64(v[:], uint64(i))
   599  		vals = append(vals, v)
   600  	}
   601  	return vals
   602  }
   603  
   604  func genPtrValues[T any](start, end int) []*T {
   605  	// Start and end don't mean much. Each pointer by definition has a
   606  	// unique identity.
   607  	vals := make([]*T, 0, end-start)
   608  	for i := start; i < end; i++ {
   609  		v := new(T)
   610  		vals = append(vals, v)
   611  	}
   612  	return vals
   613  }
   614  
   615  func genIntSliceValues[T int | int32 | int64](start, end int) [][]T {
   616  	vals := make([][]T, 0, end-start)
   617  	for i := start; i < end; i++ {
   618  		vals = append(vals, []T{T(i)})
   619  	}
   620  	return vals
   621  }
   622  
   623  func genValues[T mapBenchmarkElemType](start, end int) []T {
   624  	var t T
   625  	switch any(t).(type) {
   626  	case int32:
   627  		return any(genIntValues[int32](start, end)).([]T)
   628  	case int64:
   629  		return any(genIntValues[int64](start, end)).([]T)
   630  	case string:
   631  		return any(genStringValues(start, end)).([]T)
   632  	case smallType:
   633  		return any(genSmallValues(start, end)).([]T)
   634  	case mediumType:
   635  		return any(genMediumValues(start, end)).([]T)
   636  	case bigType:
   637  		return any(genBigValues(start, end)).([]T)
   638  	case *int32:
   639  		return any(genPtrValues[int32](start, end)).([]T)
   640  	case []int32:
   641  		return any(genIntSliceValues[int32](start, end)).([]T)
   642  	default:
   643  		panic("unreachable")
   644  	}
   645  }
   646  
   647  // Avoid inlining to force a heap allocation.
   648  //
   649  //go:noinline
   650  func newSink[T mapBenchmarkElemType]() *T {
   651  	return new(T)
   652  }
   653  
   654  // Return a new maps filled with keys and elems. Both slices must be the same length.
   655  func fillMap[K mapBenchmarkKeyType, E mapBenchmarkElemType](keys []K, elems []E) map[K]E {
   656  	m := make(map[K]E, len(keys))
   657  	for i := range keys {
   658  		m[keys[i]] = elems[i]
   659  	}
   660  	return m
   661  }
   662  
   663  func iterCount(b *testing.B, n int) int {
   664  	// Divide b.N by n so that the ns/op reports time per element,
   665  	// not time per full map iteration. This makes benchmarks of
   666  	// different map sizes more comparable.
   667  	//
   668  	// If size is zero we still need to do iterations.
   669  	if n == 0 {
   670  		return b.N
   671  	}
   672  	return b.N / n
   673  }
   674  
   675  func checkAllocSize[K, E any](b *testing.B, n int) {
   676  	var k K
   677  	size := uint64(n) * uint64(unsafe.Sizeof(k))
   678  	var e E
   679  	size += uint64(n) * uint64(unsafe.Sizeof(e))
   680  
   681  	if size >= 1<<30 {
   682  		b.Skipf("Total key+elem size %d exceeds 1GiB", size)
   683  	}
   684  }
   685  
   686  func benchmarkMapIter[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   687  	checkAllocSize[K, E](b, n)
   688  	k := genValues[K](0, n)
   689  	e := genValues[E](0, n)
   690  	m := fillMap(k, e)
   691  	iterations := iterCount(b, n)
   692  	sinkK := newSink[K]()
   693  	sinkE := newSink[E]()
   694  	b.ResetTimer()
   695  
   696  	for i := 0; i < iterations; i++ {
   697  		for k, e := range m {
   698  			*sinkK = k
   699  			*sinkE = e
   700  		}
   701  	}
   702  }
   703  
   704  func BenchmarkMapIter(b *testing.B) {
   705  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapIter[int32, int32]))
   706  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapIter[int64, int64]))
   707  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapIter[string, string]))
   708  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapIter[smallType, int32]))
   709  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapIter[mediumType, int32]))
   710  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapIter[bigType, int32]))
   711  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapIter[bigType, bigType]))
   712  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapIter[int32, bigType]))
   713  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapIter[*int32, int32]))
   714  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapIter[int32, *int32]))
   715  }
   716  
   717  func benchmarkMapIterLowLoad[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   718  	// Only insert one entry regardless of map size.
   719  	k := genValues[K](0, 1)
   720  	e := genValues[E](0, 1)
   721  
   722  	m := make(map[K]E, n)
   723  	for i := range k {
   724  		m[k[i]] = e[i]
   725  	}
   726  
   727  	iterations := iterCount(b, n)
   728  	sinkK := newSink[K]()
   729  	sinkE := newSink[E]()
   730  	b.ResetTimer()
   731  
   732  	for i := 0; i < iterations; i++ {
   733  		for k, e := range m {
   734  			*sinkK = k
   735  			*sinkE = e
   736  		}
   737  	}
   738  }
   739  
   740  func BenchmarkMapIterLowLoad(b *testing.B) {
   741  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapIterLowLoad[int32, int32]))
   742  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapIterLowLoad[int64, int64]))
   743  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapIterLowLoad[string, string]))
   744  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapIterLowLoad[smallType, int32]))
   745  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapIterLowLoad[mediumType, int32]))
   746  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapIterLowLoad[bigType, int32]))
   747  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapIterLowLoad[bigType, bigType]))
   748  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapIterLowLoad[int32, bigType]))
   749  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapIterLowLoad[*int32, int32]))
   750  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapIterLowLoad[int32, *int32]))
   751  }
   752  
   753  func benchmarkMapAccessHit[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   754  	if n == 0 {
   755  		b.Skip("can't access empty map")
   756  	}
   757  	checkAllocSize[K, E](b, n)
   758  	k := genValues[K](0, n)
   759  	e := genValues[E](0, n)
   760  	m := fillMap(k, e)
   761  	sink := newSink[E]()
   762  	b.ResetTimer()
   763  
   764  	for i := 0; i < b.N; i++ {
   765  		*sink = m[k[i%n]]
   766  	}
   767  }
   768  
   769  func BenchmarkMapAccessHit(b *testing.B) {
   770  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAccessHit[int32, int32]))
   771  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAccessHit[int64, int64]))
   772  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAccessHit[string, string]))
   773  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAccessHit[smallType, int32]))
   774  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAccessHit[mediumType, int32]))
   775  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAccessHit[bigType, int32]))
   776  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapAccessHit[bigType, bigType]))
   777  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapAccessHit[int32, bigType]))
   778  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapAccessHit[*int32, int32]))
   779  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapAccessHit[int32, *int32]))
   780  }
   781  
   782  var sinkOK bool
   783  
   784  func benchmarkMapAccessMiss[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   785  	checkAllocSize[K, E](b, n)
   786  	k := genValues[K](0, n)
   787  	e := genValues[E](0, n)
   788  	m := fillMap(k, e)
   789  	if n == 0 { // Create a lookup values for empty maps.
   790  		n = 1
   791  	}
   792  	w := genValues[K](n, 2*n)
   793  	b.ResetTimer()
   794  
   795  	var ok bool
   796  	for i := 0; i < b.N; i++ {
   797  		_, ok = m[w[i%n]]
   798  	}
   799  
   800  	sinkOK = ok
   801  }
   802  
   803  func BenchmarkMapAccessMiss(b *testing.B) {
   804  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAccessMiss[int32, int32]))
   805  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAccessMiss[int64, int64]))
   806  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAccessMiss[string, string]))
   807  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAccessMiss[smallType, int32]))
   808  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAccessMiss[mediumType, int32]))
   809  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAccessMiss[bigType, int32]))
   810  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapAccessMiss[bigType, bigType]))
   811  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapAccessMiss[int32, bigType]))
   812  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapAccessMiss[*int32, int32]))
   813  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapAccessMiss[int32, *int32]))
   814  }
   815  
   816  // Assign to a key that already exists.
   817  func benchmarkMapAssignExists[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   818  	if n == 0 {
   819  		b.Skip("can't assign to existing keys in empty map")
   820  	}
   821  	checkAllocSize[K, E](b, n)
   822  	k := genValues[K](0, n)
   823  	e := genValues[E](0, n)
   824  	m := fillMap(k, e)
   825  	b.ResetTimer()
   826  
   827  	for i := 0; i < b.N; i++ {
   828  		m[k[i%n]] = e[i%n]
   829  	}
   830  }
   831  
   832  func BenchmarkMapAssignExists(b *testing.B) {
   833  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAssignExists[int32, int32]))
   834  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAssignExists[int64, int64]))
   835  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAssignExists[string, string]))
   836  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAssignExists[smallType, int32]))
   837  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAssignExists[mediumType, int32]))
   838  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAssignExists[bigType, int32]))
   839  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapAssignExists[bigType, bigType]))
   840  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapAssignExists[int32, bigType]))
   841  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapAssignExists[*int32, int32]))
   842  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapAssignExists[int32, *int32]))
   843  }
   844  
   845  // Fill a map of size n with no hint. Time is per-key. A new map is created
   846  // every n assignments.
   847  //
   848  // TODO(prattmic): Results don't make much sense if b.N < n.
   849  // TODO(prattmic): Measure distribution of assign time to reveal the grow
   850  // latency.
   851  func benchmarkMapAssignFillNoHint[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   852  	if n == 0 {
   853  		b.Skip("can't create empty map via assignment")
   854  	}
   855  	checkAllocSize[K, E](b, n)
   856  	k := genValues[K](0, n)
   857  	e := genValues[E](0, n)
   858  	b.ResetTimer()
   859  
   860  	var m map[K]E
   861  	for i := 0; i < b.N; i++ {
   862  		if i%n == 0 {
   863  			m = make(map[K]E)
   864  		}
   865  		m[k[i%n]] = e[i%n]
   866  	}
   867  }
   868  
   869  func BenchmarkMapAssignFillNoHint(b *testing.B) {
   870  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAssignFillNoHint[int32, int32]))
   871  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAssignFillNoHint[int64, int64]))
   872  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAssignFillNoHint[string, string]))
   873  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAssignFillNoHint[smallType, int32]))
   874  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAssignFillNoHint[mediumType, int32]))
   875  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAssignFillNoHint[bigType, int32]))
   876  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapAssignFillNoHint[bigType, bigType]))
   877  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapAssignFillNoHint[int32, bigType]))
   878  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapAssignFillNoHint[*int32, int32]))
   879  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapAssignFillNoHint[int32, *int32]))
   880  }
   881  
   882  // Identical to benchmarkMapAssignFillNoHint, but additionally measures the
   883  // latency of each mapassign to report tail latency due to map grow.
   884  func benchmarkMapAssignGrowLatency[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   885  	if n == 0 {
   886  		b.Skip("can't create empty map via assignment")
   887  	}
   888  	checkAllocSize[K, E](b, n)
   889  	k := genValues[K](0, n)
   890  	e := genValues[E](0, n)
   891  
   892  	// Store the run time of each mapassign. Keeping the full data rather
   893  	// than a histogram provides higher precision. b.N tends to be <10M, so
   894  	// the memory requirement isn't too bad.
   895  	sample := make([]int64, b.N)
   896  
   897  	b.ResetTimer()
   898  
   899  	var m map[K]E
   900  	for i := 0; i < b.N; i++ {
   901  		if i%n == 0 {
   902  			m = make(map[K]E)
   903  		}
   904  		start := runtime.Nanotime()
   905  		m[k[i%n]] = e[i%n]
   906  		end := runtime.Nanotime()
   907  		sample[i] = end - start
   908  	}
   909  
   910  	b.StopTimer()
   911  
   912  	slices.Sort(sample)
   913  	// TODO(prattmic): Grow is so rare that even p99.99 often doesn't
   914  	// display a grow case. Switch to a more direct measure of grow cases
   915  	// only?
   916  	b.ReportMetric(float64(sample[int(float64(len(sample))*0.5)]), "p50-ns/op")
   917  	b.ReportMetric(float64(sample[int(float64(len(sample))*0.99)]), "p99-ns/op")
   918  	b.ReportMetric(float64(sample[int(float64(len(sample))*0.999)]), "p99.9-ns/op")
   919  	b.ReportMetric(float64(sample[int(float64(len(sample))*0.9999)]), "p99.99-ns/op")
   920  	b.ReportMetric(float64(sample[len(sample)-1]), "p100-ns/op")
   921  }
   922  
   923  func BenchmarkMapAssignGrowLatency(b *testing.B) {
   924  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAssignGrowLatency[int32, int32]))
   925  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAssignGrowLatency[int64, int64]))
   926  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAssignGrowLatency[string, string]))
   927  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAssignGrowLatency[smallType, int32]))
   928  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAssignGrowLatency[mediumType, int32]))
   929  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAssignGrowLatency[bigType, int32]))
   930  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapAssignGrowLatency[bigType, bigType]))
   931  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapAssignGrowLatency[int32, bigType]))
   932  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapAssignGrowLatency[*int32, int32]))
   933  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapAssignGrowLatency[int32, *int32]))
   934  }
   935  
   936  // Fill a map of size n with size hint. Time is per-key. A new map is created
   937  // every n assignments.
   938  //
   939  // TODO(prattmic): Results don't make much sense if b.N < n.
   940  func benchmarkMapAssignFillHint[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   941  	if n == 0 {
   942  		b.Skip("can't create empty map via assignment")
   943  	}
   944  	checkAllocSize[K, E](b, n)
   945  	k := genValues[K](0, n)
   946  	e := genValues[E](0, n)
   947  	b.ResetTimer()
   948  
   949  	var m map[K]E
   950  	for i := 0; i < b.N; i++ {
   951  		if i%n == 0 {
   952  			m = make(map[K]E, n)
   953  		}
   954  		m[k[i%n]] = e[i%n]
   955  	}
   956  }
   957  
   958  func BenchmarkMapAssignFillHint(b *testing.B) {
   959  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAssignFillHint[int32, int32]))
   960  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAssignFillHint[int64, int64]))
   961  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAssignFillHint[string, string]))
   962  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAssignFillHint[smallType, int32]))
   963  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAssignFillHint[mediumType, int32]))
   964  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAssignFillHint[bigType, int32]))
   965  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapAssignFillHint[bigType, bigType]))
   966  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapAssignFillHint[int32, bigType]))
   967  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapAssignFillHint[*int32, int32]))
   968  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapAssignFillHint[int32, *int32]))
   969  }
   970  
   971  // Fill a map of size n, reusing the same map. Time is per-key. The map is
   972  // cleared every n assignments.
   973  //
   974  // TODO(prattmic): Results don't make much sense if b.N < n.
   975  func benchmarkMapAssignFillClear[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
   976  	if n == 0 {
   977  		b.Skip("can't create empty map via assignment")
   978  	}
   979  	checkAllocSize[K, E](b, n)
   980  	k := genValues[K](0, n)
   981  	e := genValues[E](0, n)
   982  	m := fillMap(k, e)
   983  	b.ResetTimer()
   984  
   985  	for i := 0; i < b.N; i++ {
   986  		if i%n == 0 {
   987  			clear(m)
   988  		}
   989  		m[k[i%n]] = e[i%n]
   990  	}
   991  }
   992  
   993  func BenchmarkMapAssignFillClear(b *testing.B) {
   994  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAssignFillClear[int32, int32]))
   995  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAssignFillClear[int64, int64]))
   996  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAssignFillClear[string, string]))
   997  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAssignFillClear[smallType, int32]))
   998  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAssignFillClear[mediumType, int32]))
   999  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAssignFillClear[bigType, int32]))
  1000  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapAssignFillClear[bigType, bigType]))
  1001  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapAssignFillClear[int32, bigType]))
  1002  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapAssignFillClear[*int32, int32]))
  1003  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapAssignFillClear[int32, *int32]))
  1004  }
  1005  
  1006  // Modify values using +=.
  1007  func benchmarkMapAssignAddition[K mapBenchmarkKeyType, E int32 | int64 | string](b *testing.B, n int) {
  1008  	if n == 0 {
  1009  		b.Skip("can't modify empty map via assignment")
  1010  	}
  1011  	checkAllocSize[K, E](b, n)
  1012  	k := genValues[K](0, n)
  1013  	e := genValues[E](0, n)
  1014  	m := fillMap(k, e)
  1015  	b.ResetTimer()
  1016  
  1017  	for i := 0; i < b.N; i++ {
  1018  		m[k[i%n]] += e[i%n]
  1019  	}
  1020  }
  1021  
  1022  func BenchmarkMapAssignAddition(b *testing.B) {
  1023  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapAssignAddition[int32, int32]))
  1024  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapAssignAddition[int64, int64]))
  1025  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapAssignAddition[string, string]))
  1026  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapAssignAddition[smallType, int32]))
  1027  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapAssignAddition[mediumType, int32]))
  1028  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapAssignAddition[bigType, int32]))
  1029  }
  1030  
  1031  // Modify values append.
  1032  func benchmarkMapAssignAppend[K mapBenchmarkKeyType](b *testing.B, n int) {
  1033  	if n == 0 {
  1034  		b.Skip("can't modify empty map via append")
  1035  	}
  1036  	checkAllocSize[K, []int32](b, n)
  1037  	k := genValues[K](0, n)
  1038  	e := genValues[[]int32](0, n)
  1039  	m := fillMap(k, e)
  1040  	b.ResetTimer()
  1041  
  1042  	for i := 0; i < b.N; i++ {
  1043  		m[k[i%n]] = append(m[k[i%n]], e[i%n][0])
  1044  	}
  1045  }
  1046  
  1047  func BenchmarkMapAssignAppend(b *testing.B) {
  1048  	b.Run("Key=int32/Elem=[]int32", benchSizes(benchmarkMapAssignAppend[int32]))
  1049  	b.Run("Key=int64/Elem=[]int32", benchSizes(benchmarkMapAssignAppend[int64]))
  1050  	b.Run("Key=string/Elem=[]int32", benchSizes(benchmarkMapAssignAppend[string]))
  1051  }
  1052  
  1053  func benchmarkMapDelete[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
  1054  	if n == 0 {
  1055  		b.Skip("can't delete from empty map")
  1056  	}
  1057  	checkAllocSize[K, E](b, n)
  1058  	k := genValues[K](0, n)
  1059  	e := genValues[E](0, n)
  1060  	m := fillMap(k, e)
  1061  	b.ResetTimer()
  1062  
  1063  	for i := 0; i < b.N; i++ {
  1064  		if len(m) == 0 {
  1065  			// We'd like to StopTimer while refilling the map, but
  1066  			// it is way too expensive and thus makes the benchmark
  1067  			// take a long time. See https://go.dev/issue/20875.
  1068  			for j := range k {
  1069  				m[k[j]] = e[j]
  1070  			}
  1071  		}
  1072  		delete(m, k[i%n])
  1073  	}
  1074  }
  1075  
  1076  func BenchmarkMapDelete(b *testing.B) {
  1077  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapDelete[int32, int32]))
  1078  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapDelete[int64, int64]))
  1079  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapDelete[string, string]))
  1080  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapDelete[smallType, int32]))
  1081  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapDelete[mediumType, int32]))
  1082  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapDelete[bigType, int32]))
  1083  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapDelete[bigType, bigType]))
  1084  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapDelete[int32, bigType]))
  1085  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapDelete[*int32, int32]))
  1086  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapDelete[int32, *int32]))
  1087  }
  1088  
  1089  // Use iterator to pop an element. We want this to be fast, see
  1090  // https://go.dev/issue/8412.
  1091  func benchmarkMapPop[K mapBenchmarkKeyType, E mapBenchmarkElemType](b *testing.B, n int) {
  1092  	if n == 0 {
  1093  		b.Skip("can't delete from empty map")
  1094  	}
  1095  	checkAllocSize[K, E](b, n)
  1096  	k := genValues[K](0, n)
  1097  	e := genValues[E](0, n)
  1098  	m := fillMap(k, e)
  1099  	b.ResetTimer()
  1100  
  1101  	for i := 0; i < b.N; i++ {
  1102  		if len(m) == 0 {
  1103  			// We'd like to StopTimer while refilling the map, but
  1104  			// it is way too expensive and thus makes the benchmark
  1105  			// take a long time. See https://go.dev/issue/20875.
  1106  			for j := range k {
  1107  				m[k[j]] = e[j]
  1108  			}
  1109  		}
  1110  		for key := range m {
  1111  			delete(m, key)
  1112  			break
  1113  		}
  1114  	}
  1115  }
  1116  
  1117  func BenchmarkMapPop(b *testing.B) {
  1118  	b.Run("Key=int32/Elem=int32", benchSizes(benchmarkMapPop[int32, int32]))
  1119  	b.Run("Key=int64/Elem=int64", benchSizes(benchmarkMapPop[int64, int64]))
  1120  	b.Run("Key=string/Elem=string", benchSizes(benchmarkMapPop[string, string]))
  1121  	b.Run("Key=smallType/Elem=int32", benchSizes(benchmarkMapPop[smallType, int32]))
  1122  	b.Run("Key=mediumType/Elem=int32", benchSizes(benchmarkMapPop[mediumType, int32]))
  1123  	b.Run("Key=bigType/Elem=int32", benchSizes(benchmarkMapPop[bigType, int32]))
  1124  	b.Run("Key=bigType/Elem=bigType", benchSizes(benchmarkMapPop[bigType, bigType]))
  1125  	b.Run("Key=int32/Elem=bigType", benchSizes(benchmarkMapPop[int32, bigType]))
  1126  	b.Run("Key=*int32/Elem=int32", benchSizes(benchmarkMapPop[*int32, int32]))
  1127  	b.Run("Key=int32/Elem=*int32", benchSizes(benchmarkMapPop[int32, *int32]))
  1128  }
  1129  
  1130  func BenchmarkMapDeleteLargeKey(b *testing.B) {
  1131  	m := map[string]int{}
  1132  	for i := range 9 {
  1133  		m[fmt.Sprintf("%d", i)] = i
  1134  	}
  1135  	key := strings.Repeat("*", 10000)
  1136  	for range b.N {
  1137  		delete(m, key)
  1138  	}
  1139  }
  1140  

View as plain text