github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/cachekit/core_test.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package cachekit 7 8 import ( 9 "math" 10 "strconv" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestBasicLimits(t *testing.T) { 17 c := NewUintCache(newStrategy(5, 15, false, true)) 18 19 for i := 1; i <= 20; i++ { 20 s := strconv.Itoa(i) 21 c.Put(uint64(i), "V" + s) 22 } 23 24 require.Equal(t, 15, c.Occupied()) 25 require.Equal(t, 15, c.core.Occupied()) 26 require.Equal(t, 20, c.Allocated()) 27 28 for i := 1; i <= 5; i++ { 29 require.False(t, c.Contains(uint64(i)), i) 30 } 31 32 for i := 6; i <= 20; i++ { 33 s := strconv.Itoa(i) 34 require.True(t, c.Contains(uint64(i)), i) 35 v, ok := c.Peek(uint64(i)) 36 require.True(t, ok, i) 37 require.Equal(t, "V" + s, v) 38 } 39 } 40 41 func TestDelete(t *testing.T) { 42 c := NewUintCache(newStrategy(5, 15, false, true)) 43 44 for i := 1; i <= 15; i++ { 45 s := strconv.Itoa(i) 46 c.Put(uint64(i), "V" + s) 47 } 48 49 c.Delete(8) 50 require.False(t, c.Contains(8)) 51 52 for i := 16; i <= 20; i++ { 53 s := strconv.Itoa(i) 54 c.Put(uint64(i), "V" + s) 55 } 56 57 require.Equal(t, 14, c.Occupied()) 58 require.Equal(t, 15, c.core.Occupied()) 59 require.Equal(t, 20, c.Allocated()) 60 61 // this loop forces creation of generations and cleanup of them 62 // so the deleted item can be collected 63 for i := 100; i > 0; i-- { 64 c.Get(19) 65 c.Get(20) 66 } 67 68 require.Equal(t, 14, c.core.Occupied()) 69 70 for i := 1; i <= 5; i++ { 71 require.False(t, c.Contains(uint64(i)), i) 72 } 73 74 for i := 6; i <= 7; i++ { 75 s := strconv.Itoa(i) 76 require.True(t, c.Contains(uint64(i)), i) 77 v, ok := c.Peek(uint64(i)) 78 require.True(t, ok, i) 79 require.Equal(t, "V" + s, v) 80 } 81 82 require.False(t, c.Contains(8)) 83 84 for i := 9; i <= 20; i++ { 85 s := strconv.Itoa(i) 86 require.True(t, c.Contains(uint64(i)), i) 87 v, ok := c.Peek(uint64(i)) 88 require.True(t, ok, i) 89 require.Equal(t, "V" + s, v) 90 } 91 92 // it uses the empty slot released by deletion 93 c.Put(21, "V21") 94 95 require.Equal(t, 15, c.Occupied()) 96 require.Equal(t, 15, c.core.Occupied()) 97 require.Equal(t, 20, c.Allocated()) 98 } 99 100 func TestCounter(t *testing.T) { 101 c := NewUintCache(newStrategy(2, 2, false, true)) 102 c.Put(1, "A") 103 c.Put(2, "B") 104 for i := 100; i > 0; i-- { 105 c.Get(1) 106 c.Get(2) 107 } 108 109 require.Less(t, int32(2), *c.core.alloc.get(1)) 110 111 c.core.trimGenerations(math.MaxUint32) 112 113 require.EqualValues(t, 2, *c.core.alloc.get(1)) 114 } 115 116 func TestFencedCounter(t *testing.T) { 117 c := NewUintCache(newStrategy(4, 2, true, true)) 118 c.Put(1, "A") 119 c.Put(2, "B") 120 for i := 100; i > 0; i-- { 121 c.Get(1) 122 c.Get(2) 123 } 124 125 require.EqualValues(t, 2, *c.core.alloc.get(1)) 126 127 c.core.trimGenerations(math.MaxUint32) 128 129 require.EqualValues(t, 2, *c.core.alloc.get(1)) 130 } 131 132 func TestTouch(t *testing.T) { 133 c := NewUintCache(newStrategy(5, 15, true, true)) 134 135 for i := 1; i <= 20; i++ { 136 s := strconv.Itoa(i) 137 c.Put(uint64(i), "V" + s) 138 139 _, _ = c.Peek(4) 140 v, ok := c.Get(5) 141 142 if i < 5 { 143 require.False(t, ok, i) 144 } else { 145 require.True(t, ok, i) 146 require.Equal(t, "V5", v) 147 } 148 } 149 150 require.Equal(t, 15, c.Occupied()) 151 require.Equal(t, 15, c.core.Occupied()) 152 require.Equal(t, 20, c.Allocated()) 153 154 for i := 1; i <= 4; i++ { 155 require.False(t, c.Contains(uint64(i)), i) 156 } 157 158 require.True(t, c.Contains(5)) 159 160 for i := 7; i <= 20; i++ { 161 s := strconv.Itoa(i) 162 require.True(t, c.Contains(uint64(i)), i) 163 v, ok := c.Peek(uint64(i)) 164 require.True(t, ok, i) 165 require.Equal(t, "V" + s, v) 166 } 167 } 168 169 func BenchmarkCore(b *testing.B) { 170 b.Run("fenced-16K", func(b *testing.B) { 171 benchmarkCore(b, newStrategy(1024, 1<<14, true, false)) 172 }) 173 174 b.Run("bare-16K", func(b *testing.B) { 175 benchmarkCore(b, newStrategy(1024, 1<<14, false, false)) 176 }) 177 178 b.Run("fenced-16M", func(b *testing.B) { 179 benchmarkCore(b, newStrategy(1024, 1<<24, true, false)) 180 }) 181 182 b.Run("bare-16M", func(b *testing.B) { 183 benchmarkCore(b, newStrategy(1024, 1<<24, false, false)) 184 }) 185 } 186 187 func benchmarkCore(b *testing.B, s Strategy) { 188 c := NewUintCache(s) 189 b.Run("put", func(b *testing.B) { 190 b.ReportAllocs() 191 for i := b.N; i > 0; i-- { 192 c.Put(uint64(i), "") 193 } 194 }) 195 196 max := c.Occupied() 197 b.Run("put-dup", func(b *testing.B) { 198 b.ReportAllocs() 199 for i := b.N; i > 0; i-- { 200 c.Put(uint64(i%max), "") 201 } 202 }) 203 204 b.Run("peek", func(b *testing.B) { 205 b.ReportAllocs() 206 for i := b.N; i > 0; i-- { 207 c.Peek(uint64(i%max)) 208 } 209 }) 210 211 b.Run("get", func(b *testing.B) { 212 b.ReportAllocs() 213 for i := b.N; i > 0; i-- { 214 c.Get(uint64(i%max)) 215 } 216 }) 217 218 b.Run("get-subset", func(b *testing.B) { 219 b.ReportAllocs() 220 for i := b.N; i > 0; i-- { 221 c.Get(uint64((i&0xFF)<<10)) // access a small (256) set of records across all 222 } 223 }) 224 } 225 226 func BenchmarkFence(b *testing.B) { 227 b.Run("fenced", func(b *testing.B) { 228 benchmarkFence(b, newStrategy(32, 4, true, false)) 229 }) 230 231 b.Run("bare", func(b *testing.B) { 232 benchmarkFence(b, newStrategy(32, 4, false, false)) 233 }) 234 } 235 236 func benchmarkFence(b *testing.B, s Strategy) { 237 c := NewUintCache(s) 238 c.Put(0, "") 239 c.Put(1, "") 240 241 b.Run("get", func(b *testing.B) { 242 b.ReportAllocs() 243 for i := b.N; i > 0; i-- { 244 c.Get(uint64(i)&1) 245 } 246 }) 247 248 }