github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/perf/lru/cache_test.go (about) 1 package lru 2 3 import ( 4 "math/rand" 5 "runtime" 6 "testing" 7 "time" 8 ) 9 10 func createFilledCache(ttl time.Duration) *Cache[int64, int64] { 11 c := NewCache[int64, int64](1000) 12 for i := 0; i < 1000; i++ { 13 key := int64(rand.Intn(5000)) 14 c.Set(key, key, ttl) 15 } 16 return c 17 } 18 19 func createRandInts(size int) []int64 { 20 s := make([]int64, size) 21 for i := 0; i < size; i++ { 22 s[i] = rand.Int63n(5000) 23 } 24 return s 25 } 26 27 func TestBasicEviction(t *testing.T) { 28 t.Parallel() 29 c := NewCache[string, string](3) 30 if _, ok, _ := c.Get("a"); ok { 31 t.Error("a") 32 } 33 34 c.Set("b", "vb", 2*time.Second) 35 c.Set("a", "va", time.Second) 36 c.Set("c", "vc", 3*time.Second) 37 38 if v, _, _ := c.Get("a"); v != "va" { 39 t.Error("va") 40 } 41 if v, _, _ := c.Get("b"); v != "vb" { 42 t.Error("vb") 43 } 44 if v, _, _ := c.Get("c"); v != "vc" { 45 t.Error("vc") 46 } 47 48 c.Set("d", "vd", time.Second) 49 if _, ok, _ := c.Get("a"); ok { 50 t.Error("expecting element A to be evicted") 51 } 52 c.Set("e", "ve", time.Second) 53 if _, ok, _ := c.Get("b"); ok { 54 t.Error("expecting element B to be evicted") 55 } 56 c.Set("f", "vf", time.Second) 57 if _, ok, _ := c.Get("c"); ok { 58 t.Error("expecting element C to be evicted") 59 } 60 61 if v, _, _ := c.Get("d"); v != "vd" { 62 t.Error("expecting element D to not be evicted") 63 } 64 65 // e, f, d, [g] 66 c.Set("g", "vg", time.Second) 67 if _, ok, _ := c.Get("E"); ok { 68 t.Error("expecting element E to be evicted") 69 } 70 71 if l := c.Len(); l != 3 { 72 t.Errorf("invalid length, want= 3, got= %v", l) 73 } 74 75 c.Delete("missing") 76 c.Delete("g") 77 if l := c.Len(); l != 2 { 78 t.Errorf("invalid length, want= 2, got= %v", l) 79 } 80 81 // f, d, [h, i] 82 c.MSet(map[string]string{"h": "vh", "i": "vi"}, time.Second) 83 if _, ok, _ := c.Get("e"); ok { 84 t.Error("expecting element E to be evicted") 85 } 86 if _, ok, _ := c.Get("f"); ok { 87 t.Error("expecting element F to be evicted") 88 } 89 if v, _, _ := c.Get("d"); v != "vd" { 90 t.Error("expecting element D to not be evicted") 91 } 92 93 // h/i, i/h, d, [h, i] 94 m := c.MGet("h", "i") 95 if m["h"] != "vh" { 96 t.Error("expecting MSet and MGet to work") 97 } 98 if m["i"] != "vi" { 99 t.Error("expecting MSet and MGet to work") 100 } 101 102 if v, _, _ := c.GetQuiet("d"); v != "vd" { 103 t.Error("expecting GetQuiet to work") 104 } 105 106 if v, _ := c.GetNotStale("d"); v != "vd" { 107 t.Error("expecting GetNotStale to work") 108 } 109 } 110 111 func TestConcurrentGet(t *testing.T) { 112 t.Parallel() 113 c := createFilledCache(time.Second) 114 s := createRandInts(50000) 115 116 runConcurrentGetTest(t, c, s) 117 } 118 119 func runConcurrentGetTest(t *testing.T, c Interface[int64, int64], s []int64) { 120 done := make(chan bool) 121 worker := func() { 122 for i := 0; i < 5000; i++ { 123 key := s[i] 124 v, exists, _ := c.Get(key) 125 if exists && v != key { 126 t.Errorf("value not match: want= %v, got= %v", key, v) 127 } 128 } 129 done <- true 130 } 131 workers := 4 132 for i := 0; i < workers; i++ { 133 go worker() 134 } 135 for i := 0; i < workers; i++ { 136 _ = <-done 137 } 138 } 139 140 func TestConcurrentSet(t *testing.T) { 141 t.Parallel() 142 c := createFilledCache(time.Second) 143 s := createRandInts(5000) 144 145 runConcurrentSetTest(t, c, s) 146 } 147 148 func runConcurrentSetTest(t *testing.T, c Interface[int64, int64], s []int64) { 149 done := make(chan bool) 150 worker := func() { 151 ttl := 4 * time.Second 152 for i := 0; i < 5000; i++ { 153 key := s[i] 154 c.Set(key, key, ttl) 155 } 156 done <- true 157 } 158 workers := 4 159 for i := 0; i < workers; i++ { 160 go worker() 161 } 162 for i := 0; i < workers; i++ { 163 _ = <-done 164 } 165 } 166 167 func TestConcurrentGetSet(t *testing.T) { 168 t.Parallel() 169 c := createFilledCache(time.Second) 170 s := createRandInts(5000) 171 172 runConcurrentGetSetTest(t, c, s) 173 } 174 175 func runConcurrentGetSetTest(t *testing.T, c Interface[int64, int64], s []int64) { 176 done := make(chan bool) 177 getWorker := func() { 178 for i := 0; i < 5000; i++ { 179 key := s[i] 180 v, exists, _ := c.Get(key) 181 if exists && v != key { 182 t.Errorf("value not match: want= %v, got= %v", key, v) 183 } 184 } 185 done <- true 186 } 187 setWorker := func() { 188 ttl := 4 * time.Second 189 for i := 0; i < 5000; i++ { 190 key := s[i] 191 c.Set(key, key, ttl) 192 } 193 done <- true 194 } 195 workers := 4 196 for i := 0; i < workers; i++ { 197 go getWorker() 198 go setWorker() 199 } 200 for i := 0; i < workers*2; i++ { 201 _ = <-done 202 } 203 } 204 205 func BenchmarkConcurrentGetLRUCache(bb *testing.B) { 206 c := createFilledCache(time.Second) 207 s := createRandInts(5000) 208 209 bb.ReportAllocs() 210 bb.ResetTimer() 211 cpu := runtime.GOMAXPROCS(0) 212 ch := make(chan bool) 213 worker := func() { 214 for i := 0; i < bb.N/cpu; i++ { 215 c.Get(s[i%5000]) 216 } 217 ch <- true 218 } 219 for i := 0; i < cpu; i++ { 220 go worker() 221 } 222 for i := 0; i < cpu; i++ { 223 _ = <-ch 224 } 225 } 226 227 func BenchmarkConcurrentSetLRUCache(bb *testing.B) { 228 c := createFilledCache(time.Second) 229 s := createRandInts(5000) 230 231 bb.ReportAllocs() 232 bb.ResetTimer() 233 cpu := runtime.GOMAXPROCS(0) 234 ch := make(chan bool) 235 worker := func() { 236 ttl := 4 * time.Second 237 for i := 0; i < bb.N/cpu; i++ { 238 key := s[i%5000] 239 c.Set(key, key, ttl) 240 } 241 ch <- true 242 } 243 for i := 0; i < cpu; i++ { 244 go worker() 245 } 246 for i := 0; i < cpu; i++ { 247 _ = <-ch 248 } 249 } 250 251 // No expiry 252 func BenchmarkConcurrentSetNXLRUCache(bb *testing.B) { 253 c := createFilledCache(time.Second) 254 s := createRandInts(5000) 255 256 bb.ReportAllocs() 257 bb.ResetTimer() 258 cpu := runtime.GOMAXPROCS(0) 259 ch := make(chan bool) 260 worker := func() { 261 for i := 0; i < bb.N/cpu; i++ { 262 key := s[i%5000] 263 c.Set(key, key, 0) 264 } 265 ch <- true 266 } 267 for i := 0; i < cpu; i++ { 268 go worker() 269 } 270 for i := 0; i < cpu; i++ { 271 _ = <-ch 272 } 273 }