1extern crate alloc;
4use alloc::vec::Vec;
5use alloc::string::String;
6
7pub struct BenchmarkResults {
8 measurements: Vec<u64>,
9 name: String,
10}
11
12impl BenchmarkResults {
13 pub fn new(name: String) -> Self {
14 Self {
15 measurements: Vec::with_capacity(10000),
16 name,
17 }
18 }
19
20 pub fn record(&mut self, nanoseconds: u64) {
21 self.measurements.push(nanoseconds);
22 }
23
24 pub fn len(&self) -> usize {
25 self.measurements.len()
26 }
27
28 pub fn is_empty(&self) -> bool {
29 self.measurements.is_empty()
30 }
31
32 pub fn analyze(&self) -> BenchmarkAnalysis {
33 if self.measurements.is_empty() {
34 return BenchmarkAnalysis::empty(self.name.clone());
35 }
36
37 let mut sorted = self.measurements.clone();
38 sorted.sort_unstable();
39
40 let len = sorted.len();
41 let sum: u64 = sorted.iter().sum();
42 let mean = sum / len as u64;
43
44 let variance = sorted.iter()
45 .map(|&x| {
46 let diff = (x as f64) - (mean as f64);
47 diff * diff
48 })
49 .sum::<f64>() / len as f64;
50
51 BenchmarkAnalysis {
52 name: self.name.clone(),
53 count: len,
54 min: sorted[0],
55 max: sorted[len - 1],
56 mean,
57 p50: percentile(&sorted, 50.0),
58 p95: percentile(&sorted, 95.0),
59 p99: percentile(&sorted, 99.0),
60 p999: percentile(&sorted, 99.9),
61 std_dev: variance.sqrt(),
62 }
63 }
64
65 pub fn clear(&mut self) {
66 self.measurements.clear();
67 }
68}
69
70#[derive(Debug, Clone)]
71pub struct BenchmarkAnalysis {
72 pub name: String,
73 pub count: usize,
74 pub min: u64,
75 pub max: u64,
76 pub mean: u64,
77 pub p50: u64,
78 pub p95: u64,
79 pub p99: u64,
80 pub p999: u64,
81 pub std_dev: f64,
82}
83
84fn percentile(sorted_data: &[u64], p: f64) -> u64 {
85 let len = sorted_data.len();
86 if len == 0 { return 0; }
87 if len == 1 { return sorted_data[0]; }
88
89 let index = (p / 100.0 * (len - 1) as f64).round() as usize;
90 sorted_data[index.min(len - 1)]
91}
92
93impl BenchmarkAnalysis {
94 pub fn empty(name: String) -> Self {
95 Self {
96 name,
97 count: 0,
98 min: 0,
99 max: 0,
100 mean: 0,
101 p50: 0,
102 p95: 0,
103 p99: 0,
104 p999: 0,
105 std_dev: 0.0,
106 }
107 }
108 pub fn summary(&self) -> String {
109 format!(
110 "{}: {} samples, mean={:>6}ns, p50={:>6}ns, p95={:>6}ns, p99={:>6}ns, p99.9={:>6}ns, std_dev={:>6.1}ns",
111 self.name, self.count, self.mean, self.p50, self.p95, self.p99, self.p999, self.std_dev
112 )
113 }
114
115 pub fn meets_target(&self, target_p99_ns: u64) -> bool {
116 self.p99 <= target_p99_ns
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_benchmark_results() {
126 let mut results = BenchmarkResults::new("test".to_string());
127
128 for i in 1..=100 {
129 results.record(i * 10);
130 }
131
132 let analysis = results.analyze();
133 assert_eq!(analysis.count, 100);
134 assert_eq!(analysis.min, 10);
135 assert_eq!(analysis.max, 1000);
136 assert_eq!(analysis.mean, 505);
137 assert_eq!(analysis.p50, 510);
138 }
139
140 #[test]
141 fn test_empty_results() {
142 let results = BenchmarkResults::new("empty".to_string());
143 let analysis = results.analyze();
144
145 assert_eq!(analysis.count, 0);
146 assert_eq!(analysis.mean, 0);
147 }
148
149 #[test]
150 fn test_target_checking() {
151 let mut results = BenchmarkResults::new("target_test".to_string());
152
153 for _ in 0..1000 {
154 results.record(100);
155 }
156
157 let analysis = results.analyze();
158 assert!(analysis.meets_target(150));
159 assert!(!analysis.meets_target(50));
160 }
161
162 #[test]
163 fn test_percentile_calculation() {
164 let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
165
166 assert_eq!(percentile(&data, 50.0), 6);
167 assert_eq!(percentile(&data, 90.0), 9);
168 assert_eq!(percentile(&data, 99.0), 10);
169
170 assert_eq!(percentile(&[], 50.0), 0);
171 assert_eq!(percentile(&[42], 50.0), 42);
172 }
173
174 #[test]
175 fn test_clear_measurements() {
176 let mut results = BenchmarkResults::new("clear_test".to_string());
177 results.record(100);
178 results.record(200);
179
180 assert_eq!(results.len(), 2);
181 assert!(!results.is_empty());
182
183 results.clear();
184 assert_eq!(results.len(), 0);
185 assert!(results.is_empty());
186 }
187
188 #[test]
189 fn test_analysis_summary_format() {
190 let mut results = BenchmarkResults::new("format_test".to_string());
191 results.record(100);
192 results.record(200);
193
194 let analysis = results.analyze();
195 let summary = analysis.summary();
196
197 assert!(summary.contains("format_test"));
198 assert!(summary.contains("2 samples"));
199 assert!(summary.contains("mean"));
200 }
201}