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>
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;
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;
}
}
(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
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;
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;
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
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);
}
{
last_pos = 0;
}
+ double _get_fragmentation() {
+ std::lock_guard<std::mutex> l(lock);
+ return l1.get_fragmentation();
+ }
};
#endif
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;
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));
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)
{
// digits after decimal point due to this.
EXPECT_EQ(0, uint64_t(alloc->get_fragmentation(alloc_unit) * 100));
}
-
INSTANTIATE_TEST_CASE_P(
Allocator,
AllocTest,