New adaptation policy
This commit is contained in:
@@ -151,6 +151,89 @@ struct AdaptationTelemetry {
|
||||
std::size_t last_evidence_windows = 0;
|
||||
};
|
||||
|
||||
// The production policy is deliberately small. It does not try to infer a
|
||||
// workload phase from every operation; AdaptiveSequence consults these limits
|
||||
// only when its logical capacity grows or shrinks. The crossover value is a
|
||||
// calibratable constant and is updated from the focused benchmark rather than
|
||||
// from the retired benchmark matrix.
|
||||
struct ResizePolicyConfig {
|
||||
std::size_t minimum_tiered_size = 4 * 1024;
|
||||
std::size_t shrink_denominator = 8; // shrink at 12.5% occupancy
|
||||
};
|
||||
|
||||
class ResizePolicy {
|
||||
public:
|
||||
explicit ResizePolicy(ResizePolicyConfig config = {}, TieredConfig tiered = {}) noexcept
|
||||
: config_(normalize(config)), active_tiered_(normalize_tiered(tiered)) {}
|
||||
|
||||
void set_tiered_config(TieredConfig tiered) noexcept {
|
||||
active_tiered_ = normalize_tiered(tiered);
|
||||
}
|
||||
|
||||
// Kept as no-op compatibility hooks so a custom CostModelPolicy can still
|
||||
// be substituted for experiments without putting policy work on the
|
||||
// default read/edit paths.
|
||||
void observe(const OperationSample&) noexcept {}
|
||||
[[nodiscard]] bool decision_ready() const noexcept { return false; }
|
||||
[[nodiscard]] std::optional<AdaptationDecision>
|
||||
recommended_decision(StorageMode, std::size_t, TieredConfig) noexcept {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void on_transition(StorageMode from,
|
||||
StorageMode to,
|
||||
TieredConfig active_config) noexcept {
|
||||
const auto normalized = normalize_tiered(active_config);
|
||||
if (from == StorageMode::vector && to == StorageMode::tiered) {
|
||||
++telemetry_.vector_to_tiered;
|
||||
} else if (from == StorageMode::tiered && to == StorageMode::vector) {
|
||||
++telemetry_.tiered_to_vector;
|
||||
} else if (from == StorageMode::tiered && to == StorageMode::tiered) {
|
||||
++telemetry_.tiered_rebuilds;
|
||||
if (normalized.leaf_capacity == active_tiered_.leaf_capacity) {
|
||||
++telemetry_.tiered_directory_rebuilds;
|
||||
} else {
|
||||
++telemetry_.tiered_leaf_rebuilds;
|
||||
}
|
||||
}
|
||||
active_tiered_ = normalized;
|
||||
}
|
||||
|
||||
void on_transition(StorageMode from, StorageMode to) noexcept {
|
||||
on_transition(from, to, active_tiered_);
|
||||
}
|
||||
|
||||
void reset() noexcept { telemetry_ = {}; }
|
||||
|
||||
[[nodiscard]] const ResizePolicyConfig& config() const noexcept { return config_; }
|
||||
[[nodiscard]] const AdaptationTelemetry& telemetry() const noexcept {
|
||||
return telemetry_;
|
||||
}
|
||||
|
||||
private:
|
||||
static ResizePolicyConfig normalize(ResizePolicyConfig config) noexcept {
|
||||
config.minimum_tiered_size = std::max<std::size_t>(1,
|
||||
config.minimum_tiered_size);
|
||||
config.shrink_denominator = std::max<std::size_t>(2,
|
||||
config.shrink_denominator);
|
||||
return config;
|
||||
}
|
||||
|
||||
static TieredConfig normalize_tiered(TieredConfig config) noexcept {
|
||||
config.leaf_capacity = std::clamp<std::size_t>(
|
||||
config.leaf_capacity, 4, 1U << 20U);
|
||||
config.directory_fanout = std::clamp<std::size_t>(
|
||||
config.directory_fanout, 2, 1U << 16U);
|
||||
config.directory_levels = std::clamp<std::size_t>(
|
||||
config.directory_levels, 1, 8);
|
||||
return config;
|
||||
}
|
||||
|
||||
ResizePolicyConfig config_{};
|
||||
TieredConfig active_tiered_{};
|
||||
AdaptationTelemetry telemetry_{};
|
||||
};
|
||||
|
||||
class CostModelPolicy {
|
||||
public:
|
||||
static constexpr std::size_t candidate_count = 5;
|
||||
|
||||
Reference in New Issue
Block a user