github.com/pidato/unsafe@v0.1.4/memory/tlsf/heap_test.go (about) 1 package tlsf 2 3 import ( 4 "fmt" 5 "math/rand" 6 "runtime" 7 "sync" 8 "testing" 9 "time" 10 ) 11 12 func Test_AllocatorCounts(t *testing.T) { 13 p := NewHeapWithConfig(1, NewSysArena(), GrowMin) 14 p1 := p.Alloc(38) 15 println("alloc size", p.AllocSize) 16 p2 := p.Alloc(81) 17 println("alloc size", p.AllocSize) 18 p.Free(p1) 19 println("alloc size", p.AllocSize) 20 p.Free(p2) 21 println("alloc size", p.AllocSize) 22 } 23 24 //func Benchmark_Ctz32(b *testing.B) { 25 // b.Run("bits.TrailingZeroes32", func(b *testing.B) { 26 // for i := 0; i < b.N; i++ { 27 // bits.TrailingZeros32(100) 28 // bits.TrailingZeros32(101) 29 // bits.TrailingZeros32(102) 30 // } 31 // }) 32 // 33 // b.Run("Ctz32", func(b *testing.B) { 34 // for i := 0; i < b.N; i++ { 35 // Ctz32(100) 36 // Ctz32(101) 37 // Ctz32(102) 38 // } 39 // }) 40 //} 41 42 func Test_AllocatorThrash(t *testing.T) { 43 a := NewSysArena() 44 statsBefore := runtime.MemStats{} 45 runtime.ReadMemStats(&statsBefore) 46 thrashAllocator(NewHeapWithConfig(1, a, GrowMin), false, 47 1000000, 100, 15000, 21000, 48 randomSize(0.95, 16, 48), 49 randomSize(0.95, 48, 192), 50 randomSize(0.55, 64, 512), 51 //randomSize(0.70, 128, 512), 52 //randomSize(0.15, 128, 512), 53 //randomSize(0.30, 128, 1024), 54 ) 55 56 var statsAfter runtime.MemStats 57 runtime.ReadMemStats(&statsAfter) 58 fmt.Println("SysAllocator Size", a.Size()) 59 fmt.Println("GCStats Before", statsBefore) 60 fmt.Println("GCStats After", statsAfter) 61 62 //thrashAllocator(newAllocator(2), 100000, 100, 12000, 17000, 63 // randomSize(0.80, 24, 96), 64 // //randomSize(0.70, 128, 512), 65 // //randomSize(0.15, 128, 512), 66 // //randomSize(0.30, 128, 1024), 67 //) 68 } 69 70 type sizeClass struct { 71 pct float64 72 min, max int 73 next func() int 74 } 75 76 func randomSize(pct float64, min, max int) *sizeClass { 77 sz := &sizeClass{pct, min, max, nil} 78 sz.next = sz.nextRandom 79 return sz 80 } 81 82 func (s *sizeClass) nextRandom() int { 83 if s.max == s.min { 84 return s.max 85 } 86 return rand.Intn(s.max-s.min) + s.min 87 } 88 89 func thrashAllocator( 90 allocator *Heap, shuffle bool, 91 iterations, allocsPerIteration, minAllocs, maxAllocs int, 92 sizeClasses ...*sizeClass, 93 ) { 94 type allocation struct { 95 ptr uintptr 96 size int 97 } 98 99 sz := make([]int, 0, allocsPerIteration) 100 for _, sc := range sizeClasses { 101 for i := 0; i < int(float64(allocsPerIteration)*sc.pct); i++ { 102 sz = append(sz, sc.next()) 103 } 104 } 105 106 allocs := make([]allocation, 0, maxAllocs) 107 allocSize := 0 108 totalAllocs := 0 109 totalFrees := 0 110 maxAllocCount := 0 111 maxAllocSize := 0 112 113 if shuffle { 114 rand.Seed(time.Now().UnixNano()) 115 } 116 117 start := time.Now() 118 for i := 0; i < iterations; i++ { 119 if shuffle { 120 rand.Shuffle(len(sz), func(i, j int) { sz[i], sz[j] = sz[j], sz[i] }) 121 } 122 123 for _, size := range sz { 124 allocs = append(allocs, allocation{ 125 ptr: allocator.Alloc(uintptr(size)), //tlsfalloc(uintptr(size)), 126 size: size, 127 }) 128 allocSize += size 129 } 130 totalAllocs += len(sz) 131 132 if maxAllocCount < len(allocs) { 133 maxAllocCount = len(allocs) 134 } 135 if allocSize > maxAllocSize { 136 maxAllocSize = allocSize 137 } 138 139 if len(allocs) < minAllocs || len(allocs) < maxAllocs { 140 continue 141 } 142 143 //rand.Shuffle(len(allocs), func(i, j int) { allocs[i], allocs[j] = allocs[j], allocs[i] }) 144 max := randomRange(minAllocs, maxAllocs) 145 //max := maxAllocs 146 totalFrees += len(allocs) - max 147 for x := max; x < len(allocs); x++ { 148 alloc := allocs[x] 149 allocator.Free(alloc.ptr) 150 allocSize -= alloc.size 151 } 152 allocs = allocs[:max] 153 } 154 155 elapsed := time.Now().Sub(start) 156 seconds := float64(elapsed) / float64(time.Second) 157 println("total time ", elapsed.String()) 158 fmt.Printf("allocs per sec %.1f million / sec\n", float64(float64(totalAllocs)/seconds/1000000)) 159 //println("allocs per sec ", float64(totalAllocs) / seconds) 160 println("alloc bytes ", allocSize) 161 println("alloc count ", len(allocs)) 162 println("total allocs ", totalAllocs) 163 println("total frees ", totalFrees) 164 println("total frees ", totalFrees) 165 println("memory pages ", allocator.Pages) 166 println("heap size ", allocator.HeapSize) 167 println("free size ", allocator.FreeSize) 168 println("alloc size ", allocator.AllocSize) 169 //println("alloc size ", AllocSize) 170 println("max allocs ", maxAllocCount) 171 println("max alloc size ", allocator.PeakAllocSize) 172 println("fragmentation ", fmt.Sprintf("%.2f", allocator.Stats.Fragmentation())) 173 } 174 175 //func TestAllocatorKind(t *testing.T) { 176 // a := NewHeapWithConfig(1, NewSysArena(), GrowMin) 177 // s := a.ToSync() 178 // 179 // p1 := toTLSFAllocator(a) 180 // p2 := toTLSFSyncAllocator(s) 181 // 182 // p1.Free(p1.Create(64)) 183 // p2.Free(p2.Create(128)) 184 // 185 // println(p1, p1|_TLSFNoSync) 186 // println(p2, p2|_TLSFSync, (p2|_TLSFSync) & ^_AllocatorMask) 187 // println((p2 | _TLSFSync) & ^_AllocatorMask) 188 // println((p1 | _TLSFNoSync) & _AllocatorMask) 189 // println((p2 | _TLSFSync) & _AllocatorMask) 190 //} 191 192 func Test_Allocator(t *testing.T) { 193 a := NewHeapWithConfig(1, NewSysArena(), GrowMin) 194 PrintDebugInfo() 195 ptr := a.Alloc(16) 196 ptr2 := a.Alloc(49) 197 ptr4 := a.Alloc(8224) 198 println("ptr", uint(ptr)) 199 println("ptr2", uint(ptr2)) 200 ptr3 := a.Alloc(517) 201 a.Free(ptr) 202 a.Free(ptr4) 203 a.Free(ptr2) 204 a.Free(ptr3) 205 } 206 207 func BenchmarkAllocator_Alloc(b *testing.B) { 208 var ( 209 min, max = 24, 2048 210 showGCStats = true 211 ) 212 doAfter := func(before, after runtime.MemStats) { 213 if showGCStats { 214 fmt.Println("Before", "GC CPU", before.GCCPUFraction, "TotalAllocs", before.TotalAlloc, "Frees", before.Frees, "PauseNs Total", before.PauseTotalNs) 215 fmt.Println("After ", "GC CPU", after.GCCPUFraction, "TotalAllocs", after.TotalAlloc, "Frees", after.Frees, "PauseNs Total", after.PauseTotalNs) 216 println() 217 } 218 } 219 //b.Run("Random Range time", func(b *testing.B) { 220 // b.ReportAllocs() 221 // b.ResetTimer() 222 // for i := 0; i < b.N; i++ { 223 // randomRange(min, max) 224 // } 225 // after() 226 //}) 227 228 randomRangeSizes := make([]uintptr, 0, 256) 229 for i := 0; i < 1000; i++ { 230 randomRangeSizes = append(randomRangeSizes, uintptr(randomRange(min, max))) 231 } 232 233 //b.Run("Heap alloc", func(b *testing.B) { 234 // a := NewHeapWithConfig(1, NewSysArena(), GrowMin) 235 // var before runtime.MemStats 236 // runtime.ReadMemStats(&before) 237 // b.ReportAllocs() 238 // b.ResetTimer() 239 // for i := 0; i < b.N; i++ { 240 // size := randomRangeSizes[i%len(randomRangeSizes)] 241 // b.SetBytes(int64(size)) 242 // a.Free(a.Create(uintptr(size))) 243 // } 244 // after(before) 245 //}) 246 //b.Run("Heap allocZeroed", func(b *testing.B) { 247 // a := NewHeapWithConfig(1, NewSysArena(), GrowMin) 248 // var before runtime.MemStats 249 // runtime.ReadMemStats(&before) 250 // b.ReportAllocs() 251 // b.ResetTimer() 252 // for i := 0; i < b.N; i++ { 253 // size := randomRangeSizes[i%len(randomRangeSizes)] 254 // b.SetBytes(int64(size)) 255 // a.Free(a.AllocZeroed(uintptr(size))) 256 // } 257 // after(before) 258 //}) 259 //b.Run("Allocator alloc", func(b *testing.B) { 260 // al := NewHeapWithConfig(1, NewSysArena(), GrowMin) 261 // a := toTLSFAllocator(al) 262 // b.ReportAllocs() 263 // b.ResetTimer() 264 // for i := 0; i < b.N; i++ { 265 // size := randomRangeSizes[i%len(randomRangeSizes)] 266 // b.SetBytes(int64(size)) 267 // a.Free(a.Create(uintptr(size))) 268 // } 269 // after() 270 //}) 271 //b.Run("Allocator allocZeroed", func(b *testing.B) { 272 // al := NewHeapWithConfig(1, NewSysArena(), GrowMin) 273 // a := toTLSFAllocator(al) 274 // b.ReportAllocs() 275 // b.ResetTimer() 276 // for i := 0; i < b.N; i++ { 277 // size := randomRangeSizes[i%len(randomRangeSizes)] 278 // b.SetBytes(int64(size)) 279 // a.Free(a.AllocZeroed(uintptr(size))) 280 // } 281 // after() 282 //}) 283 b.Run("tlsf Heap alloc", func(b *testing.B) { 284 a := NewHeapWithConfig(1, NewSysArena(), GrowMin) //.ToSync() 285 runtime.GC() 286 runtime.GC() 287 b.ReportAllocs() 288 b.ResetTimer() 289 var before runtime.MemStats 290 runtime.ReadMemStats(&before) 291 for i := 0; i < b.N; i++ { 292 size := randomRangeSizes[i%len(randomRangeSizes)] 293 b.SetBytes(int64(size)) 294 a.Free(a.Alloc(uintptr(size))) 295 } 296 b.StopTimer() 297 var after runtime.MemStats 298 runtime.ReadMemStats(&after) 299 doAfter(before, after) 300 }) 301 //b.Run("Sync Heap allocZeroed", func(b *testing.B) { 302 // a := NewHeapWithConfig(1, NewSysArena(), GrowMin).ToSync() 303 // var before runtime.MemStats 304 // runtime.ReadMemStats(&before) 305 // b.ReportAllocs() 306 // b.ResetTimer() 307 // for i := 0; i < b.N; i++ { 308 // size := randomRangeSizes[i%len(randomRangeSizes)] 309 // b.SetBytes(int64(size)) 310 // a.Free(a.AllocZeroed(uintptr(size))) 311 // } 312 // b.StopTimer() 313 // var after runtime.MemStats 314 // runtime.ReadMemStats(&after) 315 // doAfter(before, after) 316 //}) 317 //b.Run("Sync Allocator alloc", func(b *testing.B) { 318 // al := NewHeapWithConfig(1, NewSysArena(), GrowMin).ToSync() 319 // a := toTLSFSyncAllocator(al) 320 // b.ReportAllocs() 321 // b.ResetTimer() 322 // for i := 0; i < b.N; i++ { 323 // size := randomRangeSizes[i%len(randomRangeSizes)] 324 // b.SetBytes(int64(size)) 325 // a.Free(a.Create(uintptr(size))) 326 // } 327 // after() 328 //}) 329 //b.Run("Sync Allocator allocZeroed", func(b *testing.B) { 330 // a := NewHeapWithConfig(1, NewSysArena(), GrowMin).ToSync() 331 // b.ReportAllocs() 332 // b.ResetTimer() 333 // for i := 0; i < b.N; i++ { 334 // size := randomRangeSizes[i%len(randomRangeSizes)] 335 // b.SetBytes(int64(size)) 336 // a.Free(a.AllocZeroed(uintptr(size))) 337 // } 338 // after() 339 //}) 340 341 b.Run("Go GC pool", func(b *testing.B) { 342 runtime.GC() 343 runtime.GC() 344 b.ReportAllocs() 345 b.ResetTimer() 346 var before runtime.MemStats 347 runtime.ReadMemStats(&before) 348 for i := 0; i < b.N; i++ { 349 size := randomRangeSizes[i%len(randomRangeSizes)] 350 b.SetBytes(int64(size)) 351 PutBytes(GetBytes(int(size))) 352 } 353 b.StopTimer() 354 var after runtime.MemStats 355 runtime.ReadMemStats(&after) 356 doAfter(before, after) 357 }) 358 359 b.Run("Go GC alloc", func(b *testing.B) { 360 runtime.GC() 361 runtime.GC() 362 b.ReportAllocs() 363 b.ResetTimer() 364 var before runtime.MemStats 365 runtime.ReadMemStats(&before) 366 for i := 0; i < b.N; i++ { 367 size := randomRangeSizes[i%len(randomRangeSizes)] 368 b.SetBytes(int64(size)) 369 _ = make([]byte, 0, size) 370 } 371 b.StopTimer() 372 var after runtime.MemStats 373 runtime.ReadMemStats(&after) 374 doAfter(before, after) 375 }) 376 } 377 378 func randomRange(min, max int) int { 379 return rand.Intn(max-min) + min 380 } 381 382 var ( 383 pool1 = &sync.Pool{New: func() interface{} { 384 return make([]byte, 1) 385 }} 386 pool2 = &sync.Pool{New: func() interface{} { 387 return make([]byte, 2) 388 }} 389 pool4 = &sync.Pool{New: func() interface{} { 390 return make([]byte, 4) 391 }} 392 pool8 = &sync.Pool{New: func() interface{} { 393 return make([]byte, 8) 394 }} 395 pool12 = &sync.Pool{New: func() interface{} { 396 return make([]byte, 12) 397 }} 398 pool16 = &sync.Pool{New: func() interface{} { 399 return make([]byte, 16) 400 }} 401 pool24 = &sync.Pool{New: func() interface{} { 402 return make([]byte, 24) 403 }} 404 pool32 = &sync.Pool{New: func() interface{} { 405 return make([]byte, 32) 406 }} 407 pool40 = &sync.Pool{New: func() interface{} { 408 return make([]byte, 40) 409 }} 410 pool48 = &sync.Pool{New: func() interface{} { 411 return make([]byte, 48) 412 }} 413 pool56 = &sync.Pool{New: func() interface{} { 414 return make([]byte, 56) 415 }} 416 pool64 = &sync.Pool{New: func() interface{} { 417 return make([]byte, 64) 418 }} 419 pool72 = &sync.Pool{New: func() interface{} { 420 return make([]byte, 72) 421 }} 422 pool96 = &sync.Pool{New: func() interface{} { 423 return make([]byte, 96) 424 }} 425 pool128 = &sync.Pool{New: func() interface{} { 426 return make([]byte, 128) 427 }} 428 pool192 = &sync.Pool{New: func() interface{} { 429 return make([]byte, 192) 430 }} 431 pool256 = &sync.Pool{New: func() interface{} { 432 return make([]byte, 256) 433 }} 434 pool384 = &sync.Pool{New: func() interface{} { 435 return make([]byte, 384) 436 }} 437 pool512 = &sync.Pool{New: func() interface{} { 438 return make([]byte, 512) 439 }} 440 pool768 = &sync.Pool{New: func() interface{} { 441 return make([]byte, 768) 442 }} 443 pool1024 = &sync.Pool{New: func() interface{} { 444 return make([]byte, 1024) 445 }} 446 pool2048 = &sync.Pool{New: func() interface{} { 447 return make([]byte, 2048) 448 }} 449 pool4096 = &sync.Pool{New: func() interface{} { 450 return make([]byte, 4096) 451 }} 452 pool8192 = &sync.Pool{New: func() interface{} { 453 return make([]byte, 8192) 454 }} 455 pool16384 = &sync.Pool{New: func() interface{} { 456 return make([]byte, 16384) 457 }} 458 pool32768 = &sync.Pool{New: func() interface{} { 459 return make([]byte, 32768) 460 }} 461 pool65536 = &sync.Pool{New: func() interface{} { 462 return make([]byte, 65536) 463 }} 464 ) 465 466 func GetBytes(n int) []byte { 467 v := ceilToPowerOfTwo(n) 468 switch v { 469 case 0, 1: 470 return pool1.Get().([]byte)[:n] 471 case 2: 472 return pool2.Get().([]byte)[:n] 473 case 4: 474 return pool4.Get().([]byte)[:n] 475 case 8: 476 return pool8.Get().([]byte)[:n] 477 case 16: 478 return pool16.Get().([]byte)[:n] 479 case 24: 480 return pool24.Get().([]byte)[:n] 481 case 32: 482 return pool32.Get().([]byte)[:n] 483 case 64: 484 switch { 485 case n < 41: 486 return pool40.Get().([]byte)[:n] 487 case n < 49: 488 return pool48.Get().([]byte)[:n] 489 case n < 57: 490 return pool56.Get().([]byte)[:n] 491 } 492 return pool64.Get().([]byte)[:n] 493 case 128: 494 switch { 495 case n < 73: 496 return pool72.Get().([]byte)[:n] 497 case n < 97: 498 return pool96.Get().([]byte)[:n] 499 } 500 return pool128.Get().([]byte)[:n] 501 case 256: 502 switch { 503 case n < 193: 504 return pool192.Get().([]byte)[:n] 505 } 506 return pool256.Get().([]byte)[:n] 507 case 512: 508 if n <= 384 { 509 return pool384.Get().([]byte) 510 } 511 return pool512.Get().([]byte)[:n] 512 case 1024: 513 if n <= 768 { 514 return pool768.Get().([]byte)[:n] 515 } 516 return pool1024.Get().([]byte)[:n] 517 case 2048: 518 return pool2048.Get().([]byte)[:n] 519 case 4096: 520 return pool4096.Get().([]byte)[:n] 521 case 8192: 522 return pool8192.Get().([]byte)[:n] 523 case 16384: 524 return pool16384.Get().([]byte)[:n] 525 case 32768: 526 return pool32768.Get().([]byte)[:n] 527 case 65536: 528 return pool65536.Get().([]byte)[:n] 529 } 530 531 return make([]byte, n) 532 } 533 534 func PutBytes(b []byte) { 535 switch cap(b) { 536 case 1: 537 pool1.Put(b) 538 case 2: 539 pool2.Put(b) 540 case 4: 541 pool4.Put(b) 542 case 8: 543 pool8.Put(b) 544 case 12: 545 pool12.Put(b) 546 case 16: 547 pool16.Put(b) 548 case 24: 549 pool24.Put(b) 550 case 32: 551 pool32.Put(b) 552 case 40: 553 pool40.Put(b) 554 case 48: 555 pool48.Put(b) 556 case 56: 557 pool56.Put(b) 558 case 64: 559 pool64.Put(b) 560 case 72: 561 pool72.Put(b) 562 case 96: 563 pool96.Put(b) 564 case 128: 565 pool128.Put(b) 566 case 192: 567 pool192.Put(b) 568 case 256: 569 pool256.Put(b) 570 case 384: 571 pool384.Put(b) 572 case 512: 573 pool512.Put(b) 574 case 768: 575 pool768.Put(b) 576 case 1024: 577 pool1024.Put(b) 578 case 2048: 579 pool2048.Put(b) 580 case 4096: 581 pool4096.Put(b) 582 case 8192: 583 pool8192.Put(b) 584 case 16384: 585 pool16384.Put(b) 586 case 32768: 587 pool32768.Put(b) 588 case 65536: 589 pool65536.Put(b) 590 } 591 } 592 593 const ( 594 bitsize = 32 << (^uint(0) >> 63) 595 maxint = int(1<<(bitsize-1) - 1) 596 maxintHeadBit = 1 << (bitsize - 2) 597 ) 598 599 // LogarithmicRange iterates from ceiled to power of two min to max, 600 // calling cb on each iteration. 601 func LogarithmicRange(min, max int, cb func(int)) { 602 if min == 0 { 603 min = 1 604 } 605 for n := ceilToPowerOfTwo(min); n <= max; n <<= 1 { 606 cb(n) 607 } 608 } 609 610 // IsPowerOfTwo reports whether given integer is a power of two. 611 func IsPowerOfTwo(n int) bool { 612 return n&(n-1) == 0 613 } 614 615 // Identity is identity. 616 func Identity(n int) int { 617 return n 618 } 619 620 // ceilToPowerOfTwo returns the least power of two integer value greater than 621 // or equal to n. 622 func ceilToPowerOfTwo(n int) int { 623 if n&maxintHeadBit != 0 && n > maxintHeadBit { 624 panic("argument is too large") 625 } 626 if n <= 2 { 627 return n 628 } 629 n-- 630 n = fillBits(n) 631 n++ 632 return n 633 } 634 635 // FloorToPowerOfTwo returns the greatest power of two integer value less than 636 // or equal to n. 637 func FloorToPowerOfTwo(n int) int { 638 if n <= 2 { 639 return n 640 } 641 n = fillBits(n) 642 n >>= 1 643 n++ 644 return n 645 } 646 647 func fillBits(n int) int { 648 n |= n >> 1 649 n |= n >> 2 650 n |= n >> 4 651 n |= n >> 8 652 n |= n >> 16 653 n |= n >> 32 654 return n 655 }