⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/
bench.rs

1//! Benchmarking framework for VeridianOS kernel
2//!
3//! Provides performance measurement capabilities for kernel subsystems
4
5// Benchmarking framework
6
7#[cfg(feature = "alloc")]
8extern crate alloc;
9
10/// Trait for benchmarkable operations
11pub trait Benchmark {
12    /// Run the benchmark and return the result
13    fn run(&mut self) -> BenchmarkResult;
14
15    /// Get the name of this benchmark
16    fn name(&self) -> &str;
17}
18
19/// Result of a benchmark run
20#[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    /// Create a new benchmark result
35    #[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    /// Check if this benchmark meets a target latency
54    pub fn meets_target(&self, target_ns: u64) -> bool {
55        self.avg_time_ns <= target_ns
56    }
57}
58
59/// Architecture-specific timestamp counter.
60///
61/// Re-exports the centralized [`crate::arch::entropy::read_timestamp`] which
62/// provides implementations for x86_64 (RDTSC), AArch64 (CNTVCT_EL0), and
63/// RISC-V (rdcycle).
64#[inline]
65pub fn read_timestamp() -> u64 {
66    crate::arch::entropy::read_timestamp()
67}
68
69/// Convert cycles to nanoseconds (approximate)
70/// This assumes a 2GHz processor for now
71pub fn cycles_to_ns(cycles: u64) -> u64 {
72    // 2GHz = 2 cycles per nanosecond
73    cycles / 2
74}
75
76/// Run a benchmark function multiple times
77#[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    // Warmup
88    for _ in 0..10 {
89        f();
90    }
91
92    // Actual benchmark
93    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(), &times)
102}
103
104/// Benchmark harness for running multiple benchmarks
105#[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
161/// Simple bencher for benchmark tests
162pub 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        // Simple iteration - in real benchmarking framework
176        // this would do more sophisticated timing
177        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/// Black box to prevent compiler optimizations
190#[inline]
191pub fn black_box<T>(x: T) -> T {
192    // This is a simple implementation that prevents the compiler
193    // from optimizing away the value
194    // SAFETY: read_volatile reads the value from a valid stack reference.
195    // mem::forget prevents the original from being dropped (avoiding
196    // double-free). The volatile read prevents the compiler from
197    // optimizing away the benchmark computation.
198    unsafe {
199        let ret = core::ptr::read_volatile(&x);
200        core::mem::forget(x);
201        ret
202    }
203}