sorting_race/services/fairness/
walltime.rs1use crate::models::traits::{FairnessModel, Sorter};
4use std::time::Duration;
5use std::collections::HashMap;
6
7#[derive(Debug)]
10pub struct WallTimeFairness {
11 slice_ms: u64, algorithm_timings: HashMap<String, Duration>, algorithm_speeds: HashMap<String, f64>, }
15
16impl WallTimeFairness {
17 pub fn new(slice_ms: u64) -> Self {
22 Self {
23 slice_ms: slice_ms.max(1),
24 algorithm_timings: HashMap::new(),
25 algorithm_speeds: HashMap::new(),
26 }
27 }
28
29 pub fn update_timing(&mut self, algorithm_name: &str, duration: Duration, operations_performed: usize) {
31 self.algorithm_timings.insert(algorithm_name.to_string(), duration);
32
33 if duration.as_millis() > 0 {
34 let speed = operations_performed as f64 / duration.as_millis() as f64;
35 self.algorithm_speeds.insert(algorithm_name.to_string(), speed);
36 }
37 }
38
39 fn estimate_operations_for_time_slice(&self, algorithm_name: &str) -> usize {
41 let default_speed = 10.0; let speed = self.algorithm_speeds.get(algorithm_name).copied().unwrap_or(default_speed);
43 ((speed * self.slice_ms as f64).round() as usize).max(1)
44 }
45
46 pub fn get_slice_ms(&self) -> u64 {
48 self.slice_ms
49 }
50
51 pub fn clear_history(&mut self) {
53 self.algorithm_timings.clear();
54 self.algorithm_speeds.clear();
55 }
56}
57
58impl Default for WallTimeFairness {
59 fn default() -> Self {
60 Self::new(50) }
62}
63
64impl FairnessModel for WallTimeFairness {
65 fn allocate_budget(&self, algorithms: &[Box<dyn Sorter>]) -> Vec<usize> {
66 algorithms
67 .iter()
68 .map(|algorithm| {
69 if algorithm.is_complete() {
70 0 } else {
72 self.estimate_operations_for_time_slice(algorithm.name())
74 }
75 })
76 .collect()
77 }
78
79 fn name(&self) -> &str {
80 "Wall Time Fairness"
81 }
82}
83
84#[derive(Debug)]
86pub struct AdaptiveWallTimeFairness {
87 base_fairness: WallTimeFairness,
88 performance_multipliers: HashMap<String, f64>,
89 adaptation_rate: f64,
90}
91
92impl AdaptiveWallTimeFairness {
93 pub fn new(slice_ms: u64, adaptation_rate: f64) -> Self {
95 Self {
96 base_fairness: WallTimeFairness::new(slice_ms),
97 performance_multipliers: HashMap::new(),
98 adaptation_rate: adaptation_rate.clamp(0.01, 1.0),
99 }
100 }
101
102 pub fn update_performance(&mut self, algorithm_name: &str, efficiency: f64) {
104 let multiplier = self.performance_multipliers
105 .entry(algorithm_name.to_string())
106 .or_insert(1.0);
107
108 *multiplier = *multiplier * (1.0 - self.adaptation_rate) + efficiency * self.adaptation_rate;
110
111 *multiplier = multiplier.clamp(0.1, 3.0);
113 }
114
115 fn get_performance_multiplier(&self, algorithm_name: &str) -> f64 {
117 self.performance_multipliers.get(algorithm_name).copied().unwrap_or(1.0)
118 }
119
120 pub fn clear_performance_history(&mut self) {
122 self.performance_multipliers.clear();
123 }
124
125 pub fn update_timing(&mut self, algorithm_name: &str, duration: Duration, operations_performed: usize) {
127 self.base_fairness.update_timing(algorithm_name, duration, operations_performed);
128 }
129}
130
131impl Default for AdaptiveWallTimeFairness {
132 fn default() -> Self {
133 Self::new(50, 0.1) }
135}
136
137impl FairnessModel for AdaptiveWallTimeFairness {
138 fn allocate_budget(&self, algorithms: &[Box<dyn Sorter>]) -> Vec<usize> {
139 let base_budgets = self.base_fairness.allocate_budget(algorithms);
140
141 base_budgets
142 .into_iter()
143 .zip(algorithms.iter())
144 .map(|(budget, algorithm)| {
145 if budget == 0 {
146 0 } else {
148 let multiplier = self.get_performance_multiplier(algorithm.name());
149 ((budget as f64) * multiplier).round() as usize
150 }
151 })
152 .collect()
153 }
154
155 fn name(&self) -> &str {
156 "Adaptive Wall Time"
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::services::sorters::bubble::BubbleSort;
164
165 use std::time::Duration;
166
167 #[test]
168 fn test_walltime_fairness_timing() {
169 let mut fairness = WallTimeFairness::new(50);
170
171 let duration = Duration::from_millis(10);
172 fairness.update_timing("Test Algorithm", duration, 100);
173
174 let budget = fairness.estimate_operations_for_time_slice("Test Algorithm");
175 assert!(budget > 0);
176 }
177
178 #[test]
179 fn test_walltime_fairness_budget_calculation() {
180 let mut fairness = WallTimeFairness::new(100);
181
182 fairness.update_timing("Fast", Duration::from_millis(10), 200); fairness.update_timing("Slow", Duration::from_millis(50), 100); let mut bubble_fast = BubbleSort::new();
187 let mut bubble_slow = BubbleSort::new();
188 bubble_fast.reset(vec![1, 2, 3]);
189 bubble_slow.reset(vec![3, 2, 1]);
190
191 let algorithms: Vec<Box<dyn Sorter>> = vec![
192 Box::new(bubble_fast),
193 Box::new(bubble_slow),
194 ];
195
196 let budgets = fairness.allocate_budget(&algorithms);
197
198 assert!(budgets.iter().all(|&b| b > 0));
200
201 }
205
206 #[test]
207 fn test_adaptive_walltime_fairness_performance_update() {
208 let mut fairness = AdaptiveWallTimeFairness::new(100, 0.2);
209
210 fairness.update_performance("Test Algorithm", 2.0);
212 let result = fairness.get_performance_multiplier("Test Algorithm");
213 let expected = 1.2; assert!((result - expected).abs() < 0.000001, "Expected {}, got {}", expected, result);
215
216 fairness.update_performance("Test Algorithm", 0.5);
218 let expected = 1.2 * 0.8 + 0.5 * 0.2; let result = fairness.get_performance_multiplier("Test Algorithm");
220 assert!((result - expected).abs() < 0.000001, "Expected {}, got {}", expected, result);
221 }
222
223 #[test]
224 fn test_walltime_fairness_name() {
225 let fairness = WallTimeFairness::default();
226 assert_eq!(fairness.name(), "Wall Time Fairness");
227
228 let adaptive_fairness = AdaptiveWallTimeFairness::default();
229 assert_eq!(adaptive_fairness.name(), "Adaptive Wall Time");
230 }
231}