use crate::models::traits::{FairnessModel, Sorter};
use std::time::Duration;
use std::collections::HashMap;
#[derive(Debug)]
pub struct WallTimeFairness {
slice_ms: u64, algorithm_timings: HashMap<String, Duration>, algorithm_speeds: HashMap<String, f64>, }
impl WallTimeFairness {
pub fn new(slice_ms: u64) -> Self {
Self {
slice_ms: slice_ms.max(1),
algorithm_timings: HashMap::new(),
algorithm_speeds: HashMap::new(),
}
}
pub fn update_timing(&mut self, algorithm_name: &str, duration: Duration, operations_performed: usize) {
self.algorithm_timings.insert(algorithm_name.to_string(), duration);
if duration.as_millis() > 0 {
let speed = operations_performed as f64 / duration.as_millis() as f64;
self.algorithm_speeds.insert(algorithm_name.to_string(), speed);
}
}
fn estimate_operations_for_time_slice(&self, algorithm_name: &str) -> usize {
let default_speed = 10.0; let speed = self.algorithm_speeds.get(algorithm_name).copied().unwrap_or(default_speed);
((speed * self.slice_ms as f64).round() as usize).max(1)
}
pub fn get_slice_ms(&self) -> u64 {
self.slice_ms
}
pub fn clear_history(&mut self) {
self.algorithm_timings.clear();
self.algorithm_speeds.clear();
}
}
impl Default for WallTimeFairness {
fn default() -> Self {
Self::new(50) }
}
impl FairnessModel for WallTimeFairness {
fn allocate_budget(&self, algorithms: &[Box<dyn Sorter>]) -> Vec<usize> {
algorithms
.iter()
.map(|algorithm| {
if algorithm.is_complete() {
0 } else {
self.estimate_operations_for_time_slice(algorithm.name())
}
})
.collect()
}
fn name(&self) -> &str {
"Wall Time Fairness"
}
}
#[derive(Debug)]
pub struct AdaptiveWallTimeFairness {
base_fairness: WallTimeFairness,
performance_multipliers: HashMap<String, f64>,
adaptation_rate: f64,
}
impl AdaptiveWallTimeFairness {
pub fn new(slice_ms: u64, adaptation_rate: f64) -> Self {
Self {
base_fairness: WallTimeFairness::new(slice_ms),
performance_multipliers: HashMap::new(),
adaptation_rate: adaptation_rate.clamp(0.01, 1.0),
}
}
pub fn update_performance(&mut self, algorithm_name: &str, efficiency: f64) {
let multiplier = self.performance_multipliers
.entry(algorithm_name.to_string())
.or_insert(1.0);
*multiplier = *multiplier * (1.0 - self.adaptation_rate) + efficiency * self.adaptation_rate;
*multiplier = multiplier.clamp(0.1, 3.0);
}
fn get_performance_multiplier(&self, algorithm_name: &str) -> f64 {
self.performance_multipliers.get(algorithm_name).copied().unwrap_or(1.0)
}
pub fn clear_performance_history(&mut self) {
self.performance_multipliers.clear();
}
pub fn update_timing(&mut self, algorithm_name: &str, duration: Duration, operations_performed: usize) {
self.base_fairness.update_timing(algorithm_name, duration, operations_performed);
}
}
impl Default for AdaptiveWallTimeFairness {
fn default() -> Self {
Self::new(50, 0.1) }
}
impl FairnessModel for AdaptiveWallTimeFairness {
fn allocate_budget(&self, algorithms: &[Box<dyn Sorter>]) -> Vec<usize> {
let base_budgets = self.base_fairness.allocate_budget(algorithms);
base_budgets
.into_iter()
.zip(algorithms.iter())
.map(|(budget, algorithm)| {
if budget == 0 {
0 } else {
let multiplier = self.get_performance_multiplier(algorithm.name());
((budget as f64) * multiplier).round() as usize
}
})
.collect()
}
fn name(&self) -> &str {
"Adaptive Wall Time"
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::services::sorters::bubble::BubbleSort;
use std::time::Duration;
#[test]
fn test_walltime_fairness_timing() {
let mut fairness = WallTimeFairness::new(50);
let duration = Duration::from_millis(10);
fairness.update_timing("Test Algorithm", duration, 100);
let budget = fairness.estimate_operations_for_time_slice("Test Algorithm");
assert!(budget > 0);
}
#[test]
fn test_walltime_fairness_budget_calculation() {
let mut fairness = WallTimeFairness::new(100);
fairness.update_timing("Fast", Duration::from_millis(10), 200); fairness.update_timing("Slow", Duration::from_millis(50), 100);
let mut bubble_fast = BubbleSort::new();
let mut bubble_slow = BubbleSort::new();
bubble_fast.reset(vec![1, 2, 3]);
bubble_slow.reset(vec![3, 2, 1]);
let algorithms: Vec<Box<dyn Sorter>> = vec![
Box::new(bubble_fast),
Box::new(bubble_slow),
];
let budgets = fairness.allocate_budget(&algorithms);
assert!(budgets.iter().all(|&b| b > 0));
}
#[test]
fn test_adaptive_walltime_fairness_performance_update() {
let mut fairness = AdaptiveWallTimeFairness::new(100, 0.2);
fairness.update_performance("Test Algorithm", 2.0);
let result = fairness.get_performance_multiplier("Test Algorithm");
let expected = 1.2; assert!((result - expected).abs() < 0.000001, "Expected {}, got {}", expected, result);
fairness.update_performance("Test Algorithm", 0.5);
let expected = 1.2 * 0.8 + 0.5 * 0.2; let result = fairness.get_performance_multiplier("Test Algorithm");
assert!((result - expected).abs() < 0.000001, "Expected {}, got {}", expected, result);
}
#[test]
fn test_walltime_fairness_name() {
let fairness = WallTimeFairness::default();
assert_eq!(fairness.name(), "Wall Time Fairness");
let adaptive_fairness = AdaptiveWallTimeFairness::default();
assert_eq!(adaptive_fairness.name(), "Adaptive Wall Time");
}
}