]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
BlueStore/allocator: Improved (but slower) method of calculating fragmentation.
authorAdam Kupczyk <akupczyk@redhat.com>
Thu, 21 Jun 2018 11:58:45 +0000 (13:58 +0200)
committerNeha Ojha <nojha@redhat.com>
Wed, 7 Aug 2019 19:51:14 +0000 (15:51 -0400)
Signed-off-by: Adam Kupczyk <akupczyk@redhat.com>
(cherry picked from commit c1ac6a1207cdb71a471321e42ceca517383b36c8)

src/os/bluestore/Allocator.cc
src/os/bluestore/Allocator.h
src/os/bluestore/BitmapAllocator.cc
src/os/bluestore/BitmapAllocator.h
src/os/bluestore/StupidAllocator.cc
src/os/bluestore/StupidAllocator.h
src/os/bluestore/fastbmap_allocator_impl.cc
src/os/bluestore/fastbmap_allocator_impl.h

index b6026353165cba9d1250aa85cff1dc65ca634603..4c140cefd48278bf2d8e459e688796a8c0668eb8 100644 (file)
@@ -29,3 +29,55 @@ void Allocator::release(const PExtentVector& release_vec)
   }
   release(release_set);
 }
+
+/**
+ * Gives fragmentation a numeric value.
+ *
+ * Following algorithm applies value to each existing free unallocated block.
+ * Value of single block is a multiply of size and per-byte-value.
+ * Per-byte-value is greater for larger blocks.
+ * Assume block size X has value per-byte p; then block size 2*X will have per-byte value 1.1*p.
+ *
+ * This could be expressed in logarithms, but for speed this is interpolated inside ranges.
+ * [1]  [2..3] [4..7] [8..15] ...
+ * ^    ^      ^      ^
+ * 1.1  1.1^2  1.1^3  1.1^4 ...
+ *
+ * Final score is obtained by proportion between score that would have been obtained
+ * in condition of absolute fragmentation and score in no fragmentation at all.
+ */
+double Allocator::get_fragmentation_score()
+{
+  // this value represents how much worth is 2X bytes in one chunk then in X + X bytes
+  static const double double_size_worth = 1.1 ;
+  std::vector<double> scales{1};
+  double score_sum = 0;
+  size_t sum = 0;
+
+  auto get_score = [&](size_t v) -> double {
+    size_t sc = sizeof(v) * 8 - clz(v) - 1; //assign to grade depending on log2(len)
+    while (scales.size() <= sc + 1) {
+      //unlikely expand scales vector
+      scales.push_back(scales[scales.size() - 1] * double_size_worth);
+    }
+
+    size_t sc_shifted = size_t(1) << sc;
+    double x = double(v - sc_shifted) / sc_shifted; //x is <0,1) in its scale grade
+    // linear extrapolation in its scale grade
+    double score = (sc_shifted    ) * scales[sc]   * (1-x) +
+                   (sc_shifted * 2) * scales[sc+1] * x;
+    return score;
+  };
+
+  auto iterated_allocation = [&](size_t off, size_t len) {
+    ceph_assert(len > 0);
+    score_sum += get_score(len);
+    sum += len;
+  };
+  dump(iterated_allocation);
+
+
+  double ideal = get_score(sum);
+  double terrible = sum * get_score(1);
+  return (ideal - score_sum) / (ideal - terrible);
+}
index ddf194f91483c5bfac08a92c2ed93cccfa71fb2c..7425e33629d0eda8dcd7c7a56c286b48a69056a6 100644 (file)
@@ -15,6 +15,7 @@
 #include <ostream>
 #include "include/assert.h"
 #include "os/bluestore/bluestore_types.h"
+#include <functional>
 
 class FreelistManager;
 
@@ -46,6 +47,7 @@ public:
   void release(const PExtentVector& release_set);
 
   virtual void dump() = 0;
