]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore: provide a rough estimate for bitmap allocator 22578/head
authorIgor Fedotov <ifedotov@suse.com>
Fri, 15 Jun 2018 14:04:57 +0000 (17:04 +0300)
committerIgor Fedotov <ifedotov@suse.com>
Fri, 15 Jun 2018 14:04:57 +0000 (17:04 +0300)
fragmentation.
The approach is counting 'partial' and 'free' slots at L1 on the fly and
use partial / (partial+free) value as rough fragmentation estimate.

Signed-off-by: Igor Fedotov <ifedotov@suse.com>
src/os/bluestore/BitmapAllocator.h
src/os/bluestore/fastbmap_allocator_impl.cc
src/os/bluestore/fastbmap_allocator_impl.h
src/test/objectstore/Allocator_test.cc

index c4f7f7beb29a8cbecfb38ce74e3b45beeb7a7172..d47d307c8e4255b6dbcda1437cfea2e6c31f82e5 100755 (executable)
@@ -38,6 +38,10 @@ public:
   void dump() override
   {
   }
+  double get_fragmentation(uint64_t) override
+  {
+    return _get_fragmentation();
+  }
 
   void init_add_free(uint64_t offset, uint64_t length) override;
   void init_rm_free(uint64_t offset, uint64_t length) override;
index 6309c97a15596e3070feb4aa040c42f8a432d5c2..9fdaab2fb3ba9950e338d94d389f6f68279088c9 100755 (executable)
@@ -218,51 +218,63 @@ void AllocatorLevel01Loose::_mark_l1_on_l0(int64_t l0_pos, int64_t l0_pos_end)
 
   int64_t idx = l0_pos / bits_per_slot;
   int64_t idx_end = l0_pos_end / bits_per_slot;
