use std::collections::{BTreeMap, HashMap};
#[derive(Debug, Clone)]
pub struct FunctionInfo {
pub id: String,
pub name: String,
pub size: usize,
pub call_count: u64,
pub complexity: f64,
pub is_recursive: bool,
pub inline_depth: usize,
pub has_side_effects: bool,
}
#[derive(Debug, Clone)]
pub struct InlineDecision {
pub should_inline: bool,
pub estimated_benefit: f64,
pub estimated_cost: usize,
pub reason: String,
pub optimization_level: InlineOptLevel,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InlineOptLevel {
None,
Minimal,
Standard,
Aggressive,
}
#[derive(Debug, Clone, Default)]
pub struct InlineStats {
pub total_decisions: u64,
pub inlined_count: u64,
pub rejected_count: u64,
pub total_size_increase: usize,
pub estimated_speedup: f64,
}
#[derive(Debug, Clone)]
pub struct InlineResult {
pub function_id: String,
pub was_inlined: bool,
pub actual_size_increase: usize,
pub measured_speedup: Option<f64>,
pub timestamp: u64, }
pub struct InlineStrategy {
max_inline_depth: usize,
max_code_size: usize,
max_expansion_ratio: f64,
min_call_threshold: u64,
inline_history: HashMap<String, Vec<InlineResult>>,
stats: InlineStats,
config: InlineConfig,
current_system_load: f64,
hot_path_functions: HashMap<String, f64>, cache_locality_scores: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct InlineConfig {
pub base_size_threshold: usize,
pub call_frequency_weight: f64,
pub complexity_penalty: f64,
pub recursion_penalty: f64,
pub side_effect_penalty: f64,
pub cache_locality_weight: f64,
pub branch_prediction_weight: f64,
pub system_load_threshold: f64,
}
impl Default for InlineConfig {
fn default() -> Self {
Self {
base_size_threshold: 100,
call_frequency_weight: 0.3,
complexity_penalty: 0.2,
recursion_penalty: 0.5,
side_effect_penalty: 0.1,
cache_locality_weight: 0.25,
branch_prediction_weight: 0.30,
system_load_threshold: 100.0,
}
}
}
impl InlineStrategy {
pub fn new() -> Self {
Self::with_config(InlineConfig::default())
}
pub fn with_config(config: InlineConfig) -> Self {
Self {
max_inline_depth: 10,
max_code_size: 500,
max_expansion_ratio: 2.0,
min_call_threshold: 3,
inline_history: HashMap::new(),
stats: InlineStats::default(),
config,
current_system_load: 100.0, hot_path_functions: HashMap::new(),
cache_locality_scores: HashMap::new(),
}
}
pub fn should_inline(&mut self, callee: &FunctionInfo) -> InlineDecision {
self.stats.total_decisions += 1;
if let Some(rejection) = self.quick_rejection_check(callee) {
self.stats.rejected_count += 1;
return rejection;
}
let benefit: _ = self.estimate_benefit(callee);
let cost: _ = self.estimate_cost(callee);
let benefit_ratio: _ = if cost > 0 {
benefit / cost as f64
} else {
benefit
};
let should_inline: _ = benefit_ratio > 0.3;
let opt_level: _ = self.determine_opt_level(benefit_ratio);
if should_inline {
self.stats.inlined_count += 1;
self.stats.total_size_increase += cost;
self.stats.estimated_speedup += benefit * 0.01; } else {
self.stats.rejected_count += 1;
}
InlineDecision {
should_inline,
estimated_benefit: benefit,
estimated_cost: cost,
reason: self.generate_reason(callee, benefit, cost, should_inline),
optimization_level: opt_level,
}
}
fn quick_rejection_check(&self, callee: &FunctionInfo) -> Option<InlineDecision> {
if callee.size > self.max_code_size {
return Some(InlineDecision {
should_inline: false,
estimated_benefit: 0.0,
estimated_cost: callee.size,
reason: format!("Function too large: {} > {}", callee.size, self.max_code_size),
optimization_level: InlineOptLevel::None,
});
}
if callee.inline_depth >= self.max_inline_depth {
return Some(InlineDecision {
should_inline: false,
estimated_benefit: 0.0,
estimated_cost: callee.size,
reason: format!("Inline depth limit: {} >= {}", callee.inline_depth, self.max_inline_depth),
optimization_level: InlineOptLevel::None,
});
}
if callee.call_count < self.min_call_threshold {
return Some(InlineDecision {
should_inline: false,
estimated_benefit: 0.0,
estimated_cost: callee.size,
reason: format!("Insufficient calls: {} < {}", callee.call_count, self.min_call_threshold),
optimization_level: InlineOptLevel::None,
});
}
None
}
pub fn estimate_benefit(&mut self, callee: &FunctionInfo) -> f64 {
let mut benefit = 50.0; let call_bonus: _ = (callee.call_count as f64).log2() * 10.0 * self.config.call_frequency_weight;
benefit += call_bonus.min(30.0);
let size_bonus: _ = (1.0 - (callee.size as f64 / self.max_code_size as f64)) * 20.0;
benefit += size_bonus.max(0.0);
let complexity_penalty: _ = callee.complexity * self.config.complexity_penalty;
benefit -= complexity_penalty;
if callee.is_recursive {
benefit *= 1.0 - self.config.recursion_penalty;
}
if callee.has_side_effects {
benefit *= 1.0 - self.config.side_effect_penalty;
}
let cache_locality: _ = self.get_cache_locality_score(callee);
let cache_locality_bonus: _ = cache_locality * self.config.cache_locality_weight * 15.0;
benefit += cache_locality_bonus;
let branch_prediction_cost: _ = if callee.has_side_effects {
callee.complexity * 0.4 * self.config.branch_prediction_weight
} else {
0.0
};
benefit -= branch_prediction_cost;
let hotness_score: _ = self.hot_path_functions.get(&callee.id).unwrap_or(&0.0);
if *hotness_score > 0.7 {
benefit *= 1.5;
} else if *hotness_score > 0.4 {
benefit *= 1.2;
} else if *hotness_score < 0.2 {
benefit *= 0.9;
}
let load_adjustment: _ = self.calculate_load_adjustment();
benefit *= load_adjustment;
if let Some(history) = self.inline_history.get(&callee.id) {
let avg_speedup: f64 = history
.iter()
.filter_map(|r| r.measured_speedup)
.sum::<f64>()
/ history.len().max(1) as f64;
if avg_speedup > 0.0 {
benefit *= 1.0 + avg_speedup;
}
}
benefit.clamp(0.0, 150.0) }
fn estimate_cost(&self, callee: &FunctionInfo) -> usize {
let base_cost: _ = callee.size;
let expansion: _ = callee.call_count.min(10) as usize;
let total_cost: _ = base_cost * expansion;
total_cost.min(self.max_code_size * 10)
}
fn determine_opt_level(&self, benefit_ratio: f64) -> InlineOptLevel {
if benefit_ratio < 0.3 {
InlineOptLevel::None
} else if benefit_ratio < 0.6 {
InlineOptLevel::Minimal
} else if benefit_ratio < 1.0 {
InlineOptLevel::Standard
} else {
InlineOptLevel::Aggressive
}
}
fn generate_reason(&self, callee: &FunctionInfo, benefit: f64, cost: usize, inline: bool) -> String {
if inline {
format!(
"Inline '{}': benefit={:.1}, cost={}, ratio={:.2}",
callee.name,
benefit,
cost,
benefit / cost.max(1) as f64
)
} else {
format!(
"Skip inline '{}': benefit={:.1} too low for cost={}",
callee.name, benefit, cost
)
}
}
pub fn record_inline(&mut self, result: InlineResult) {
let function_id: _ = result.function_id.clone();
self.inline_history
.entry(function_id.clone())
.or_insert_with(Vec::new)
.push(result);
if let Some(history) = self.inline_history.get_mut(&function_id) {
if history.len() > 100 {
history.remove(0);
}
}
}
pub fn get_stats(&self) -> &InlineStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = InlineStats::default();
}
pub fn get_inline_rate(&self) -> f64 {
if self.stats.total_decisions == 0 {
0.0
} else {
self.stats.inlined_count as f64 / self.stats.total_decisions as f64
}
}
fn get_cache_locality_score(&self, callee: &FunctionInfo) -> f64 {
let size_score: _ = (1.0 / (callee.size as f64 / 10.0 + 1.0)).min(1.0);
let call_score: _ = (callee.call_count as f64 / 100.0).min(1.0);
(size_score + call_score) / 2.0
}
pub fn calculate_load_adjustment(&self) -> f64 {
match self.current_system_load {
x if x < 75.0 => 1.2, x if x < 150.0 => 1.0, _ => 0.8, }
}
pub fn update_system_load(&mut self, load: f64) {
self.current_system_load = load.max(0.0).min(300.0); }
pub fn mark_hot_path(&mut self, function_id: String, hotness_score: f64) {
let clamped_score: _ = hotness_score.clamp(0.0, 1.0);
self.hot_path_functions.insert(function_id, clamped_score);
}
pub fn get_function_hotness(&self, function_id: &str) -> f64 {
self.hot_path_functions.get(function_id).unwrap_or(&0.0).clone()
}
pub fn predict_performance_impact(&mut self, callee: &FunctionInfo) -> f64 {
let benefit: _ = self.estimate_benefit(callee);
let cost: _ = self.estimate_cost(callee) as f64;
let call_savings: _ = (callee.call_count as f64 * 0.001).min(0.5);
let size_penalty: _ = (callee.size as f64 * 0.0001).max(0.0);
let complexity_factor: _ = (100.0 - callee.complexity) / 100.0;
let predicted_speedup: _ = (call_savings - size_penalty) * complexity_factor * (benefit / 100.0);
predicted_speedup.clamp(-1.0, 2.0) }
pub fn get_optimization_stats(&self) -> OptimizationStats {
let hot_functions_count: _ = self.hot_path_functions.len();
let avg_hotness: f64 = if hot_functions_count > 0 {
self.hot_path_functions.values().sum::<f64>() / hot_functions_count as f64
} else {
0.0
};
OptimizationStats {
total_decisions: self.stats.total_decisions,
inlined_count: self.stats.inlined_count,
hot_functions_count,
avg_hotness_score: avg_hotness,
current_system_load: self.current_system_load,
cache_locality_avg: self.calculate_avg_cache_locality(),
}
}
fn calculate_avg_cache_locality(&self) -> f64 {
if self.cache_locality_scores.is_empty() {
0.5 } else {
self.cache_locality_scores.values().sum::<f64>() / self.cache_locality_scores.len() as f64
}
}
pub fn adjust_config_for_system(&mut self, system_type: SystemProfile) {
match system_type {
SystemProfile::HighPerformance => {
self.config.cache_locality_weight = 0.35;
self.config.branch_prediction_weight = 0.40;
self.config.system_load_threshold = 120.0;
}
SystemProfile::Balanced => {
self.config.cache_locality_weight = 0.25;
self.config.branch_prediction_weight = 0.30;
self.config.system_load_threshold = 100.0;
}
SystemProfile::MemoryConstrained => {
self.config.cache_locality_weight = 0.40; self.config.branch_prediction_weight = 0.25;
self.config.system_load_threshold = 80.0;
}
}
}
}
impl Default for InlineStrategy {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct OptimizationStats {
pub total_decisions: u64,
pub inlined_count: u64,
pub hot_functions_count: usize,
pub avg_hotness_score: f64,
pub current_system_load: f64,
pub cache_locality_avg: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SystemProfile {
HighPerformance,
Balanced,
MemoryConstrained,
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Instant, SystemTime};
fn make_function(name: &str, size: usize, calls: u64) -> FunctionInfo {
FunctionInfo {
id: name.to_string(),
name: name.to_string(),
size,
call_count: calls,
complexity: 30.0,
is_recursive: false,
inline_depth: 0,
has_side_effects: false,
}
}
#[test]
fn test_should_inline_small_hot_function() {
let mut strategy = InlineStrategy::new();
let func: _ = make_function("small_hot", 20, 100);
let decision: _ = strategy.should_inline(&func);
assert!(decision.should_inline, "Decision: {:?}", decision);
assert!(decision.estimated_benefit > 40.0);
}
#[test]
fn test_reject_large_function() {
let mut strategy = InlineStrategy::new();
let func: _ = make_function("large", 1000, 100);
let decision: _ = strategy.should_inline(&func);
assert!(!decision.should_inline);
assert!(decision.reason.contains("too large"));
}
#[test]
fn test_reject_rarely_called() {
let mut strategy = InlineStrategy::new();
let func: _ = make_function("cold", 50, 1);
let decision: _ = strategy.should_inline(&func);
assert!(!decision.should_inline);
assert!(decision.reason.contains("Insufficient calls"));
}
#[test]
fn test_recursive_penalty() {
let mut strategy = InlineStrategy::new();
let normal: _ = make_function("normal", 50, 50);
let mut recursive = make_function("recursive", 50, 50);
recursive.is_recursive = true;
let normal_benefit: _ = strategy.estimate_benefit(&normal);
let recursive_benefit: _ = strategy.estimate_benefit(&recursive);
assert!(normal_benefit > recursive_benefit);
}
#[test]
fn test_inline_rate() {
let mut strategy = InlineStrategy::new();
for i in 0..10 {
let func: _ = make_function(&format!("func_{}", i), 10 + i * 5, 100);
strategy.should_inline(&func);
}
let rate: _ = strategy.get_inline_rate();
assert!(rate >= 0.0, "Rate should be >= 0");
assert!(rate <= 1.0, "Rate should be <= 1");
}
#[test]
fn test_record_and_learn() {
let mut strategy = InlineStrategy::new();
strategy.record_inline(InlineResult {
function_id: "func_a".to_string(),
was_inlined: true,
actual_size_increase: 100,
measured_speedup: Some(0.5), timestamp: std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
});
let func: _ = make_function("func_a", 50, 50);
let benefit: _ = strategy.estimate_benefit(&func);
strategy.inline_history.clear();
let benefit_no_history: _ = strategy.estimate_benefit(&func);
assert!(benefit > benefit_no_history);
}
}