bitmap_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Copyright 2018-present the CoreDHCP Authors. All rights reserved
  2. // This source code is licensed under the MIT license found in the
  3. // LICENSE file in the root directory of this source tree.
  4. package bitmap
  5. import (
  6. "math"
  7. "math/rand"
  8. "net"
  9. "testing"
  10. "github.com/bits-and-blooms/bitset"
  11. )
  12. func getAllocator(bits int) *Allocator {
  13. _, prefix, err := net.ParseCIDR("2001:db8::/56")
  14. if err != nil {
  15. panic(err)
  16. }
  17. alloc, err := NewBitmapAllocator(*prefix, 56+bits)
  18. if err != nil {
  19. panic(err)
  20. }
  21. return alloc
  22. }
  23. func TestAlloc(t *testing.T) {
  24. alloc := getAllocator(8)
  25. net, err := alloc.Allocate(net.IPNet{})
  26. if err != nil {
  27. t.Fatal(err)
  28. }
  29. err = alloc.Free(net)
  30. if err != nil {
  31. t.Fatal(err)
  32. }
  33. err = alloc.Free(net)
  34. if err == nil {
  35. t.Fatal("Expected DoubleFree error")
  36. }
  37. }
  38. func TestExhaust(t *testing.T) {
  39. _, prefix, _ := net.ParseCIDR("2001:db8::/62")
  40. alloc, _ := NewBitmapAllocator(*prefix, 64)
  41. allocd := []net.IPNet{}
  42. for i := 0; i < 4; i++ {
  43. net, err := alloc.Allocate(net.IPNet{Mask: net.CIDRMask(64, 128)})
  44. if err != nil {
  45. t.Fatalf("Error before exhaustion: %v", err)
  46. }
  47. allocd = append(allocd, net)
  48. }
  49. _, err := alloc.Allocate(net.IPNet{})
  50. if err == nil {
  51. t.Fatalf("Successfully allocated more prefixes than there are in the pool")
  52. }
  53. err = alloc.Free(allocd[1])
  54. if err != nil {
  55. t.Fatalf("Could not free: %v", err)
  56. }
  57. net, err := alloc.Allocate(allocd[1])
  58. if err != nil {
  59. t.Fatalf("Could not reallocate after free: %v", err)
  60. }
  61. if !net.IP.Equal(allocd[1].IP) || net.Mask.String() != allocd[1].Mask.String() {
  62. t.Fatalf("Did not obtain the right network after free: got %v, expected %v", net, allocd[1])
  63. }
  64. }
  65. func TestOutOfPool(t *testing.T) {
  66. alloc := getAllocator(8)
  67. _, prefix, _ := net.ParseCIDR("fe80:abcd::/48")
  68. res, err := alloc.Allocate(*prefix)
  69. if err != nil {
  70. t.Fatalf("Failed to allocate with invalid hint: %v", err)
  71. }
  72. if !alloc.containing.Contains(res.IP) {
  73. t.Fatal("Obtained prefix outside of range: ", res)
  74. }
  75. if prefLen, totalLen := res.Mask.Size(); prefLen != 64 || totalLen != 128 {
  76. t.Fatalf("Prefixes have wrong size %d/%d", prefLen, totalLen)
  77. }
  78. }
  79. func prefixSizeForAllocs(allocs int) int {
  80. return int(math.Ceil(math.Log2(float64(allocs))))
  81. }
  82. // Benchmark parallel Allocate, when the bitmap is mostly empty and we're allocating few values
  83. // compared to the available allocations
  84. func BenchmarkParallelAllocInitiallyEmpty(b *testing.B) {
  85. // Run with -race to debug concurrency issues
  86. alloc := getAllocator(prefixSizeForAllocs(b.N) + 2) // Use max 25% of the bitmap (initially empty)
  87. b.RunParallel(func(pb *testing.PB) {
  88. for pb.Next() {
  89. if net, err := alloc.Allocate(net.IPNet{}); err != nil {
  90. b.Logf("Could not allocate (got %v and an error): %v", net, err)
  91. b.Fail()
  92. }
  93. }
  94. })
  95. }
  96. func BenchmarkParallelAllocPartiallyFilled(b *testing.B) {
  97. // We'll make a bitmap with 2x the number of allocs we want to make.
  98. // Then randomly fill it to about 50% utilization
  99. alloc := getAllocator(prefixSizeForAllocs(b.N) + 1)
  100. // Build a replacement bitmap that we'll put in the allocator, with approx. 50% of values filled
  101. newbmap := make([]uint64, alloc.bitmap.Len())
  102. for i := uint(0); i < alloc.bitmap.Len(); i++ {
  103. newbmap[i] = rand.Uint64()
  104. }
  105. alloc.bitmap = bitset.From(newbmap)
  106. b.ResetTimer()
  107. b.RunParallel(func(pb *testing.PB) {
  108. for pb.Next() {
  109. if net, err := alloc.Allocate(net.IPNet{}); err != nil {
  110. b.Logf("Could not allocate (got %v and an error): %v", net, err)
  111. b.Fail()
  112. }
  113. }
  114. })
  115. }