+  virtual void dump(std::function<void(uint64_t offset, uint64_t length)> notify) = 0;
 
   virtual void init_add_free(uint64_t offset, uint64_t length) = 0;
   virtual void init_rm_free(uint64_t offset, uint64_t length) = 0;
@@ -55,7 +57,7 @@ public:
   {
     return 0.0;
   }
-
+  virtual double get_fragmentation_score();
   virtual void shutdown() = 0;
   static Allocator *create(CephContext* cct, string type, int64_t size,
                           int64_t block_size);
index d632d5fab8772be69474b1781a3491614310d24f..3e80d0258436d3a25fec18f4911adad1e52da667 100755 (executable)
@@ -100,3 +100,13 @@ void BitmapAllocator::dump()
     ++it;
   }
 }
+
+void BitmapAllocator::dump(std::function<void(uint64_t offset, uint64_t length)> notify)
+{
+  size_t alloc_size = get_min_alloc_size();
+  auto multiply_by_alloc_size = [alloc_size, notify](size_t off, size_t len) {
+    notify(off * alloc_size, len * alloc_size);
+  };
+  std::lock_guard<std::mutex> lck(lock);
+  l1.dump(multiply_by_alloc_size);
+}
index 223c21dfbc5e87a64bb10d9fd015e704fa368ebf..df41c1a958c51040788c0a1fed8abf60b90e4997 100755 (executable)
@@ -36,6 +36,7 @@ public:
   }
 
   void dump() override;
+  void dump(std::function<void(uint64_t offset, uint64_t length)> notify) override;
   double get_fragmentation(uint64_t) override
   {
     return _get_fragmentation();
index c7224ea8c4e86b9836fd03c2e576197d0a8ee48b..0f957e4c6d129e2979cc2bf5f72dfdb7f912c87d 100644 (file)
@@ -287,6 +287,16 @@ void StupidAllocator::dump()
   }
 }
 
+void StupidAllocator::dump(std::function<void(uint64_t offset, uint64_t length)> notify)
+{
+  std::lock_guard<std::mutex> l(lock);
+  for (unsigned bin = 0; bin < free.size(); ++bin) {
+    for (auto p = free[bin].begin(); p != free[bin].end(); ++p) {
+      notify(p.get_start(), p.get_len());
+    }
+  }
+}
+
 void StupidAllocator::init_add_free(uint64_t offset, uint64_t length)
 {
   std::lock_guard<std::mutex> l(lock);
index c994cc718e45ce777edad9f3dfb8e818a645dedd..fb0e2761a0ae2153606cdfb022ed2ada295fb0c7 100644 (file)
@@ -52,6 +52,7 @@ public:
   double get_fragmentation(uint64_t alloc_unit) override;
 
   void dump() override;
+  void dump(std::function<void(uint64_t offset, uint64_t length)> notify) override;
 
   void init_add_free(uint64_t offset, uint64_t length) override;
   void init_rm_free(uint64_t offset, uint64_t length) override;
index 876e987dcc9059f1a48b440b8003575fc1a0ca84..73ac5d0097a880ff465ab03e75fbdbf2561c65a9 100755 (executable)
@@ -544,3 +544,88 @@ void AllocatorLevel01Loose::collect_stats(
     bins_overall[cbits(free_seq_cnt) - 1]++;
   }
 }
