cbtop/performance_prediction/
types.rs1pub const MIN_SAMPLES_FOR_FIT: usize = 5;
5
6#[derive(Debug, Clone, Copy)]
8pub struct DataPoint {
9 pub size: usize,
11 pub performance: f64,
13 pub latency_us: f64,
15}
16
17impl DataPoint {
18 pub fn new(size: usize, performance: f64, latency_us: f64) -> Self {
20 Self {
21 size,
22 performance,
23 latency_us,
24 }
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum ModelType {
31 Linear,
33 Polynomial,
35 ExponentialDecay,
37 Logarithmic,
39 Roofline,
41}
42
43impl ModelType {
44 pub fn name(&self) -> &'static str {
46 match self {
47 Self::Linear => "linear",
48 Self::Polynomial => "polynomial",
49 Self::ExponentialDecay => "exponential_decay",
50 Self::Logarithmic => "logarithmic",
51 Self::Roofline => "roofline",
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct FittedModel {
59 pub model_type: ModelType,
61 pub coefficients: Vec<f64>,
63 pub r_squared: f64,
65 pub rss: f64,
67 pub sample_count: usize,
69}
70
71impl FittedModel {
72 pub fn predict(&self, size: usize) -> f64 {
74 let x = size as f64;
75 match self.model_type {
76 ModelType::Linear => {
77 let a = self.coefficients.first().copied().unwrap_or(0.0);
79 let b = self.coefficients.get(1).copied().unwrap_or(0.0);
80 a * x + b
81 }
82 ModelType::Polynomial => {
83 let a = self.coefficients.first().copied().unwrap_or(0.0);
85 let b = self.coefficients.get(1).copied().unwrap_or(0.0);
86 let c = self.coefficients.get(2).copied().unwrap_or(0.0);
87 a * x * x + b * x + c
88 }
89 ModelType::ExponentialDecay => {
90 let a = self.coefficients.first().copied().unwrap_or(0.0);
92 let b = self.coefficients.get(1).copied().unwrap_or(0.0);
93 let c = self.coefficients.get(2).copied().unwrap_or(0.0);
94 a * (-b * x).exp() + c
95 }
96 ModelType::Logarithmic => {
97 let a = self.coefficients.first().copied().unwrap_or(0.0);
99 let b = self.coefficients.get(1).copied().unwrap_or(0.0);
100 if x > 0.0 {
101 a * x.ln() + b
102 } else {
103 b
104 }
105 }
106 ModelType::Roofline => {
107 let peak = self.coefficients.first().copied().unwrap_or(100.0);
110 let _knee = self.coefficients.get(1).copied().unwrap_or(1000.0);
111 let slope = self.coefficients.get(2).copied().unwrap_or(1.0);
112
113 let linear = slope * x;
114 linear.min(peak).max(0.0)
115 }
116 }
117 }
118
119 pub fn is_good_fit(&self) -> bool {
121 self.r_squared > 0.9
122 }
123}
124
125#[derive(Debug, Clone)]
127pub struct Prediction {
128 pub size: usize,
130 pub predicted: f64,
132 pub lower_bound: f64,
134 pub upper_bound: f64,
136 pub confidence_level: f64,
138 pub model_type: ModelType,
140 pub is_extrapolation: bool,
142}
143
144impl Prediction {
145 pub fn is_reasonable(&self) -> bool {
147 self.predicted > 0.0
148 && self.lower_bound >= 0.0
149 && self.upper_bound >= self.predicted
150 && !self.predicted.is_nan()
151 && !self.predicted.is_infinite()
152 }
153
154 pub fn range_width(&self) -> f64 {
156 self.upper_bound - self.lower_bound
157 }
158
159 pub fn uncertainty_percent(&self) -> f64 {
161 if self.predicted > 0.0 {
162 (self.range_width() / self.predicted) * 100.0 / 2.0
163 } else {
164 100.0
165 }
166 }
167}