github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/cache/lrucache/robin_hood_test.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package lrucache 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "runtime" 21 "testing" 22 "time" 23 24 "golang.org/x/exp/rand" 25 ) 26 27 func TestRobinHoodMap(t *testing.T) { 28 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 29 rhMap := newRobinHoodMap(0) 30 defer rhMap.free() 31 32 goMap := make(map[key]*entry) 33 34 randomKey := func() key { 35 n := rng.Intn(len(goMap)) 36 for k := range goMap { 37 if n == 0 { 38 return k 39 } 40 n-- 41 } 42 return key{} 43 } 44 45 ops := 10000 + rng.Intn(10000) 46 for i := 0; i < ops; i++ { 47 var which float64 48 if len(goMap) > 0 { 49 which = rng.Float64() 50 } 51 52 switch { 53 case which < 0.4: 54 var k key 55 k.id = rng.Uint64() 56 k.offset = rng.Uint64() 57 e := &entry{} 58 goMap[k] = e 59 rhMap.Put(k, e) 60 if len(goMap) != rhMap.Count() { 61 t.Fatalf("map sizes differ: %d != %d", len(goMap), rhMap.Count()) 62 } 63 64 case which < 0.1: 65 k := randomKey() 66 e := &entry{} 67 goMap[k] = e 68 rhMap.Put(k, e) 69 if len(goMap) != rhMap.Count() { 70 t.Fatalf("map sizes differ: %d != %d", len(goMap), rhMap.Count()) 71 } 72 73 case which < 0.75: 74 k := randomKey() 75 delete(goMap, k) 76 rhMap.Delete(k) 77 if len(goMap) != rhMap.Count() { 78 t.Fatalf("map sizes differ: %d != %d", len(goMap), rhMap.Count()) 79 } 80 81 default: 82 k := randomKey() 83 v := goMap[k] 84 u := rhMap.Get(k) 85 if v != u { 86 t.Fatalf("%s: expected %p, but found %p", k, v, u) 87 } 88 } 89 } 90 91 t.Logf("map size: %d", len(goMap)) 92 } 93 94 const benchSize = 1 << 20 95 96 func BenchmarkGoMapInsert(b *testing.B) { 97 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 98 keys := make([]key, benchSize) 99 for i := range keys { 100 keys[i].offset = uint64(rng.Intn(1 << 20)) 101 } 102 b.ResetTimer() 103 104 var m map[key]*entry 105 for i, j := 0, 0; i < b.N; i, j = i+1, j+1 { 106 if m == nil || j == len(keys) { 107 b.StopTimer() 108 m = make(map[key]*entry, len(keys)) 109 j = 0 110 b.StartTimer() 111 } 112 m[keys[j]] = nil 113 } 114 } 115 116 func BenchmarkRobinHoodInsert(b *testing.B) { 117 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 118 keys := make([]key, benchSize) 119 for i := range keys { 120 keys[i].offset = uint64(rng.Intn(1 << 20)) 121 } 122 e := &entry{} 123 b.ResetTimer() 124 125 var m *robinHoodMap 126 for i, j := 0, 0; i < b.N; i, j = i+1, j+1 { 127 if m == nil || j == len(keys) { 128 b.StopTimer() 129 m = newRobinHoodMap(len(keys)) 130 j = 0 131 b.StartTimer() 132 } 133 m.Put(keys[j], e) 134 } 135 136 runtime.KeepAlive(e) 137 } 138 139 func BenchmarkGoMapLookupHit(b *testing.B) { 140 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 141 keys := make([]key, benchSize) 142 m := make(map[key]*entry, len(keys)) 143 e := &entry{} 144 for i := range keys { 145 keys[i].offset = uint64(rng.Intn(1 << 20)) 146 m[keys[i]] = e 147 } 148 b.ResetTimer() 149 150 var p *entry 151 for i, j := 0, 0; i < b.N; i, j = i+1, j+1 { 152 if j == len(keys) { 153 j = 0 154 } 155 p = m[keys[j]] 156 } 157 158 if testing.Verbose() { 159 fmt.Fprintln(ioutil.Discard, p) 160 } 161 } 162 163 func BenchmarkRobinHoodLookupHit(b *testing.B) { 164 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 165 keys := make([]key, benchSize) 166 m := newRobinHoodMap(len(keys)) 167 e := &entry{} 168 for i := range keys { 169 keys[i].offset = uint64(rng.Intn(1 << 20)) 170 m.Put(keys[i], e) 171 } 172 b.ResetTimer() 173 174 var p *entry 175 for i, j := 0, 0; i < b.N; i, j = i+1, j+1 { 176 if j == len(keys) { 177 j = 0 178 } 179 p = m.Get(keys[j]) 180 } 181 182 if testing.Verbose() { 183 fmt.Fprintln(ioutil.Discard, p) 184 } 185 runtime.KeepAlive(e) 186 } 187 188 func BenchmarkGoMapLookupMiss(b *testing.B) { 189 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 190 keys := make([]key, benchSize) 191 m := make(map[key]*entry, len(keys)) 192 e := &entry{} 193 for i := range keys { 194 keys[i].id = 1 195 keys[i].offset = uint64(rng.Intn(1 << 20)) 196 m[keys[i]] = e 197 keys[i].id = 2 198 } 199 b.ResetTimer() 200 201 var p *entry 202 for i, j := 0, 0; i < b.N; i, j = i+1, j+1 { 203 if j == len(keys) { 204 j = 0 205 } 206 p = m[keys[j]] 207 } 208 209 if testing.Verbose() { 210 fmt.Fprintln(ioutil.Discard, p) 211 } 212 } 213 214 func BenchmarkRobinHoodLookupMiss(b *testing.B) { 215 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 216 keys := make([]key, benchSize) 217 m := newRobinHoodMap(len(keys)) 218 e := &entry{} 219 for i := range keys { 220 keys[i].id = 1 221 keys[i].offset = uint64(rng.Intn(1 << 20)) 222 m.Put(keys[i], e) 223 keys[i].id = 2 224 } 225 b.ResetTimer() 226 227 var p *entry 228 for i, j := 0, 0; i < b.N; i, j = i+1, j+1 { 229 if j == len(keys) { 230 j = 0 231 } 232 p = m.Get(keys[j]) 233 } 234 235 if testing.Verbose() { 236 fmt.Fprintln(ioutil.Discard, p) 237 } 238 runtime.KeepAlive(e) 239 }