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 }