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

veridian_kernel/ipc/
perf.rs

1//! IPC performance measurement
2//!
3//! This module provides functions for measuring IPC performance
4//! to ensure we meet our latency targets.
5
6// IPC performance measurement
7
8use core::sync::atomic::{AtomicU64, Ordering};
9
10use crate::arch::entropy::read_timestamp;
11
12/// Global IPC performance statistics
13pub struct IpcPerfStats {
14    /// Total IPC operations
15    pub total_ops: AtomicU64,
16    /// Total cycles spent in IPC
17    pub total_cycles: AtomicU64,
18    /// Minimum latency observed
19    pub min_latency: AtomicU64,
20    /// Maximum latency observed
21    pub max_latency: AtomicU64,
22    /// Fast path success count
23    pub fast_path_count: AtomicU64,
24    /// Slow path count
25    pub slow_path_count: AtomicU64,
26}
27
28impl IpcPerfStats {
29    pub const fn new() -> Self {
30        Self {
31            total_ops: AtomicU64::new(0),
32            total_cycles: AtomicU64::new(0),
33            min_latency: AtomicU64::new(u64::MAX),
34            max_latency: AtomicU64::new(0),
35            fast_path_count: AtomicU64::new(0),
36            slow_path_count: AtomicU64::new(0),
37        }
38    }
39
40    /// Record an IPC operation
41    pub fn record_operation(&self, cycles: u64, is_fast_path: bool) {
42        self.total_ops.fetch_add(1, Ordering::Relaxed);
43        self.total_cycles.fetch_add(cycles, Ordering::Relaxed);
44
45        // Update min latency
46        let mut current_min = self.min_latency.load(Ordering::Relaxed);
47        while cycles < current_min {
48            match self.min_latency.compare_exchange_weak(
49                current_min,
50                cycles,
51                Ordering::Relaxed,
52                Ordering::Relaxed,
53            ) {
54                Ok(_) => break,
55                Err(val) => current_min = val,
56            }
57        }
58
59        // Update max latency
60        let mut current_max = self.max_latency.load(Ordering::Relaxed);
61        while cycles > current_max {
62            match self.max_latency.compare_exchange_weak(
63                current_max,
64                cycles,
65                Ordering::Relaxed,
66                Ordering::Relaxed,
67            ) {
68                Ok(_) => break,
69                Err(val) => current_max = val,
70            }
71        }
72
73        // Update path counters
74        if is_fast_path {
75            self.fast_path_count.fetch_add(1, Ordering::Relaxed);
76        } else {
77            self.slow_path_count.fetch_add(1, Ordering::Relaxed);
78        }
79    }
80
81    /// Get average latency in cycles
82    pub fn average_latency(&self) -> u64 {
83        let ops = self.total_ops.load(Ordering::Relaxed);
84        let cycles = self.total_cycles.load(Ordering::Relaxed);
85        if ops > 0 {
86            cycles / ops
87        } else {
88            0
89        }
90    }
91
92    /// Get a performance report
93    pub fn get_report(&self) -> IpcPerfReport {
94        let total_ops = self.total_ops.load(Ordering::Relaxed);
95        let fast_path = self.fast_path_count.load(Ordering::Relaxed);
96        let _slow_path = self.slow_path_count.load(Ordering::Relaxed);
97
98        IpcPerfReport {
99            total_operations: total_ops,
100            average_latency_cycles: self.average_latency(),
101            min_latency_cycles: self.min_latency.load(Ordering::Relaxed),
102            max_latency_cycles: self.max_latency.load(Ordering::Relaxed),
103            fast_path_percentage: if total_ops > 0 {
104                (fast_path * 100) / total_ops
105            } else {
106                0
107            },
108            average_latency_ns: cycles_to_ns(self.average_latency()),
109            min_latency_ns: cycles_to_ns(self.min_latency.load(Ordering::Relaxed)),
110            max_latency_ns: cycles_to_ns(self.max_latency.load(Ordering::Relaxed)),
111        }
112    }
113}
114
115impl Default for IpcPerfStats {
116    fn default() -> Self {
117        Self::new()
118    }
119}
120
121/// Global IPC performance statistics instance
122pub(crate) static IPC_PERF_STATS: IpcPerfStats = IpcPerfStats::new();
123
124/// IPC performance report
125#[derive(Debug, Clone, Copy)]
126pub struct IpcPerfReport {
127    pub total_operations: u64,
128    pub average_latency_cycles: u64,
129    pub min_latency_cycles: u64,
130    pub max_latency_cycles: u64,
131    pub fast_path_percentage: u64,
132    pub average_latency_ns: u64,
133    pub min_latency_ns: u64,
134    pub max_latency_ns: u64,
135}
136
137impl IpcPerfReport {
138    /// Check if we meet Phase 1 targets
139    pub fn meets_phase1_targets(&self) -> bool {
140        // Phase 1 targets: < 5μs (5000ns) for all messages
141        self.average_latency_ns < 5000 && self.max_latency_ns < 10000
142    }
143
144    /// Check if we meet Phase 5 targets
145    pub fn meets_phase5_targets(&self) -> bool {
146        // Phase 5 targets: < 1μs (1000ns) average
147        self.average_latency_ns < 1000
148    }
149
150    /// Print the performance report
151    pub fn print(&self) {
152        println!("\n=== IPC Performance Report ===");
153        println!("Total operations: {}", self.total_operations);
154        println!("Fast path usage: {}%", self.fast_path_percentage);
155        println!("\nLatency (cycles):");
156        println!("  Average: {}", self.average_latency_cycles);
157        println!("  Min: {}", self.min_latency_cycles);
158        println!("  Max: {}", self.max_latency_cycles);
159        println!("\nLatency (nanoseconds):");
160        println!("  Average: {} ns", self.average_latency_ns);
161        println!("  Min: {} ns", self.min_latency_ns);
162        println!("  Max: {} ns", self.max_latency_ns);
163
164        let _phase1_status = if self.meets_phase1_targets() {
165            "\n[PASS] Meets Phase 1 targets (<5us)"
166        } else {
167            "\n[FAIL] Does not meet Phase 1 targets"
168        };
169        println!("{}", _phase1_status);
170
171        let _phase5_status = if self.meets_phase5_targets() {
172            "[PASS] Meets Phase 5 targets (<1us average)"
173        } else {
174            "[FAIL] Does not meet Phase 5 targets"
175        };
176        println!("{}", _phase5_status);
177    }
178}
179
180/// Convert CPU cycles to nanoseconds (assumes 2GHz CPU)
181pub fn cycles_to_ns(cycles: u64) -> u64 {
182    // For a 2GHz CPU: 1 cycle = 0.5ns
183    // Adjust this based on actual CPU frequency
184    cycles / 2
185}
186
187/// Measure a single IPC operation
188#[inline(always)]
189pub fn measure_ipc_operation<F, R>(f: F) -> (R, u64)
190where
191    F: FnOnce() -> R,
192{
193    let start = read_timestamp();
194    let result = f();
195    let elapsed = read_timestamp() - start;
196    (result, elapsed)
197}
198
199/// Benchmark utilities for measuring IPC performance
200pub mod bench {
201    use super::*;
202
203    /// Run a performance test
204    pub fn run_perf_test<F>(name: &str, iterations: usize, mut f: F)
205    where
206        F: FnMut(),
207    {
208        kprintln!("\nRunning performance test:");
209        kprint_rt!(name);
210        kprintln!();
211
212        // Warmup
213        for _ in 0..10 {
214            f();
215        }
216
217        // Actual measurement
218        let start = read_timestamp();
219        for _ in 0..iterations {
220            f();
221        }
222        let total_cycles = read_timestamp() - start;
223        let avg_cycles = total_cycles / iterations as u64;
224
225        let avg_ns = cycles_to_ns(avg_cycles);
226
227        kprintln!("  Iterations:");
228        kprint_u64!(iterations);
229        kprintln!();
230        kprintln!("  Average cycles:");
231        kprint_u64!(avg_cycles);
232        kprintln!();
233        kprintln!("  Average ns:");
234        kprint_u64!(avg_ns);
235        kprintln!();
236
237        if avg_ns < 1000 {
238            kprintln!("  Sub-microsecond performance!");
239        }
240    }
241
242    /// Measure IPC throughput
243    pub fn measure_throughput<F>(name: &str, duration_ms: u64, mut f: F) -> u64
244    where
245        F: FnMut(),
246    {
247        kprintln!("\nMeasuring throughput:");
248        kprint_rt!(name);
249        kprintln!();
250
251        let duration_cycles = duration_ms * 2_000_000; // Assuming 2GHz
252        let start = read_timestamp();
253        let mut operations = 0u64;
254
255        while read_timestamp() - start < duration_cycles {
256            f();
257            operations += 1;
258        }
259
260        let actual_duration_ms = cycles_to_ns(read_timestamp() - start) / 1_000_000;
261
262        kprintln!("  Operations:");
263        kprint_u64!(operations);
264        kprintln!();
265        kprintln!("  Duration ms:");
266        kprint_u64!(actual_duration_ms);
267        kprintln!();
268
269        (operations * 1000) / actual_duration_ms
270    }
271}