New adaptation policy

This commit is contained in:
Efim Beshmenev
2026-08-13 23:42:26 +03:00
parent bbb43dbe57
commit 3770224eaf
16 changed files with 2533 additions and 489 deletions
+88 -12
View File
@@ -231,6 +231,31 @@ public:
}
}
template <class Predicate>
[[nodiscard]] std::size_t find_if(Predicate&& predicate) const {
std::size_t logical = 0;
for (const auto& leaf : leaves_) {
for (std::size_t i = 0; i < leaf->values.size(); ++i, ++logical) {
if (predicate(leaf->values[i])) {
return logical;
}
}
}
return npos;
}
template <class Predicate, class Function>
void for_each_match(Predicate&& predicate, Function&& function) const {
std::size_t logical = 0;
for (const auto& leaf : leaves_) {
for (std::size_t i = 0; i < leaf->values.size(); ++i, ++logical) {
if (predicate(leaf->values[i])) {
function(logical, leaf->values[i]);
}
}
}
}
template <class Function>
void for_each_with_location(Function&& function) const {
for (const auto& leaf : leaves_) {
@@ -262,21 +287,22 @@ public:
if (source.empty()) {
return result;
}
const auto target_occupancy = bulk_target_occupancy(
result.config_.leaf_capacity);
const auto count = (source.size() + target_occupancy - 1)
/ target_occupancy;
result.leaves_.reserve(count);
// Allocate the complete destination, including its directory, before
// moving the first value. For nothrow-move T the following loop and
// the final TieredStorage move cannot fail, so an allocation failure
// leaves source completely untouched.
const auto target_occupancy = result.prepare_bulk_destination(source.size());
std::size_t leaf_index = 0;
for (auto& value : source) {
if (result.leaves_.empty()
|| result.leaves_.back()->values.size() == target_occupancy) {
result.leaves_.push_back(result.make_leaf());
}
result.leaves_.back()->values.push_back(std::move(value));
auto& leaf = *result.leaves_[leaf_index];
leaf.values.push_back(std::move(value));
++result.size_;
if (leaf.values.size() == target_occupancy
&& leaf_index + 1 < result.leaves_.size()) {
++leaf_index;
}
}
result.rebuild_positions();
result.rebuild_directory();
assert(result.directory_.total() == result.size_);
for (std::size_t leaf = 0; leaf < result.leaves_.size(); ++leaf) {
result.refresh_leaf(leaf, relocate);
}
@@ -335,6 +361,31 @@ public:
return result;
}
// Rebuild a tiered layout without an intermediate vector. All leaves and
// directory arrays are allocated first; consequently this operation has a
// strong allocation-failure guarantee when T is nothrow-move-constructible.
static TieredStorage reconfigured_move(TieredStorage& source,
TieredConfig config) {
TieredStorage result(config);
if (source.empty()) {
return result;
}
const auto target_occupancy = result.prepare_bulk_destination(source.size());
std::size_t leaf_index = 0;
source.for_each([&](T& value) {
auto& leaf = *result.leaves_[leaf_index];
leaf.values.push_back(std::move(value));
++result.size_;
if (leaf.values.size() == target_occupancy
&& leaf_index + 1 < result.leaves_.size()) {
++leaf_index;
}
});
assert(result.directory_.total() == result.size_);
return result;
}
[[nodiscard]] std::size_t logical_index(leaf_id_type leaf_id,
std::size_t local) const {
if (leaf_id >= leaf_positions_.size()) {
@@ -387,6 +438,31 @@ private:
return std::max<std::size_t>(1, capacity - capacity / 8);
}
// Construct the entire shape needed by a bulk load while source elements
// are still untouched. RingBlock allocates all of its optional slots in
// its constructor, so inserting into these leaves does not allocate.
[[nodiscard]] std::size_t prepare_bulk_destination(std::size_t element_count) {
assert(element_count != 0);
const auto target_occupancy = bulk_target_occupancy(config_.leaf_capacity);
const auto count = 1 + (element_count - 1) / target_occupancy;
leaves_.reserve(count);
leaf_positions_.reserve(count);
std::vector<std::size_t> expected_sizes;
expected_sizes.reserve(count);
auto remaining = element_count;
for (std::size_t i = 0; i < count; ++i) {
leaves_.push_back(make_leaf());
const auto leaf_size = std::min(remaining, target_occupancy);
expected_sizes.push_back(leaf_size);
remaining -= leaf_size;
}
rebuild_positions();
directory_.rebuild(expected_sizes);
return target_occupancy;
}
[[nodiscard]] std::unique_ptr<Leaf> make_leaf() {
if (next_leaf_id_ == std::numeric_limits<leaf_id_type>::max()) {
throw std::length_error("tiered leaf id space exhausted");