-  bool was_all_free = true;
-  bool was_all_allocated = true;
+  slot_t mask_to_apply = L1_ENTRY_NOT_USED;
 
   auto l1_pos = l0_pos / d0;
 
   while (idx < idx_end) {
     if (l0[idx] == all_slot_clear) {
-      was_all_free = false;
-
       // if not all prev slots are allocated then no need to check the
       // current slot set, it's partial
       ++idx;
-      idx =
-        was_all_allocated ? idx : p2roundup(idx, int64_t(slotset_width));
+      if (mask_to_apply == L1_ENTRY_NOT_USED) {
+       mask_to_apply = L1_ENTRY_FULL;
+      } else if (mask_to_apply != L1_ENTRY_FULL) {
+       idx = p2roundup(idx, int64_t(slotset_width));
+        mask_to_apply = L1_ENTRY_PARTIAL;
+      }
     } else if (l0[idx] == all_slot_set) {
-      // all free
-      was_all_allocated = false;
       // if not all prev slots are free then no need to check the
       // current slot set, it's partial
       ++idx;
-      idx = was_all_free ? idx : p2roundup(idx, int64_t(slotset_width));
+      if (mask_to_apply == L1_ENTRY_NOT_USED) {
+       mask_to_apply = L1_ENTRY_FREE;
+      } else if (mask_to_apply != L1_ENTRY_FREE) {
+       idx = p2roundup(idx, int64_t(slotset_width));
+        mask_to_apply = L1_ENTRY_PARTIAL;
+      }
     } else {
       // no need to check the current slot set, it's partial
-      was_all_free = false;
-      was_all_allocated = false;
+      mask_to_apply = L1_ENTRY_PARTIAL;
       ++idx;
       idx = p2roundup(idx, int64_t(slotset_width));
     }
     if ((idx % slotset_width) == 0) {
-
+      assert(mask_to_apply != L1_ENTRY_NOT_USED);
       uint64_t shift = (l1_pos % l1_w) * L1_ENTRY_WIDTH;
       slot_t& slot_val = l1[l1_pos / l1_w];
-        slot_val &= ~(uint64_t(L1_ENTRY_MASK) << shift);
-
-      if (was_all_allocated) {
-        assert(!was_all_free);
-        slot_val |= uint64_t(L1_ENTRY_FULL) << shift;
-      } else if (was_all_free) {
-        assert(!was_all_allocated);
-        slot_val |= uint64_t(L1_ENTRY_FREE) << shift;
-      } else {
-        slot_val |= uint64_t(L1_ENTRY_PARTIAL) << shift;
+      auto mask = slot_t(L1_ENTRY_MASK) << shift;
+
+      slot_t old_mask = (slot_val & mask) >> shift;
+      switch(old_mask) {
+      case L1_ENTRY_FREE:
+       unalloc_l1_count--;
+       break;
+      case L1_ENTRY_PARTIAL:
+       partial_l1_count--;
+       break;
+      }
+      slot_val &= ~mask;
+      slot_val |= slot_t(mask_to_apply) << shift;
+      switch(mask_to_apply) {
+      case L1_ENTRY_FREE:
+       unalloc_l1_count++;
+       break;
+      case L1_ENTRY_PARTIAL:
+       partial_l1_count++;
+       break;
       }
-      was_all_free = true;
-      was_all_allocated = true;
+      mask_to_apply = L1_ENTRY_NOT_USED;
       ++l1_pos;
     }
   }
@@ -465,7 +477,19 @@ bool AllocatorLevel01Loose::_allocate_l1(uint64_t length,
           (idx * d1 + free_pos / L1_ENTRY_WIDTH + 1) * l0_w,
           allocated,
           res);
-        slot_val &= (~slot_t(L1_ENTRY_MASK)) << free_pos;
+
+       auto mask = slot_t(L1_ENTRY_MASK) << free_pos;
+
+       slot_t old_mask = (slot_val & mask) >> free_pos;
+       switch(old_mask) {
+       case L1_ENTRY_FREE:
+         unalloc_l1_count--;
+         break;
+       case L1_ENTRY_PARTIAL:
+         partial_l1_count--;
+         break;
+       }
+        slot_val &= ~mask;
         if (empty) {
           // the next line is no op with the current L1_ENTRY_FULL but left
           // as-is for the sake of uniformity and to avoid potential errors
@@ -473,6 +497,7 @@ bool AllocatorLevel01Loose::_allocate_l1(uint64_t length,
           slot_val |= slot_t(L1_ENTRY_FULL) << free_pos;
         } else {
           slot_val |= slot_t(L1_ENTRY_PARTIAL) << free_pos;
+         partial_l1_count++;
         }
         if (length <= *allocated || slot_val == all_slot_clear) {
           break;
index 179a22c80777f2a24c1a5204c4bd70be86ee34b6..e490215a12d3fd47734533a5366df483a17cd41b 100755 (executable)
@@ -96,6 +96,18 @@ protected:
   uint64_t l0_granularity = 0; // space per entry
   uint64_t l1_granularity = 0; // space per entry
 
+  size_t partial_l1_count = 0;
+  size_t unalloc_l1_count = 0;
+
+  double get_fragmentation() const {
+    double res = 0.0;
+    auto total = unalloc_l1_count + partial_l1_count;
+    if (total) {
+      res = double(partial_l1_count) / double(total);
+    }
+    return res;
+  }
+
   uint64_t _level_granularity() const override
   {
     return l1_granularity;
@@ -122,6 +134,7 @@ class AllocatorLevel01Loose : public AllocatorLevel01
     L1_ENTRY_MASK = (1 << L1_ENTRY_WIDTH) - 1,
     L1_ENTRY_FULL = 0x00,
     L1_ENTRY_PARTIAL = 0x01,
+    L1_ENTRY_NOT_USED = 0x02,
     L1_ENTRY_FREE = 0x03,
     CHILD_PER_SLOT = bits_per_slot / L1_ENTRY_WIDTH, // 32
     CHILD_PER_SLOT_L0 = bits_per_slot, // 64
@@ -265,11 +278,13 @@ protected:
     l1.resize(slot_count, mark_as_free ? all_slot_set : all_slot_clear);
 
     // l0 slot count
-    slot_count = aligned_capacity / _alloc_unit / bits_per_slot;
+    size_t slot_count_l0 = aligned_capacity / _alloc_unit / bits_per_slot;
     // we use set bit(s) as a marker for (partially) free entry
-    l0.resize(slot_count, mark_as_free ? all_slot_set : all_slot_clear);
+    l0.resize(slot_count_l0, mark_as_free ? all_slot_set : all_slot_clear);
 
+    partial_l1_count = unalloc_l1_count = 0;
     if (mark_as_free) {
+      unalloc_l1_count = slot_count * _children_per_slot();
       auto l0_pos_no_use = p2roundup((int64_t)capacity, (int64_t)l0_granularity) / l0_granularity;
       _mark_alloc_l1_l0(l0_pos_no_use, aligned_capacity / l0_granularity);
     }
@@ -729,6 +744,10 @@ protected:
   {
     last_pos = 0;
   }
+  double _get_fragmentation() {
+    std::lock_guard<std::mutex> l(lock);
+    return l1.get_fragmentation();
+  }
 };
 
 #endif
index 5ff3aad7c59eaec1e879ffe19b643a16adff92b5..3b6bc358856f70b9627955fecabc2005fe408b61 100644 (file)
@@ -227,9 +227,6 @@ TEST_P(AllocTest, test_alloc_non_aligned_len)
 
 TEST_P(AllocTest, test_alloc_fragmentation)
 {
-  if (GetParam() == std::string("bitmap")) {
-    return;
-  }
   uint64_t capacity = 4 * 1024 * 1024;
   uint64_t alloc_unit = 4096;
   uint64_t want_size = alloc_unit;
@@ -237,14 +234,21 @@ TEST_P(AllocTest, test_alloc_fragmentation)
   
   init_alloc(capacity, alloc_unit);
   alloc->init_add_free(0, capacity);
+  bool bitmap_alloc = GetParam() == std::string("bitmap");
   
   EXPECT_EQ(0.0, alloc->get_fragmentation(alloc_unit));
+
   for (size_t i = 0; i < capacity / alloc_unit; ++i)
   {
     tmp.clear();
     EXPECT_EQ(want_size, alloc->allocate(want_size, alloc_unit, 0, 0, &tmp));
     allocated.insert(allocated.end(), tmp.begin(), tmp.end());
-    EXPECT_EQ(0.0, alloc->get_fragmentation(alloc_unit));
+
+    // bitmap fragmentation calculation doesn't provide such constant
+    // estimate
+    if (!bitmap_alloc) {
+      EXPECT_EQ(0.0, alloc->get_fragmentation(alloc_unit));
+    }
   }
   EXPECT_EQ(-ENOSPC, alloc->allocate(want_size, alloc_unit, 0, 0, &tmp));
 
@@ -261,8 +265,13 @@ TEST_P(AllocTest, test_alloc_fragmentation)
     release_set.insert(allocated[i].offset, allocated[i].length);
     alloc->release(release_set);
   }
-  // fragmentation approx = 257 intervals / 768 max intervals
-  EXPECT_EQ(33, uint64_t(alloc->get_fragmentation(alloc_unit) * 100));
+  if (bitmap_alloc) {
+    // fragmentation = one l1 slot is free + one l1 slot is partial
+    EXPECT_EQ(50, uint64_t(alloc->get_fragmentation(alloc_unit) * 100));
+  } else {
+    // fragmentation approx = 257 intervals / 768 max intervals
+    EXPECT_EQ(33, uint64_t(alloc->get_fragmentation(alloc_unit) * 100));
+  }
 
   for (size_t i = allocated.size() / 2 + 1; i < allocated.size(); i += 2)
   {
@@ -276,7 +285,6 @@ TEST_P(AllocTest, test_alloc_fragmentation)
   // digits after decimal point due to this.
   EXPECT_EQ(0, uint64_t(alloc->get_fragmentation(alloc_unit) * 100));
 }
-
 INSTANTIATE_TEST_CASE_P(
   Allocator,
   AllocTest,