trueno_gpu/testing/stress/
types.rs1#[derive(Debug, Clone, Default)]
8pub struct FrameProfile {
9 pub cycle: u32,
11 pub duration_ms: u64,
13 pub memory_bytes: usize,
15 pub tests_passed: u32,
17 pub tests_failed: u32,
19 pub input_seed: u64,
21 pub input_size: usize,
23}
24
25#[derive(Debug, Clone, Default)]
27pub struct StressReport {
28 pub frames: Vec<FrameProfile>,
30 pub cycles_completed: u32,
32 pub total_passed: u32,
34 pub total_failed: u32,
36 pub anomalies: Vec<Anomaly>,
38}
39
40impl StressReport {
41 #[must_use]
43 pub fn mean_frame_time_ms(&self) -> f64 {
44 if self.frames.is_empty() {
45 return 0.0;
46 }
47 let sum: u64 = self.frames.iter().map(|f| f.duration_ms).sum();
48 sum as f64 / self.frames.len() as f64
49 }
50
51 #[must_use]
53 pub fn timing_variance(&self) -> f64 {
54 if self.frames.len() < 2 {
55 return 0.0;
56 }
57 let mean = self.mean_frame_time_ms();
58 if mean == 0.0 {
59 return 0.0;
60 }
61 let variance: f64 = self
62 .frames
63 .iter()
64 .map(|f| {
65 let diff = f.duration_ms as f64 - mean;
66 diff * diff
67 })
68 .sum::<f64>()
69 / self.frames.len() as f64;
70 variance.sqrt() / mean
71 }
72
73 #[must_use]
75 pub fn max_frame_time_ms(&self) -> u64 {
76 self.frames.iter().map(|f| f.duration_ms).max().unwrap_or(0)
77 }
78
79 #[must_use]
81 pub fn pass_rate(&self) -> f64 {
82 let total = self.total_passed + self.total_failed;
83 if total == 0 {
84 return 1.0;
85 }
86 self.total_passed as f64 / total as f64
87 }
88
89 pub fn add_frame(&mut self, profile: FrameProfile) {
91 self.total_passed += profile.tests_passed;
92 self.total_failed += profile.tests_failed;
93 self.cycles_completed += 1;
94 self.frames.push(profile);
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct Anomaly {
101 pub cycle: u32,
103 pub kind: AnomalyKind,
105 pub description: String,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum AnomalyKind {
112 SlowFrame,
114 HighMemory,
116 TestFailure,
118 TimingSpike,
120 NonDeterministic,
122}
123
124#[derive(Debug, Clone)]
126pub struct PerformanceThresholds {
127 pub max_frame_time_ms: u64,
129 pub max_memory_bytes: usize,
131 pub max_timing_variance: f64,
133 pub max_failure_rate: f64,
135}
136
137impl Default for PerformanceThresholds {
138 fn default() -> Self {
139 Self {
140 max_frame_time_ms: 100, max_memory_bytes: 64 * 1024 * 1024, max_timing_variance: 0.2, max_failure_rate: 0.01, }
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct PerformanceResult {
151 pub passed: bool,
153 pub max_frame_ms: u64,
155 pub mean_frame_ms: f64,
157 pub variance: f64,
159 pub pass_rate: f64,
161 pub violations: Vec<String>,
163}
164
165#[must_use]
167pub fn verify_performance(
168 report: &StressReport,
169 thresholds: &PerformanceThresholds,
170) -> PerformanceResult {
171 let max_frame = report.max_frame_time_ms();
172 let mean_frame = report.mean_frame_time_ms();
173 let variance = report.timing_variance();
174 let pass_rate = report.pass_rate();
175
176 let mut violations = Vec::new();
177
178 if max_frame > thresholds.max_frame_time_ms {
179 violations.push(format!(
180 "Max frame time {}ms exceeds threshold {}ms",
181 max_frame, thresholds.max_frame_time_ms
182 ));
183 }
184
185 if variance > thresholds.max_timing_variance {
186 violations.push(format!(
187 "Timing variance {:.3} exceeds threshold {:.3}",
188 variance, thresholds.max_timing_variance
189 ));
190 }
191
192 if pass_rate < (1.0 - thresholds.max_failure_rate) {
193 violations.push(format!(
194 "Pass rate {:.1}% below threshold {:.1}%",
195 pass_rate * 100.0,
196 (1.0 - thresholds.max_failure_rate) * 100.0
197 ));
198 }
199
200 PerformanceResult {
201 passed: violations.is_empty(),
202 max_frame_ms: max_frame,
203 mean_frame_ms: mean_frame,
204 variance,
205 pass_rate,
206 violations,
207 }
208}