#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CostModelConfig {
pub seq_page_cost: f64,
pub random_page_cost: f64,
pub cpu_tuple_cost: f64,
pub cpu_index_tuple_cost: f64,
pub cpu_operator_cost: f64,
pub hash_build_overhead: f64,
pub sort_comparison_cost: f64,
pub memory_sort_threshold: u64,
pub external_sort_page_cost: f64,
pub edge_traversal_cost: f64,
pub multi_hop_penalty: f64,
pub neighbor_lookup_cost: f64,
pub effective_cache_pages: u64,
pub cache_hit_cost_factor: f64,
pub shortest_path_base_cost: f64,
pub path_enumeration_factor: f64,
pub super_node_threshold: u64,
pub super_node_penalty: f64,
pub default_unwind_list_size: f64,
pub default_loop_iterations: u32,
pub default_select_branches: usize,
pub memory_byte_cost: f64,
pub memory_pressure_threshold: usize,
pub memory_pressure_penalty: f64,
pub simple_expression_cost: f64,
pub function_call_base_cost: f64,
pub expression_nesting_cost: f64,
pub fixed_type_cost_factor: f64,
pub variable_type_cost_factor: f64,
pub complex_type_cost_factor: f64,
pub graph_type_cost_factor: f64,
pub row_width_bytes: usize,
pub strategy_thresholds: StrategyThresholds,
}
pub const UNKNOWN_SCAN_ROWS: u64 = 1_000;
pub const DEFAULT_FANOUT: u64 = 10;
pub const DEFAULT_DEGREE_FALLBACK: f64 = 2.0;
pub const ROWS_PER_PAGE: u64 = 100;
pub const SUPER_NODE_THRESHOLD: u64 = 10_000;
pub const TOPN_RATIO: u64 = 10;
pub const TOPN_MIN_ROWS: u64 = 10_000;
pub const GET_VERTICES_DEFAULT_ROWS: u64 = 1;
pub const GET_EDGES_DEFAULT_ROWS: u64 = 10;
pub const DEDUP_SELECTIVITY: f64 = 0.8;
pub const SKEW_SEVERE_GINI: f64 = 0.7;
pub const SKEW_MODERATE_GINI: f64 = 0.5;
pub const SKEW_MILD_GINI: f64 = 0.3;
pub const SKEW_MAX_DEGREE_RATIO: f64 = 10.0;
pub const SKEW_DIRECTION_RATIO: f64 = 5.0;
pub const SKEW_SEVERE_PENALTY: f64 = 2.0;
pub const SKEW_MODERATE_PENALTY: f64 = 1.5;
pub const SKEW_MILD_PENALTY: f64 = 1.2;
pub const SKEW_SEVERE_ROW_FACTOR: f64 = 1.5;
pub const SKEW_MODERATE_ROW_FACTOR: f64 = 1.3;
pub const SKEW_MILD_ROW_FACTOR: f64 = 1.1;
pub const ESTIMATED_ROW_WIDTH_BYTES: usize = std::mem::size_of::<linkrs_core::Value>();
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StrategyThresholds {
pub small_dataset_threshold: u64,
pub low_cardinality_threshold: u64,
pub high_cardinality_ratio: f64,
pub bidirectional_savings_threshold: f64,
pub default_branching_factor: f64,
pub topn_threshold: f64,
pub topn_default_limit: u64,
pub join_reorder_min_improvement: f64,
pub max_result_rows: u64,
pub min_reference_count: usize,
pub min_complexity_score: f64,
}
impl Default for StrategyThresholds {
fn default() -> Self {
Self {
small_dataset_threshold: 1000,
low_cardinality_threshold: 100,
high_cardinality_ratio: 0.1,
bidirectional_savings_threshold: 0.3,
default_branching_factor: DEFAULT_DEGREE_FALLBACK,
topn_threshold: 1.0 / TOPN_RATIO as f64,
topn_default_limit: 100,
join_reorder_min_improvement: 0.0,
max_result_rows: 10000,
min_reference_count: 2,
min_complexity_score: 5.0,
}
}
}
impl Default for CostModelConfig {
fn default() -> Self {
Self {
seq_page_cost: 1.0,
random_page_cost: 4.0,
cpu_tuple_cost: 0.01,
cpu_index_tuple_cost: 0.005,
cpu_operator_cost: 0.0025,
hash_build_overhead: 0.1,
sort_comparison_cost: 1.0,
memory_sort_threshold: TOPN_MIN_ROWS,
external_sort_page_cost: 2.0,
edge_traversal_cost: 0.02,
multi_hop_penalty: 1.2,
neighbor_lookup_cost: 0.015,
effective_cache_pages: 10000,
cache_hit_cost_factor: 0.1,
shortest_path_base_cost: 10.0,
path_enumeration_factor: 2.0,
super_node_threshold: SUPER_NODE_THRESHOLD,
super_node_penalty: 2.0,
default_unwind_list_size: 3.0,
default_loop_iterations: 3,
default_select_branches: 2,
memory_byte_cost: 0.0001,
memory_pressure_threshold: 100 * 1024 * 1024, memory_pressure_penalty: 2.0,
simple_expression_cost: 0.001,
function_call_base_cost: 0.01,
expression_nesting_cost: 0.005,
fixed_type_cost_factor: 1.0,
variable_type_cost_factor: 1.5,
complex_type_cost_factor: 2.0,
graph_type_cost_factor: 3.0,
row_width_bytes: ESTIMATED_ROW_WIDTH_BYTES,
strategy_thresholds: StrategyThresholds::default(),
}
}
}
impl CostModelConfig {
pub fn new() -> Self {
Self::default()
}
pub fn for_ssd() -> Self {
Self {
random_page_cost: 1.1, ..Default::default()
}
}
pub fn for_in_memory() -> Self {
Self {
seq_page_cost: 0.1,
random_page_cost: 0.1,
cache_hit_cost_factor: 0.01, ..Default::default()
}
}
pub fn for_hdd() -> Self {
Self {
seq_page_cost: 1.0,
random_page_cost: 4.0,
..Default::default()
}
}
pub fn with_seq_page_cost(mut self, cost: f64) -> Self {
self.seq_page_cost = cost;
self
}
pub fn with_random_page_cost(mut self, cost: f64) -> Self {
self.random_page_cost = cost;
self
}
pub fn with_cpu_tuple_cost(mut self, cost: f64) -> Self {
self.cpu_tuple_cost = cost;
self
}
pub fn with_cpu_index_tuple_cost(mut self, cost: f64) -> Self {
self.cpu_index_tuple_cost = cost;
self
}
pub fn with_cpu_operator_cost(mut self, cost: f64) -> Self {
self.cpu_operator_cost = cost;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = CostModelConfig::default();
assert_eq!(config.seq_page_cost, 1.0);
assert_eq!(config.random_page_cost, 4.0);
assert_eq!(config.cpu_tuple_cost, 0.01);
assert_eq!(config.cpu_index_tuple_cost, 0.005);
assert_eq!(config.cpu_operator_cost, 0.0025);
assert_eq!(config.hash_build_overhead, 0.1);
assert_eq!(config.sort_comparison_cost, 1.0);
assert_eq!(config.memory_sort_threshold, 10000);
assert_eq!(config.external_sort_page_cost, 2.0);
assert_eq!(config.edge_traversal_cost, 0.02);
assert_eq!(config.multi_hop_penalty, 1.2);
assert_eq!(config.neighbor_lookup_cost, 0.015);
assert_eq!(config.effective_cache_pages, 10000);
assert_eq!(config.cache_hit_cost_factor, 0.1);
assert_eq!(config.shortest_path_base_cost, 10.0);
assert_eq!(config.path_enumeration_factor, 2.0);
assert_eq!(config.super_node_threshold, 10000);
assert_eq!(config.super_node_penalty, 2.0);
assert_eq!(config.default_unwind_list_size, 3.0);
assert_eq!(config.default_loop_iterations, 3);
assert_eq!(config.default_select_branches, 2);
}
#[test]
fn test_ssd_config() {
let config = CostModelConfig::for_ssd();
assert_eq!(config.random_page_cost, 1.1);
assert_eq!(config.seq_page_cost, 1.0); }
#[test]
fn test_in_memory_config() {
let config = CostModelConfig::for_in_memory();
assert_eq!(config.seq_page_cost, 0.1);
assert_eq!(config.random_page_cost, 0.1);
}
#[test]
fn test_builder_pattern() {
let config = CostModelConfig::new()
.with_seq_page_cost(0.5)
.with_random_page_cost(2.0)
.with_cpu_tuple_cost(0.02);
assert_eq!(config.seq_page_cost, 0.5);
assert_eq!(config.random_page_cost, 2.0);
assert_eq!(config.cpu_tuple_cost, 0.02);
assert_eq!(config.cpu_index_tuple_cost, 0.005); }
#[test]
fn test_join_reorder_min_improvement_defaults_to_zero() {
let config = CostModelConfig::default();
assert_eq!(config.strategy_thresholds.join_reorder_min_improvement, 0.0);
}
}