use super::{
lock_recovered, OptimizationConstraints, OptimizationEvent, ResourceAllocation,
ResourceOptimizationStrategy, ResourceOptimizer, ResourceUsage, StabilityRequirements,
};
use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
const DEFAULT_CHANGE_RATE_LIMIT: f64 = 0.25;
impl ResourceOptimizer {
pub(crate) fn new(strategy: ResourceOptimizationStrategy) -> Self {
let (min_stable_period, max_change_frequency, change_rate_limit, hysteresis) =
match strategy {
ResourceOptimizationStrategy::Conservative => {
(Duration::from_secs(300), 0.02, 0.05, 0.05)
}
ResourceOptimizationStrategy::Aggressive
| ResourceOptimizationStrategy::ThroughputOptimized => {
(Duration::from_secs(15), 0.5, 0.5, 0.01)
}
ResourceOptimizationStrategy::LatencyOptimized => {
(Duration::from_secs(10), 0.5, 0.35, 0.01)
}
ResourceOptimizationStrategy::PowerEfficient => {
(Duration::from_secs(120), 0.05, 0.1, 0.05)
}
ResourceOptimizationStrategy::Balanced => (
Duration::from_secs(60),
0.1,
DEFAULT_CHANGE_RATE_LIMIT,
0.02,
),
};
let mut change_rate_limits = HashMap::new();
change_rate_limits.insert("memory".to_string(), change_rate_limit);
change_rate_limits.insert("cpu".to_string(), change_rate_limit);
change_rate_limits.insert("network".to_string(), change_rate_limit);
Self {
strategy,
optimization_history: VecDeque::with_capacity(100),
performance_impact: HashMap::new(),
constraints: OptimizationConstraints {
min_guarantees: HashMap::new(),
max_limits: HashMap::new(),
change_rate_limits,
stability_requirements: StabilityRequirements {
min_stable_period,
max_change_frequency,
prevent_oscillation: true,
hysteresis_factor: hysteresis,
},
},
last_change: HashMap::new(),
pending_change: HashMap::new(),
}
}
pub(crate) fn clamp_change(&mut self, resource: &str, requested: f64) -> f64 {
let limit = self
.constraints
.change_rate_limits
.get(resource)
.copied()
.unwrap_or(DEFAULT_CHANGE_RATE_LIMIT)
.abs();
let clamped = requested.clamp(-limit, limit);
self.pending_change.insert(resource.to_string(), clamped);
clamped
}
pub(crate) fn accept_change(&self, component: &str) -> bool {
let requirements = &self.constraints.stability_requirements;
let resource = component.split('_').next().unwrap_or(component);
let magnitude = self.pending_change.get(resource).copied().unwrap_or(0.0);
if magnitude.abs() < requirements.hysteresis_factor {
return false;
}
match self.last_change.get(component) {
None => true,
Some((at, previous_magnitude)) => {
let elapsed = at.elapsed();
if elapsed < requirements.min_stable_period {
if requirements.prevent_oscillation && magnitude * previous_magnitude < 0.0 {
return false;
}
let allowed_interval = if requirements.max_change_frequency > 0.0 {
Duration::from_secs_f64(1.0 / requirements.max_change_frequency)
} else {
requirements.min_stable_period
};
return elapsed >= allowed_interval;
}
true
}
}
}
pub(crate) fn record_applied_change(&mut self, component: &str) {
let resource = component.split('_').next().unwrap_or(component).to_string();
let magnitude = self.pending_change.get(&resource).copied().unwrap_or(0.0);
self.last_change
.insert(component.to_string(), (Instant::now(), magnitude));
let impact = self
.performance_impact
.entry(component.to_string())
.or_insert(0.0);
*impact += magnitude.abs();
if let Some(event) = self.optimization_history.back_mut() {
if event.affected_resources.contains(&resource) {
event.success = true;
}
}
}
pub(crate) fn performance_impact(&self) -> &HashMap<String, f64> {
&self.performance_impact
}
pub(crate) fn check_optimization_opportunities(
&mut self,
current_usage: &ResourceUsage,
allocations: &Arc<Mutex<HashMap<String, ResourceAllocation>>>,
) -> Result<(), String> {
match self.strategy {
ResourceOptimizationStrategy::Balanced
| ResourceOptimizationStrategy::Aggressive
| ResourceOptimizationStrategy::Conservative => {
self.check_balanced_optimization(current_usage, allocations)?;
}
ResourceOptimizationStrategy::PowerEfficient => {
self.check_power_optimization(current_usage, allocations)?;
}
ResourceOptimizationStrategy::LatencyOptimized
| ResourceOptimizationStrategy::ThroughputOptimized => {
self.check_latency_optimization(current_usage, allocations)?;
}
}
Ok(())
}
fn record_event(
&mut self,
optimization_type: &str,
affected: Vec<String>,
deltas: HashMap<String, f64>,
expected_impact: f64,
) {
let event = OptimizationEvent {
timestamp: Instant::now(),
optimization_type: optimization_type.to_string(),
affected_resources: affected,
resource_deltas: deltas,
performance_impact: expected_impact,
success: false, };
if self.optimization_history.len() >= 100 {
self.optimization_history.pop_front();
}
self.optimization_history.push_back(event);
}
fn check_balanced_optimization(
&mut self,
current_usage: &ResourceUsage,
_allocations: &Arc<Mutex<HashMap<String, ResourceAllocation>>>,
) -> Result<(), String> {
let Some(memory_utilization) = current_usage.memory_usage_percent() else {
return Ok(());
};
let Some(cpu_utilization) = current_usage.cpu_usage() else {
return Ok(());
};
if (memory_utilization > 80.0 && cpu_utilization < 40.0)
|| (cpu_utilization > 80.0 && memory_utilization < 40.0)
{
let mut deltas = HashMap::new();
deltas.insert("memory".to_string(), memory_utilization - cpu_utilization);
deltas.insert("cpu".to_string(), cpu_utilization - memory_utilization);
let imbalance = (memory_utilization - cpu_utilization).abs() / 100.0;
self.record_event(
"resource_rebalancing",
vec!["memory".to_string(), "cpu".to_string()],
deltas,
imbalance,
);
}
Ok(())
}
fn check_power_optimization(
&mut self,
current_usage: &ResourceUsage,
allocations: &Arc<Mutex<HashMap<String, ResourceAllocation>>>,
) -> Result<(), String> {
let Some(cpu) = current_usage.cpu_usage() else {
return Ok(());
};
if cpu > 25.0 {
return Ok(());
}
let stale_period = self.constraints.stability_requirements.min_stable_period;
let idle: Vec<String> = {
let guard = lock_recovered(allocations);
guard
.values()
.filter(|allocation| allocation.last_access.elapsed() > stale_period)
.map(|allocation| allocation.component_name.clone())
.collect()
};
if idle.is_empty() {
return Ok(());
}
let mut deltas = HashMap::new();
for component in &idle {
deltas.insert(component.clone(), -1.0);
}
let expected = (idle.len() as f64 / 10.0).min(1.0);
self.record_event("idle_reclaim", idle, deltas, expected);
Ok(())
}
fn check_latency_optimization(
&mut self,
current_usage: &ResourceUsage,
allocations: &Arc<Mutex<HashMap<String, ResourceAllocation>>>,
) -> Result<(), String> {
let Some(cpu) = current_usage.cpu_usage() else {
return Ok(());
};
if cpu < 75.0 {
return Ok(());
}
let sheddable: Vec<String> = {
let guard = lock_recovered(allocations);
guard
.values()
.filter(|allocation| allocation.priority >= super::ResourcePriority::Low)
.map(|allocation| allocation.component_name.clone())
.collect()
};
if sheddable.is_empty() {
return Ok(());
}
let mut deltas = HashMap::new();
for component in &sheddable {
deltas.insert(component.clone(), -(cpu - 75.0) / 100.0);
}
self.record_event(
"latency_load_shedding",
sheddable,
deltas,
(cpu - 75.0) / 100.0,
);
Ok(())
}
}