From dbbe12fe6f543fc6942f41a108effc7eb1632f67 Mon Sep 17 00:00:00 2001 From: Igor Fedotov Date: Fri, 15 Jun 2018 17:04:57 +0300 Subject: [PATCH 1/2] os/bluestore: provide a rough estimate for bitmap allocator 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 --- src/os/bluestore/BitmapAllocator.h | 4 ++ src/os/bluestore/fastbmap_allocator_impl.cc | 75 ++++++++++++++------- src/os/bluestore/fastbmap_allocator_impl.h | 23 ++++++- src/test/objectstore/Allocator_test.cc | 22 ++++-- 4 files changed, 90 insertions(+), 34 deletions(-) diff --git a/src/os/bluestore/BitmapAllocator.h b/src/os/bluestore/BitmapAllocator.h index c4f7f7beb29a8..d47d307c8e425 100755 --- a/src/os/bluestore/BitmapAllocator.h +++ b/src/os/bluestore/BitmapAllocator.h @@ -38,6 +38,10 @@ class BitmapAllocator : public Allocator, 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; diff --git a/src/os/bluestore/fastbmap_allocator_impl.cc b/src/os/bluestore/fastbmap_allocator_impl.cc index 6309c97a15596..9fdaab2fb3ba9 100755 --- a/src/os/bluestore/fastbmap_allocator_impl.cc +++ b/src/os/bluestore/fastbmap_allocator_impl.cc @@ -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; diff --git a/src/os/bluestore/fastbmap_allocator_impl.h b/src/os/bluestore/fastbmap_allocator_impl.h index 179a22c80777f..e490215a12d3f 100755 --- a/src/os/bluestore/fastbmap_allocator_impl.h +++ b/src/os/bluestore/fastbmap_allocator_impl.h @@ -96,6 +96,18 @@ class AllocatorLevel01 : public AllocatorLevel 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 @@ class AllocatorLevel01Loose : public AllocatorLevel01 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 @@ class AllocatorLevel02 : public AllocatorLevel { last_pos = 0; } + double _get_fragmentation() { + std::lock_guard l(lock); + return l1.get_fragmentation(); + } }; #endif diff --git a/src/test/objectstore/Allocator_test.cc b/src/test/objectstore/Allocator_test.cc index 5ff3aad7c59ea..3b6bc358856f7 100644 --- a/src/test/objectstore/Allocator_test.cc +++ b/src/test/objectstore/Allocator_test.cc @@ -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, From a072a5995d703ff1edc699fab8771d1955b8d6be Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 22 Jun 2018 13:00:58 -0500 Subject: [PATCH 2/2] githubmap: update contributors Signed-off-by: Sage Weil --- .githubmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.githubmap b/.githubmap index 3f3feaa6f4397..6de4c30adccd0 100644 --- a/.githubmap +++ b/.githubmap @@ -60,3 +60,4 @@ jan--f Jan Fajerski idryomov Ilya Dryomov varadakari Varada Kari ErwanAliasr1 Erwan Velu +aclamk Adam Kupczyk