+
+inline ssize_t AllocatorLevel01Loose::count_0s(slot_t slot_val, size_t start_pos)
+  {
+  #ifdef __GNUC__
+    size_t pos = __builtin_ffsll(slot_val >> start_pos);
+    if (pos == 0)
+      return sizeof(slot_t)*8 - start_pos;
+    return pos - 1;
+  #else
+    size_t pos = start_pos;
+    slot_t mask = slot_t(1) << pos;
+    while (pos < bits_per_slot && (slot_val & mask) == 0) {
+      mask <<= 1;
+      pos++;
+    }
+    return pos - start_pos;
+  #endif
+  }
+
+ inline ssize_t AllocatorLevel01Loose::count_1s(slot_t slot_val, size_t start_pos)
+ {
+   return count_0s(~slot_val, start_pos);
+ }
+void AllocatorLevel01Loose::dump(
+    std::function<void(uint64_t offset, uint64_t length)> notify)
+{
+  size_t len = 0;
+  size_t off = 0;
+  for (size_t i = 0; i < l1.size(); i++)
+  {
+    for (size_t j = 0; j < L1_ENTRIES_PER_SLOT * L1_ENTRY_WIDTH; j += L1_ENTRY_WIDTH)
+    {
+      size_t w = (l1[i] >> j) & L1_ENTRY_MASK;
+      switch (w) {
+        case L1_ENTRY_FULL:
+          if (len > 0) {
+            notify(off, len);
+            len = 0;
+          }
+          break;
+        case L1_ENTRY_FREE:
+          if (len == 0)
+            off = ( ( bits_per_slot * i + j ) / L1_ENTRY_WIDTH ) * slots_per_slotset * bits_per_slot;
+          len += bits_per_slotset;
+          break;
+        case L1_ENTRY_PARTIAL:
+          size_t pos = ( ( bits_per_slot * i + j ) / L1_ENTRY_WIDTH ) * slots_per_slotset;
+          for (size_t t = 0; t < slots_per_slotset; t++) {
+            size_t p = 0;
+            slot_t allocation_pattern = l0[pos + t];
+            while (p < bits_per_slot) {
+              if (len == 0) {
+                //continue to skip allocated space, meaning bits set to 0
+                ssize_t alloc_count = count_0s(allocation_pattern, p);
+                p += alloc_count;
+                //now we are switched to expecting free space
+                if (p < bits_per_slot) {
+                  //now @p are 1s
+                  ssize_t free_count = count_1s(allocation_pattern, p);
+                  assert(free_count > 0);
+                  len = free_count;
+                  off = (pos + t) * bits_per_slot + p;
+                  p += free_count;
+                }
+              } else {
+                //continue free region
+                ssize_t free_count = count_1s(allocation_pattern, p);
+                if (free_count == 0) {
+                  notify(off, len);
+                  len = 0;
+                } else {
+                  p += free_count;
+                  len += free_count;
+                }
+              }
+            }
+          }
+          break;
+      }
+    }
+  }
+  if (len > 0)
+    notify(off, len);
+}
+
index 52764ae42b12be5a4697d802c8dbbadc7cf9b114..2c7112c123a35a7877492192850919a1dd73c102 100755 (executable)
@@ -46,6 +46,7 @@ typedef mempool::bluestore_alloc::vector<slot_t> slot_vector_t;
 
 // fitting into cache line on x86_64
 static const size_t slotset_width = 8; // 8 slots per set
+static const size_t slots_per_slotset = 8;
 static const size_t slotset_bytes = sizeof(slot_t) * slotset_width;
 static const size_t bits_per_slot = sizeof(slot_t) * 8;
 static const size_t bits_per_slotset = slotset_bytes * 8;
@@ -141,6 +142,7 @@ class AllocatorLevel01Loose : public AllocatorLevel01
     L1_ENTRY_NOT_USED = 0x02,
     L1_ENTRY_FREE = 0x03,
     CHILD_PER_SLOT = bits_per_slot / L1_ENTRY_WIDTH, // 32
+    L1_ENTRIES_PER_SLOT = bits_per_slot / L1_ENTRY_WIDTH, //32
     CHILD_PER_SLOT_L0 = bits_per_slot, // 64
   };
   uint64_t _children_per_slot() const override
@@ -469,8 +471,13 @@ public:
   }
   void collect_stats(
     std::map<size_t, size_t>& bins_overall) override;
+
+  static inline ssize_t count_0s(slot_t slot_val, size_t start_pos);
+  static inline ssize_t count_1s(slot_t slot_val, size_t start_pos);
+  void dump(std::function<void(uint64_t offset, uint64_t length)> notify);
 };
 
+
 class AllocatorLevel01Compact : public AllocatorLevel01
 {
   uint64_t _children_per_slot() const override