github.com/kpango/gache/v2@v2.0.8/gache_benchmark_test.go (about) 1 package gache 2 3 import ( 4 "fmt" 5 "math/rand" 6 "os" 7 "runtime" 8 "runtime/debug" 9 "strconv" 10 "strings" 11 "sync" 12 "testing" 13 "time" 14 "unsafe" 15 ) 16 17 type DefaultMap struct { 18 mu sync.RWMutex 19 data map[interface{}]interface{} 20 } 21 22 func NewDefault() *DefaultMap { 23 return &DefaultMap{ 24 data: make(map[interface{}]interface{}), 25 } 26 } 27 28 func (m *DefaultMap) Get(key interface{}) (interface{}, bool) { 29 m.mu.RLock() 30 defer m.mu.RUnlock() 31 v, ok := m.data[key] 32 return v, ok 33 } 34 35 func (m *DefaultMap) Set(key, val interface{}) { 36 m.mu.Lock() 37 defer m.mu.Unlock() 38 } 39 40 var ( 41 ttl time.Duration = 50 * time.Millisecond 42 43 parallelism = 10000 44 45 bigData = map[string]string{} 46 bigDataLen = 2 << 10 47 bigDataCount = 2 << 16 48 49 smallData = map[string]string{ 50 "string": "aaaa", 51 "int": "123", 52 "float": "99.99", 53 "struct": "struct{}{}", 54 } 55 ) 56 57 func init() { 58 for i := 0; i < bigDataCount; i++ { 59 bigData[randStr(bigDataLen)] = randStr(bigDataLen) 60 } 61 } 62 63 var randSrc = rand.NewSource(time.Now().UnixNano()) 64 65 const ( 66 rs6Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 67 rs6LetterIdxBits = 6 68 rs6LetterIdxMask = 1<<rs6LetterIdxBits - 1 69 rs6LetterIdxMax = 63 / rs6LetterIdxBits 70 ) 71 72 func randStr(n int) string { 73 b := make([]byte, n) 74 cache, remain := randSrc.Int63(), rs6LetterIdxMax 75 for i := n - 1; i >= 0; { 76 if remain == 0 { 77 cache, remain = randSrc.Int63(), rs6LetterIdxMax 78 } 79 idx := int(cache & rs6LetterIdxMask) 80 if idx < len(rs6Letters) { 81 b[i] = rs6Letters[idx] 82 i-- 83 } 84 cache >>= rs6LetterIdxBits 85 remain-- 86 } 87 return *(*string)(unsafe.Pointer(&b)) 88 } 89 90 func benchmark(b *testing.B, data map[string]string, 91 t time.Duration, 92 set func(string, string, time.Duration), 93 get func(string), 94 ) { 95 b.Helper() 96 b.SetParallelism(parallelism) 97 b.ReportAllocs() 98 b.ResetTimer() 99 b.RunParallel(func(pb *testing.PB) { 100 for pb.Next() { 101 for k, v := range data { 102 set(k, v, t) 103 } 104 for k := range data { 105 get(k) 106 } 107 } 108 }) 109 } 110 111 func BenchmarkDefaultMapSetSmallDataNoTTL(b *testing.B) { 112 m := NewDefault() 113 benchmark(b, smallData, NoTTL, 114 func(k, v string, t time.Duration) { m.Set(k, v) }, 115 func(k string) { m.Get(k) }) 116 } 117 118 func BenchmarkDefaultMapSetBigDataNoTTL(b *testing.B) { 119 m := NewDefault() 120 benchmark(b, bigData, NoTTL, 121 func(k, v string, t time.Duration) { m.Set(k, v) }, 122 func(k string) { m.Get(k) }) 123 } 124 125 func BenchmarkSyncMapSetSmallDataNoTTL(b *testing.B) { 126 var m sync.Map 127 benchmark(b, smallData, NoTTL, 128 func(k, v string, t time.Duration) { m.Store(k, v) }, 129 func(k string) { m.Load(k) }) 130 } 131 132 func BenchmarkSyncMapSetBigDataNoTTL(b *testing.B) { 133 var m sync.Map 134 benchmark(b, bigData, NoTTL, 135 func(k, v string, t time.Duration) { m.Store(k, v) }, 136 func(k string) { m.Load(k) }) 137 } 138 139 func BenchmarkGacheSetSmallDataNoTTL(b *testing.B) { 140 g := New[string]( 141 WithDefaultExpiration[string](NoTTL), 142 ) 143 benchmark(b, smallData, NoTTL, 144 func(k, v string, t time.Duration) { g.Set(k, v) }, 145 func(k string) { g.Get(k) }) 146 } 147 148 func BenchmarkGacheSetSmallDataWithTTL(b *testing.B) { 149 g := New( 150 WithDefaultExpiration[string](ttl), 151 ) 152 benchmark(b, smallData, ttl, 153 func(k, v string, t time.Duration) { g.SetWithExpire(k, v, t) }, 154 func(k string) { g.Get(k) }) 155 } 156 157 func BenchmarkGacheSetBigDataNoTTL(b *testing.B) { 158 g := New( 159 WithDefaultExpiration[string](NoTTL), 160 ) 161 benchmark(b, bigData, NoTTL, 162 func(k, v string, t time.Duration) { g.Set(k, v) }, 163 func(k string) { g.Get(k) }) 164 } 165 166 func BenchmarkGacheSetBigDataWithTTL(b *testing.B) { 167 g := New( 168 WithDefaultExpiration[string](ttl), 169 ) 170 benchmark(b, bigData, ttl, 171 func(k, v string, t time.Duration) { g.SetWithExpire(k, v, t) }, 172 func(k string) { g.Get(k) }) 173 } 174 175 func TestMain(m *testing.M) { 176 setup() 177 code := m.Run() 178 shutdown() 179 os.Exit(code) 180 } 181 182 func setup() { 183 debug.SetGCPercent(10) 184 } 185 186 func shutdown() { 187 PrintGCPause() 188 PrintMem() 189 PrintRate() 190 } 191 192 func BenchmarkHeavyMixedInt_gache(b *testing.B) { 193 gc := New[int]().SetDefaultExpire(10 * time.Second) 194 var wg sync.WaitGroup 195 for index := 0; index < 10000; index++ { 196 wg.Add(1) 197 go func() { 198 for i := 0; i < 8192; i++ { 199 gc.Set(Int64Key(int64(i)), i+1) 200 } 201 wg.Done() 202 }() 203 wg.Add(1) 204 go func() { 205 for i := 0; i < 8192; i++ { 206 gc.Get(Int64Key(int64(i))) 207 } 208 wg.Done() 209 }() 210 } 211 wg.Wait() 212 213 AddMem() 214 } 215 216 func BenchmarkPutInt_gache(b *testing.B) { 217 gc := New[int]().SetDefaultExpire(10 * time.Second) 218 // slen = 512 219 for i := 0; i < b.N; i++ { 220 gc.Set(Int64Key(int64(i)), i+1) 221 } 222 } 223 224 func BenchmarkGetInt_gache(b *testing.B) { 225 gc := New[string]().SetDefaultExpire(10 * time.Second) 226 // slen = 512 227 gc.Set("0", "0") 228 for i := 0; i < b.N; i++ { 229 gc.Get("0") 230 } 231 } 232 233 func BenchmarkPut1K_gache(b *testing.B) { 234 gc := New[[]byte]().SetDefaultExpire(10 * time.Second) 235 // slen = 512 236 for i := 0; i < b.N; i++ { 237 gc.Set(Int64Key(int64(i)), Data1K) 238 } 239 } 240 241 func BenchmarkPut1M_gache(b *testing.B) { 242 gc := New[[]byte]().SetDefaultExpire(10 * time.Second) 243 // slen = 512 244 for i := 0; i < b.N; i++ { 245 gc.Set(Int64Key(int64(i)), Data1M) 246 } 247 } 248 249 func BenchmarkPutTinyObject_gache(b *testing.B) { 250 gc := New[dummyData]().SetDefaultExpire(10 * time.Second) 251 // slen = 512 252 for i := 0; i < b.N; i++ { 253 gc.Set(Int64Key(int64(i)), dummyData{}) 254 } 255 } 256 257 func BenchmarkChangeOutAllInt_gache(b *testing.B) { 258 gc := New[int]().SetDefaultExpire(10 * time.Second) 259 // slen = 512 260 for i := 0; i < b.N*1024; i++ { 261 gc.Set(Int64Key(int64(i)), i+1) 262 } 263 } 264 265 func BenchmarkHeavyReadInt_gache(b *testing.B) { 266 gc := New[int]().SetDefaultExpire(10 * time.Second) 267 GCPause() 268 269 // slen = 512 270 for i := 0; i < 1024; i++ { 271 gc.Set(Int64Key(int64(i)), i+1) 272 } 273 var wg sync.WaitGroup 274 for index := 0; index < 10000; index++ { 275 wg.Add(1) 276 go func() { 277 for i := 0; i < 1024; i++ { 278 gc.Get(Int64Key(int64(i))) 279 } 280 wg.Done() 281 }() 282 } 283 wg.Wait() 284 285 AddGCPause() 286 } 287 288 func BenchmarkHeavyWriteInt_gache(b *testing.B) { 289 gc := New[int]().SetDefaultExpire(10 * time.Second) 290 GCPause() 291 292 // slen = 512 293 var wg sync.WaitGroup 294 for index := 0; index < 10000; index++ { 295 start := index 296 wg.Add(1) 297 go func() { 298 for i := 0; i < 8192; i++ { 299 gc.Set(Int64Key(int64(i+start)), i+1) 300 } 301 wg.Done() 302 }() 303 } 304 wg.Wait() 305 306 AddGCPause() 307 } 308 309 func BenchmarkHeavyWrite1K_gache(b *testing.B) { 310 gc := New[[]byte]().SetDefaultExpire(10 * time.Second) 311 GCPause() 312 313 // slen = 512 314 var wg sync.WaitGroup 315 for index := 0; index < 10000; index++ { 316 start := index 317 wg.Add(1) 318 go func() { 319 for i := 0; i < 8192; i++ { 320 gc.Set(Int64Key(int64(i+start)), Data1K) 321 } 322 wg.Done() 323 }() 324 } 325 wg.Wait() 326 327 AddGCPause() 328 } 329 330 func Int64Key(d int64) string { 331 return strconv.FormatInt(d, 10) 332 } 333 334 func randomString(n int) []byte { 335 b := make([]byte, n) 336 for i := range b { 337 b[i] = byte(rand.Intn(26) + 'a') 338 } 339 return b 340 } 341 342 var ( 343 Data1K = randomString(1024) 344 Data1M = randomString(1048576) 345 ) 346 347 var previousPause time.Duration 348 349 func GCPause() time.Duration { 350 runtime.GC() 351 var stats debug.GCStats 352 debug.ReadGCStats(&stats) 353 pause := stats.PauseTotal - previousPause 354 previousPause = stats.PauseTotal 355 return pause 356 } 357 358 var gcResult = make(map[string]time.Duration, 0) 359 360 func AddGCPause() { 361 pc, _, _, _ := runtime.Caller(1) 362 name := strings.Replace(runtime.FuncForPC(pc).Name(), "_", "GC_", 1) 363 name = name[strings.Index(name, "Benchmark"):] 364 if _, ok := gcResult[name]; !ok { 365 gcResult[name] = GCPause() 366 } 367 } 368 369 func PrintGCPause() { 370 for k, v := range gcResult { 371 fmt.Printf("%s-1 1 %d ns/op\n", k, v) 372 } 373 } 374 375 func PrintMem() { 376 for k, v := range memResult { 377 fmt.Printf("%s-1 1 %d B\n", k, v) 378 } 379 } 380 381 var memResult = make(map[string]uint64, 0) 382 383 func AddMem() { 384 var ms runtime.MemStats 385 runtime.ReadMemStats(&ms) 386 pc, _, _, _ := runtime.Caller(1) 387 name := strings.Replace(runtime.FuncForPC(pc).Name(), "_", "Mem_", 1) 388 name = name[strings.Index(name, "Benchmark"):] 389 if _, ok := memResult[name]; !ok { 390 memResult[name] = ms.Sys 391 } 392 } 393 394 var rateResult = make(map[string]float64, 0) 395 396 func AddRate(r float64) { 397 var ms runtime.MemStats 398 runtime.ReadMemStats(&ms) 399 pc, _, _, _ := runtime.Caller(1) 400 name := runtime.FuncForPC(pc).Name() 401 name = name[strings.Index(name, "Benchmark"):] 402 if _, ok := rateResult[name]; !ok { 403 rateResult[name] = r 404 } 405 } 406 407 func PrintRate() { 408 for k, v := range rateResult { 409 fmt.Printf("%s-1 1 %.2f %%\n", k, 100.*v) 410 } 411 } 412 413 type dummyData struct { 414 Name string 415 Age int32 416 Gender int32 417 Company string 418 Skills []string 419 }