1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum WorkloadPhase {
10 Warmup,
12 SteadyState,
14 Burst,
16 Cooldown,
18 Custom(&'static str),
20}
21
22pub struct RegimeClassifier {
25 current_phase: WorkloadPhase,
26 warmup_samples_remaining: u32,
27 throughput_ema: f64,
28 throughput_ema_alpha: f64,
29 burst_threshold_multiplier: f64,
30}
31
32impl RegimeClassifier {
33 pub fn new(warmup_samples: u32, burst_threshold: f64) -> Self {
38 Self {
39 current_phase: WorkloadPhase::Warmup,
40 warmup_samples_remaining: warmup_samples,
41 throughput_ema: 0.0,
42 throughput_ema_alpha: 0.1,
43 burst_threshold_multiplier: burst_threshold.max(1.1),
44 }
45 }
46
47 pub fn observe_throughput(&mut self, throughput: f64) -> WorkloadPhase {
49 if self.warmup_samples_remaining > 0 {
50 self.warmup_samples_remaining -= 1;
51 self.throughput_ema = throughput; if self.warmup_samples_remaining == 0 {
53 self.current_phase = WorkloadPhase::SteadyState;
54 }
55 return self.current_phase;
56 }
57
58 self.throughput_ema = self.throughput_ema_alpha * throughput
60 + (1.0 - self.throughput_ema_alpha) * self.throughput_ema;
61
62 if throughput > self.throughput_ema * self.burst_threshold_multiplier {
64 self.current_phase = WorkloadPhase::Burst;
65 } else if throughput < self.throughput_ema * 0.1 {
66 self.current_phase = WorkloadPhase::Cooldown;
67 } else {
68 self.current_phase = WorkloadPhase::SteadyState;
69 }
70
71 self.current_phase
72 }
73
74 pub fn set_phase(&mut self, phase: WorkloadPhase) {
76 self.current_phase = phase;
77 }
78
79 pub fn phase(&self) -> WorkloadPhase {
81 self.current_phase
82 }
83
84 pub fn reset(&mut self, warmup_samples: u32) {
86 self.current_phase = WorkloadPhase::Warmup;
87 self.warmup_samples_remaining = warmup_samples;
88 self.throughput_ema = 0.0;
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_starts_in_warmup() {
98 let classifier = RegimeClassifier::new(10, 2.0);
99 assert_eq!(classifier.phase(), WorkloadPhase::Warmup);
100 }
101
102 #[test]
103 fn test_transitions_to_steady_state() {
104 let mut classifier = RegimeClassifier::new(3, 2.0);
105 classifier.observe_throughput(100.0);
106 classifier.observe_throughput(100.0);
107 assert_eq!(classifier.phase(), WorkloadPhase::Warmup);
108 classifier.observe_throughput(100.0);
109 assert_eq!(classifier.phase(), WorkloadPhase::SteadyState);
110 }
111}