#![allow(clippy::cast_precision_loss)]
use serde::{Deserialize, Serialize};
use tracing::debug;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct OperationCostFactors {
#[serde(default = "default_seq_page_cost")]
pub seq_page_cost: f64,
#[serde(default = "default_random_page_cost")]
pub random_page_cost: f64,
#[serde(default = "default_cpu_tuple_cost")]
pub cpu_tuple_cost: f64,
#[serde(default = "default_cpu_index_cost")]
pub cpu_index_cost: f64,
#[serde(default = "default_cpu_distance_cost")]
pub cpu_distance_cost: f64,
#[serde(default = "default_cpu_edge_cost")]
pub cpu_edge_cost: f64,
}
fn default_seq_page_cost() -> f64 {
1.0
}
fn default_random_page_cost() -> f64 {
4.0
}
fn default_cpu_tuple_cost() -> f64 {
0.01
}
fn default_cpu_index_cost() -> f64 {
0.005
}
fn default_cpu_distance_cost() -> f64 {
0.1
}
fn default_cpu_edge_cost() -> f64 {
0.02
}
impl Default for OperationCostFactors {
fn default() -> Self {
Self {
seq_page_cost: default_seq_page_cost(),
random_page_cost: default_random_page_cost(),
cpu_tuple_cost: default_cpu_tuple_cost(),
cpu_index_cost: default_cpu_index_cost(),
cpu_distance_cost: default_cpu_distance_cost(),
cpu_edge_cost: default_cpu_edge_cost(),
}
}
}
pub(crate) struct CostFactorBounds;
impl CostFactorBounds {
pub const SEQ_PAGE_COST: (f64, f64) = (0.01, 10.0);
pub const RANDOM_PAGE_COST: (f64, f64) = (0.1, 20.0);
pub const CPU_TUPLE_COST: (f64, f64) = (0.001, 0.1);
pub const CPU_INDEX_COST: (f64, f64) = (0.001, 0.05);
pub const CPU_DISTANCE_COST: (f64, f64) = (0.01, 1.0);
pub const CPU_EDGE_COST: (f64, f64) = (0.005, 0.2);
}
pub(crate) fn clamp_with_log(name: &str, value: f64, bounds: (f64, f64)) -> f64 {
let clamped = value.clamp(bounds.0, bounds.1);
if (clamped - value).abs() > f64::EPSILON {
debug!(
field = name,
original = value,
clamped = clamped,
"cost factor clamped to bounds"
);
}
clamped
}
impl OperationCostFactors {
#[must_use]
pub fn ssd_optimized() -> Self {
Self {
random_page_cost: 1.5,
..Default::default()
}
}
#[must_use]
pub fn in_memory() -> Self {
Self {
seq_page_cost: 0.1,
random_page_cost: 0.1,
..Default::default()
}
}
#[must_use]
pub fn hdd_optimized() -> Self {
Self {
random_page_cost: 8.0,
..Default::default()
}
}
#[must_use]
pub fn clamped(self) -> Self {
Self {
seq_page_cost: clamp_with_log(
"seq_page_cost",
self.seq_page_cost,
CostFactorBounds::SEQ_PAGE_COST,
),
random_page_cost: clamp_with_log(
"random_page_cost",
self.random_page_cost,
CostFactorBounds::RANDOM_PAGE_COST,
),
cpu_tuple_cost: clamp_with_log(
"cpu_tuple_cost",
self.cpu_tuple_cost,
CostFactorBounds::CPU_TUPLE_COST,
),
cpu_index_cost: clamp_with_log(
"cpu_index_cost",
self.cpu_index_cost,
CostFactorBounds::CPU_INDEX_COST,
),
cpu_distance_cost: clamp_with_log(
"cpu_distance_cost",
self.cpu_distance_cost,
CostFactorBounds::CPU_DISTANCE_COST,
),
cpu_edge_cost: clamp_with_log(
"cpu_edge_cost",
self.cpu_edge_cost,
CostFactorBounds::CPU_EDGE_COST,
),
}
}
#[must_use]
pub fn is_default(&self) -> bool {
*self == Self::default()
}
}
#[cfg(test)]
#[path = "cost_factors_tests.rs"]
mod tests;