1#[cfg(feature = "alloc")]
8extern crate alloc;
9
10pub trait Benchmark {
12 fn run(&mut self) -> BenchmarkResult;
14
15 fn name(&self) -> &str;
17}
18
19#[derive(Debug, Clone)]
21pub struct BenchmarkResult {
22 #[cfg(feature = "alloc")]
23 pub name: alloc::string::String,
24 #[cfg(not(feature = "alloc"))]
25 pub name: &'static str,
26 pub iterations: u64,
27 pub total_time_ns: u64,
28 pub avg_time_ns: u64,
29 pub min_time_ns: u64,
30 pub max_time_ns: u64,
31}
32
33impl BenchmarkResult {
34 #[cfg(feature = "alloc")]
36 pub fn new(name: alloc::string::String, times: &[u64]) -> Self {
37 let iterations = times.len() as u64;
38 let total_time_ns: u64 = times.iter().sum();
39 let avg_time_ns = total_time_ns / iterations;
40 let min_time_ns = *times.iter().min().unwrap_or(&0);
41 let max_time_ns = *times.iter().max().unwrap_or(&0);
42
43 Self {
44 name,
45 iterations,
46 total_time_ns,
47 avg_time_ns,
48 min_time_ns,
49 max_time_ns,
50 }
51 }
52
53 pub fn meets_target(&self, target_ns: u64) -> bool {
55 self.avg_time_ns <= target_ns
56 }
57}
58
59#[inline]
65pub fn read_timestamp() -> u64 {
66 crate::arch::entropy::read_timestamp()
67}
68
69pub fn cycles_to_ns(cycles: u64) -> u64 {
72 cycles / 2
74}
75
76#[cfg(feature = "alloc")]
78pub fn bench_function<F>(name: &str, iterations: u64, mut f: F) -> BenchmarkResult
79where
80 F: FnMut(),
81{
82 extern crate alloc;
83 use alloc::{string::ToString, vec::Vec};
84
85 let mut times = Vec::with_capacity(iterations as usize);
86
87 for _ in 0..10 {
89 f();
90 }
91
92 for _ in 0..iterations {
94 let start = read_timestamp();
95 f();
96 let end = read_timestamp();
97 let elapsed_cycles = end.saturating_sub(start);
98 times.push(cycles_to_ns(elapsed_cycles));
99 }
100
101 BenchmarkResult::new(name.to_string(), ×)
102}
103
104#[cfg(feature = "alloc")]
106pub struct BenchmarkHarness {
107 benchmarks: alloc::vec::Vec<alloc::boxed::Box<dyn Benchmark>>,
108}
109
110#[cfg(feature = "alloc")]
111impl Default for BenchmarkHarness {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117#[cfg(feature = "alloc")]
118impl BenchmarkHarness {
119 pub fn new() -> Self {
120 Self {
121 benchmarks: alloc::vec::Vec::new(),
122 }
123 }
124
125 pub fn add_benchmark(&mut self, bench: alloc::boxed::Box<dyn Benchmark>) {
126 self.benchmarks.push(bench);
127 }
128
129 pub fn run_all(&mut self) -> alloc::vec::Vec<BenchmarkResult> {
130 use crate::serial_println;
131
132 serial_println!("Running {} benchmarks...", self.benchmarks.len());
133
134 let mut results = alloc::vec::Vec::new();
135
136 for bench in &mut self.benchmarks {
137 serial_println!("Running benchmark: {}", bench.name());
138 let result = bench.run();
139
140 serial_println!(
141 " Avg: {} ns, Min: {} ns, Max: {} ns",
142 result.avg_time_ns,
143 result.min_time_ns,
144 result.max_time_ns
145 );
146
147 results.push(result);
148 }
149
150 results
151 }
152}
153
154#[macro_export]
155macro_rules! benchmark {
156 ($name:expr, $iterations:expr, $code:block) => {{
157 $crate::bench::bench_function($name, $iterations, || $code)
158 }};
159}
160
161pub struct Bencher {
163 iterations: u64,
164}
165
166impl Bencher {
167 pub fn new() -> Self {
168 Self { iterations: 100 }
169 }
170
171 pub fn iter<F>(&mut self, mut f: F)
172 where
173 F: FnMut(),
174 {
175 for _ in 0..self.iterations {
178 f();
179 }
180 }
181}
182
183impl Default for Bencher {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189#[inline]
191pub fn black_box<T>(x: T) -> T {
192 unsafe {
199 let ret = core::ptr::read_volatile(&x);
200 core::mem::forget(x);
201 ret
202 }
203}