Skip to content

Commit 55f65bd

Browse files
committed
os/bluestore: refactor allocator histogram to use ExtentCollectionTraits
Signed-off-by: Igor Fedotov <[email protected]>
1 parent 3db0e22 commit 55f65bd

File tree

4 files changed

+123
-101
lines changed

4 files changed

+123
-101
lines changed

src/os/bluestore/Allocator.cc

Lines changed: 50 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class Allocator::SocketHook : public AdminSocketHook {
110110
} else if (command == "bluestore allocator fragmentation histogram " + name) {
111111
int64_t alloc_unit = 4096;
112112
cmd_getval(cmdmap, "alloc_unit", alloc_unit);
113-
if (alloc_unit == 0 ||
113+
if (alloc_unit <= 0 ||
114114
p2align(alloc_unit, alloc->get_block_size()) != alloc_unit) {
115115
ss << "Invalid allocation unit: '" << alloc_unit
116116
<< ", to be aligned with: '" << alloc->get_block_size()
@@ -125,20 +125,22 @@ class Allocator::SocketHook : public AdminSocketHook {
125125
return -EINVAL;
126126
}
127127

128-
Allocator::FreeStateHistogram hist;
129-
hist.resize(num_buckets);
130-
alloc->build_free_state_histogram(alloc_unit, hist);
128+
Allocator::FreeStateHistogram hist(num_buckets);
129+
alloc->foreach(
130+
[&](size_t off, size_t len) {
131+
hist.record_extent(uint64_t(alloc_unit), off, len);
132+
});
131133
f->open_array_section("extent_counts");
132-
for(int i = 0; i < num_buckets; i++) {
133-
f->open_object_section("c");
134-
f->dump_unsigned("max_len",
135-
hist[i].get_max(i, num_buckets)
136-
);
137-
f->dump_unsigned("total", hist[i].total);
138-
f->dump_unsigned("aligned", hist[i].aligned);
139-
f->dump_unsigned("units", hist[i].alloc_units);
140-
f->close_section();
141-
}
134+
hist.foreach(
135+
[&](uint64_t max_len, uint64_t total, uint64_t aligned, uint64_t units) {
136+
f->open_object_section("c");
137+
f->dump_unsigned("max_len", max_len);
138+
f->dump_unsigned("total", total);
139+
f->dump_unsigned("aligned", aligned);
140+
f->dump_unsigned("units", units);
141+
f->close_section();
142+
}
143+
);
142144
f->close_section();
143145
} else {
144146
ss << "Invalid command" << std::endl;
@@ -273,50 +275,40 @@ double Allocator::get_fragmentation_score()
273275
return (ideal - score_sum) / (ideal - terrible);
274276
}
275277

276-
void Allocator::build_free_state_histogram(
277-
size_t alloc_unit, Allocator::FreeStateHistogram& hist)
278-
{
279-
auto num_buckets = hist.size();
280-
ceph_assert(num_buckets);
278+
/*************
279+
* Allocator::FreeStateHistogram
280+
*************/
281+
using std::function;
281282

282-
auto base = free_state_hist_bucket::base;
283-
auto base_bits = free_state_hist_bucket::base_bits;
284-
auto mux = free_state_hist_bucket::mux;
285-
// maximum chunk size we track,
286-
// provided by the bucket before the last one
287-
size_t max =
288-
free_state_hist_bucket::get_max(num_buckets - 2, num_buckets);
289-
290-
auto iterated_allocation = [&](size_t off, size_t len) {
291-
size_t idx;
292-
if (len <= base) {
293-
idx = 0;
294-
} else if (len > max) {
295-
idx = num_buckets - 1;
296-
} else {
297-
size_t most_bit = cbits(uint64_t(len-1)) - 1;
298-
idx = 1 + ((most_bit - base_bits) / mux);
299-
}
300-
ceph_assert(idx < num_buckets);
301-
++hist[idx].total;
302-
303-
// now calculate the bucket for the chunk after alignment,
304-
// resulting chunks shorter than alloc_unit are discarded
305-
auto delta = p2roundup(off, alloc_unit) - off;
306-
if (len >= delta + alloc_unit) {
307-
len -= delta;
308-
if (len <= base) {
309-
idx = 0;
310-
} else if (len > max) {
311-
idx = num_buckets - 1;
312-
} else {
313-
size_t most_bit = cbits(uint64_t(len-1)) - 1;
314-
idx = 1 + ((most_bit - base_bits) / mux);
315-
}
316-
++hist[idx].aligned;
317-
hist[idx].alloc_units += len / alloc_unit;
318-
}
319-
};
283+
void Allocator::FreeStateHistogram::record_extent(uint64_t alloc_unit,
284+
uint64_t off,
285+
uint64_t len)
286+
{
287+
size_t idx = myTraits._get_p2_size_bucket(len);
288+
ceph_assert(idx < buckets.size());
289+
++buckets[idx].total;
320290

321-
foreach(iterated_allocation);
291+
// now calculate the bucket for the chunk after alignment,
292+
// resulting chunks shorter than alloc_unit are discarded
293+
auto delta = p2roundup(off, alloc_unit) - off;
294+
if (len >= delta + alloc_unit) {
295+
len -= delta;
296+
idx = myTraits._get_p2_size_bucket(len);
297+
ceph_assert(idx < buckets.size());
298+
++buckets[idx].aligned;
299+
buckets[idx].alloc_units += len / alloc_unit;
300+
}
301+
}
302+
void Allocator::FreeStateHistogram::foreach(
303+
function<void(uint64_t max_len,
304+
uint64_t total,
305+
uint64_t aligned,
306+
uint64_t unit)> cb)
307+
{
308+
size_t i = 0;
309+
for (const auto& b : buckets) {
310+
cb(myTraits._get_p2_size_bucket_max(i),
311+
b.total, b.aligned, b.alloc_units);
312+
++i;
313+
}
322314
}

src/os/bluestore/Allocator.h

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ class Allocator {
6666
ceph_assert(idx < num_buckets);
6767
return idx;
6868
}
69+
/*
70+
* returns upper bound of the bucket with log2(len) indexing.
71+
*/
72+
inline size_t _get_p2_size_bucket_max(size_t bucket) const {
73+
return
74+
bucket < num_buckets - 1 ?
75+
base << (factor * bucket) :
76+
std::numeric_limits<uint64_t>::max();
77+
};
78+
6979
/*
7080
* Determines bucket index for a given extent's length in a bucket collection
7181
* with linear (len / min_extent_size) indexing.
@@ -76,6 +86,16 @@ class Allocator {
7686
idx = idx < num_buckets ? idx : num_buckets - 1;
7787
return idx;
7888
}
89+
/*
90+
* returns upper bound of the bucket with
91+
* linear(len / min_extent_size) indexing.
92+
*/
93+
inline size_t _get_linear_size_bucket_max(size_t bucket) const {
94+
return
95+
bucket < num_buckets - 1 ?
96+
base * factor * (1 + bucket) :
97+
std::numeric_limits<uint64_t>::max();
98+
};
7999
};
80100

81101
/*
@@ -264,7 +284,7 @@ class Allocator {
264284
return block_size;
265285
}
266286

267-
// The following code build Allocator's free extents histogram.
287+
// The following class implements Allocator's free extents histogram.
268288
// Which is a set of N buckets to track extents layout.
269289
// Extent matches a bucket depending on its length using the following
270290
// length spans:
@@ -274,27 +294,30 @@ class Allocator {
274294
// - amount of extents aligned with allocation boundary
275295
// - amount of allocation units in aligned extents
276296
//
277-
struct free_state_hist_bucket {
278-
static const size_t base_bits = 12;
279-
static const size_t base = 1ull << base_bits;
280-
static const size_t mux = 2;
297+
class FreeStateHistogram {
298+
const Allocator::ExtentCollectionTraits myTraits;
299+
enum {
300+
BASE_BITS = 12, // 4096 bytes
301+
FACTOR = 2,
302+
};
303+
struct free_state_hist_bucket {
304+
size_t total = 0;
305+
size_t aligned = 0;
306+
size_t alloc_units = 0;
307+
};
308+
std::vector<free_state_hist_bucket> buckets;
309+
public:
281310

282-
size_t total = 0;
283-
size_t aligned = 0;
284-
size_t alloc_units = 0;
311+
FreeStateHistogram(size_t num_buckets)
312+
: myTraits(num_buckets, BASE_BITS, FACTOR) {
313+
buckets.resize(num_buckets);
314+
}
285315

286-
// returns upper bound of the bucket
287-
static size_t get_max(size_t bucket, size_t num_buckets) {
288-
return
289-
bucket < num_buckets - 1 ?
290-
base << (mux * bucket) :
291-
std::numeric_limits<uint64_t>::max();
292-
};
316+
void record_extent(uint64_t alloc_unit, uint64_t off, uint64_t len);
317+
void foreach(
318+
std::function<void(uint64_t, uint64_t, uint64_t, uint64_t)> cb);
293319
};
294320

295-
typedef std::vector<free_state_hist_bucket> FreeStateHistogram;
296-
void build_free_state_histogram(size_t alloc_unit, FreeStateHistogram& hist);
297-
298321
private:
299322
class SocketHook;
300323
SocketHook* asok_hook = nullptr;
@@ -303,4 +326,4 @@ class Allocator {
303326
const int64_t block_size = 0;
304327
};
305328

306-
#endif
329+
#endif

src/test/objectstore/Allocator_bench.cc

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -292,18 +292,22 @@ struct OverwriteTextContext : public Thread {
292292
}
293293

294294
void build_histogram() {
295-
size_t num_buckets = 8;
296-
Allocator::FreeStateHistogram hist;
297-
hist.resize(num_buckets);
298-
alloc->build_free_state_histogram(alloc_unit, hist);
299-
for (size_t i = 0; i < num_buckets; i++) {
300-
uint64_t a_bytes = (hist[i].alloc_units * alloc_unit);
301-
std::cout << "<=" << hist[i].get_max(i, num_buckets)
302-
<< " -> " << hist[i].total << "/" << hist[i].aligned
303-
<< " a_bytes " << a_bytes
304-
<< " " << ((float)a_bytes / alloc->get_capacity() * 100) << "%"
305-
<< std::endl;
306-
}
295+
const size_t num_buckets = 8;
296+
Allocator::FreeStateHistogram hist(num_buckets);
297+
alloc->foreach(
298+
[&](size_t off, size_t len) {
299+
hist.record_extent(uint64_t(alloc_unit), off, len);
300+
});
301+
302+
hist.foreach(
303+
[&](uint64_t max_len, uint64_t total, uint64_t aligned, uint64_t units) {
304+
uint64_t a_bytes = units * alloc_unit;
305+
std::cout << "<=" << max_len
306+
<< " -> " << total << "/" << aligned
307+
<< " a_bytes " << a_bytes
308+
<< " " << ((float)a_bytes / alloc->get_capacity() * 100) << "%"
309+
<< std::endl;
310+
});
307311
}
308312

309313
void* entry() override {

src/test/objectstore/allocator_replay_test.cc

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -773,20 +773,23 @@ int main(int argc, char **argv)
773773
std::cout << "Allocation unit:" << alloc_unit
774774
<< std::endl;
775775

776-
Allocator::FreeStateHistogram hist;
777-
hist.resize(num_buckets);
778-
a->build_free_state_histogram(alloc_unit, hist);
776+
Allocator::FreeStateHistogram hist(num_buckets);
777+
a->foreach(
778+
[&](size_t off, size_t len) {
779+
hist.record_extent(uint64_t(alloc_unit), off, len);
780+
});
779781

780782
uint64_t s = 0;
781-
for(int i = 0; i < num_buckets; i++) {
782-
uint64_t e = hist[i].get_max(i, num_buckets);
783-
std::cout << "(" << s << ".." << e << "]"
784-
<< " -> " << hist[i].total
785-
<< " chunks, " << hist[i].aligned << " aligned with "
786-
<< hist[i].alloc_units << " alloc_units."
787-
<< std::endl;
788-
s = e;
789-
}
783+
hist.foreach(
784+
[&](uint64_t max_len, uint64_t total, uint64_t aligned, uint64_t units) {
785+
uint64_t e = max_len;
786+
std::cout << "(" << s << ".." << e << "]"
787+
<< " -> " << total
788+
<< " chunks, " << aligned << " aligned with "
789+
<< units << " alloc_units."
790+
<< std::endl;
791+
s = e;
792+
});
790793
return 0;
791794
});
792795
} else if (strcmp(argv[2], "export_binary") == 0) {

0 commit comments

Comments
 (0)