github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/fast_int_map_test.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package util
    12  
    13  import (
    14  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    18  )
    19  
    20  func TestFastIntMap(t *testing.T) {
    21  	cases := []struct {
    22  		keyRange, valRange int
    23  	}{
    24  		{keyRange: 10, valRange: 10},
    25  		{keyRange: numVals, valRange: maxValue + 1},
    26  		{keyRange: numVals + 1, valRange: maxValue + 1},
    27  		{keyRange: numVals, valRange: maxValue + 2},
    28  		{keyRange: 100, valRange: 100},
    29  	}
    30  	for _, tc := range cases {
    31  		t.Run(fmt.Sprintf("%d-%d", tc.keyRange, tc.valRange), func(t *testing.T) {
    32  			t.Parallel() // SAFE FOR TESTING (this comment is for the linter)
    33  			rng, _ := randutil.NewPseudoRand()
    34  			var fm FastIntMap
    35  			m := make(map[int]int)
    36  			for i := 0; i < 1000; i++ {
    37  				// Check the entire key range.
    38  				for k := 0; k < tc.keyRange; k++ {
    39  					v, ok := fm.Get(k)
    40  					expV, expOk := m[k]
    41  					if ok != expOk || (ok && v != expV) {
    42  						t.Fatalf(
    43  							"incorrect result for key %d: (%d, %t), expected (%d, %t)",
    44  							k, v, ok, expV, expOk,
    45  						)
    46  					}
    47  				}
    48  
    49  				if e := fm.Empty(); e != (len(m) == 0) {
    50  					t.Fatalf("incorrect Empty: %t expected %t (%+v %v)", e, len(m) == 0, fm, m)
    51  				}
    52  
    53  				if l := fm.Len(); l != len(m) {
    54  					t.Fatalf("incorrect Len: %d expected %d (%+v %v)", l, len(m), fm, m)
    55  				}
    56  
    57  				// Get maximum key and value and check MaxKey and MaxValue.
    58  				maxKey, maxVal, maxOk := 0, 0, (len(m) > 0)
    59  				for k, v := range m {
    60  					if maxKey < k {
    61  						maxKey = k
    62  					}
    63  					if maxVal < v {
    64  						maxVal = v
    65  					}
    66  				}
    67  				if m, ok := fm.MaxKey(); ok != maxOk || m != maxKey {
    68  					t.Fatalf("incorrect MaxKey (%d, %t), expected (%d, %t)", m, ok, maxKey, maxOk)
    69  				}
    70  				if m, ok := fm.MaxValue(); ok != maxOk || m != maxVal {
    71  					t.Fatalf("incorrect MaxValue (%d, %t), expected (%d, %t)", m, ok, maxVal, maxOk)
    72  				}
    73  
    74  				// Check ForEach
    75  				num := 0
    76  				fm.ForEach(func(key, val int) {
    77  					num++
    78  					if m[key] != val {
    79  						t.Fatalf("incorrect ForEach %d,%d", key, val)
    80  					}
    81  				})
    82  				if num != len(m) {
    83  					t.Fatalf("ForEach reported %d keys, expected %d", num, len(m))
    84  				}
    85  				k := rng.Intn(tc.keyRange)
    86  				if rng.Intn(2) == 0 {
    87  					v := rng.Intn(tc.valRange)
    88  					fm.Set(k, v)
    89  					m[k] = v
    90  				} else {
    91  					fm.Unset(k)
    92  					delete(m, k)
    93  				}
    94  				if rng.Intn(10) == 0 {
    95  					// Verify Copy. The next iteration will verify that the copy contains
    96  					// the right data.
    97  					old := fm
    98  					fm = fm.Copy()
    99  					old.Set(1, 1)
   100  				}
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  func BenchmarkFastIntMap(b *testing.B) {
   107  	cases := []struct {
   108  		keyRange, valRange, ops int
   109  	}{
   110  		{keyRange: 4, valRange: 4, ops: 4},
   111  		{keyRange: 10, valRange: 10, ops: 4},
   112  		{keyRange: numVals, valRange: maxValue + 1, ops: 10},
   113  		{keyRange: 100, valRange: 100, ops: 50},
   114  		{keyRange: 1000, valRange: 1000, ops: 500},
   115  	}
   116  	for _, tc := range cases {
   117  		b.Run(fmt.Sprintf("%dx%d-%d", tc.keyRange, tc.valRange, tc.ops), func(b *testing.B) {
   118  			rng, _ := randutil.NewPseudoRand()
   119  			inserts := make([][2]int, tc.ops)
   120  			for i := range inserts {
   121  				inserts[i] = [2]int{rng.Intn(tc.keyRange), rng.Intn(tc.valRange)}
   122  			}
   123  			probes := make([]int, tc.ops)
   124  			for i := range probes {
   125  				probes[i] = rng.Intn(tc.keyRange)
   126  			}
   127  
   128  			b.Run("fastintmap", func(b *testing.B) {
   129  				for i := 0; i < b.N; i++ {
   130  					var fm FastIntMap
   131  					for _, x := range inserts {
   132  						fm.Set(x[0], x[1])
   133  					}
   134  					hash := 0
   135  					for _, x := range probes {
   136  						val, ok := fm.Get(x)
   137  						if ok {
   138  							hash ^= val
   139  						}
   140  					}
   141  				}
   142  			})
   143  			b.Run("map", func(b *testing.B) {
   144  				for i := 0; i < b.N; i++ {
   145  					m := make(map[int]int)
   146  					for _, x := range inserts {
   147  						m[x[0]] = x[1]
   148  					}
   149  					hash := 0
   150  					for _, x := range probes {
   151  						val, ok := m[x]
   152  						if ok {
   153  							hash ^= val
   154  						}
   155  					}
   156  				}
   157  			})
   158  			b.Run("map-sized", func(b *testing.B) {
   159  				for i := 0; i < b.N; i++ {
   160  					m := make(map[int]int, tc.keyRange)
   161  					for _, x := range inserts {
   162  						m[x[0]] = x[1]
   163  					}
   164  					hash := 0
   165  					for _, x := range probes {
   166  						val, ok := m[x]
   167  						if ok {
   168  							hash ^= val
   169  						}
   170  					}
   171  				}
   172  			})
   173  			b.Run("slice", func(b *testing.B) {
   174  				for i := 0; i < b.N; i++ {
   175  					var m []int
   176  					for _, x := range inserts {
   177  						for len(m) <= x[0] {
   178  							m = append(m, -1)
   179  						}
   180  						m[x[0]] = x[1]
   181  					}
   182  					hash := 0
   183  					for _, x := range probes {
   184  						if x < len(m) {
   185  							val := m[x]
   186  							if val != -1 {
   187  								hash ^= val
   188  							}
   189  						}
   190  					}
   191  				}
   192  			})
   193  			b.Run("slice-sized", func(b *testing.B) {
   194  				for i := 0; i < b.N; i++ {
   195  					m := make([]int, tc.keyRange)
   196  					for i := range m {
   197  						m[i] = -1
   198  					}
   199  					for _, x := range inserts {
   200  						m[x[0]] = x[1]
   201  					}
   202  					hash := 0
   203  					for _, x := range probes {
   204  						val := m[x]
   205  						if val != -1 {
   206  							hash ^= val
   207  						}
   208  					}
   209  				}
   210  			})
   211  
   212  		})
   213  	}
   214  
   215  }