Skip to main content

windjammer_runtime/
bench.rs

1//! Benchmarking utilities for Windjammer tests
2//!
3//! Provides simple benchmarking functions that can be used with `@bench` decorator.
4//! For more advanced benchmarking, use the criterion crate directly.
5
6use std::time::{Duration, Instant};
7
8/// Run a closure multiple times and return the average duration
9///
10/// # Example
11/// ```
12/// use windjammer_runtime::bench::bench_iterations;
13///
14/// let avg = bench_iterations(1000, || {
15///     // Code to benchmark
16///     let _ = (0..100).sum::<i32>();
17/// });
18///
19/// println!("Average time: {:?}", avg);
20/// ```
21pub fn bench_iterations<F: Fn()>(iterations: usize, f: F) -> Duration {
22    let mut total = Duration::ZERO;
23
24    // Warmup
25    for _ in 0..10 {
26        f();
27    }
28
29    // Actual benchmark
30    for _ in 0..iterations {
31        let start = Instant::now();
32        f();
33        total += start.elapsed();
34    }
35
36    total / iterations as u32
37}
38
39/// Benchmark a closure and return its duration
40///
41/// # Example
42/// ```
43/// use windjammer_runtime::bench::bench;
44///
45/// let duration = bench(|| {
46///     // Code to benchmark
47///     let _ = (0..1000).sum::<i32>();
48/// });
49///
50/// println!("Duration: {:?}", duration);
51/// ```
52pub fn bench<F: FnOnce()>(f: F) -> Duration {
53    let start = Instant::now();
54    f();
55    start.elapsed()
56}
57
58/// Compare two implementations and return their relative performance
59///
60/// # Example
61/// ```
62/// use windjammer_runtime::bench::bench_compare;
63///
64/// let (time_a, time_b, speedup) = bench_compare(
65///     || {
66///         // Old implementation
67///         let _ = (0..1000).sum::<i32>();
68///     },
69///     || {
70///         // New implementation
71///         let _ = (0..1000).fold(0, |acc, x| acc + x);
72///     },
73///     100, // iterations
74/// );
75///
76/// println!("Old: {:?}, New: {:?}, Speedup: {:.2}x", time_a, time_b, speedup);
77/// ```
78pub fn bench_compare<F: Fn(), G: Fn()>(f: F, g: G, iterations: usize) -> (Duration, Duration, f64) {
79    let time_f = bench_iterations(iterations, f);
80    let time_g = bench_iterations(iterations, g);
81
82    let speedup = time_f.as_secs_f64() / time_g.as_secs_f64();
83
84    (time_f, time_g, speedup)
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use std::thread;
91
92    #[test]
93    fn test_bench_iterations() {
94        let avg = bench_iterations(10, || {
95            thread::sleep(Duration::from_millis(1));
96        });
97
98        // Should be around 1ms (with some variance)
99        assert!(avg.as_millis() >= 1 && avg.as_millis() <= 5);
100    }
101
102    #[test]
103    fn test_bench() {
104        let duration = bench(|| {
105            thread::sleep(Duration::from_millis(10));
106        });
107
108        // Should be around 10ms
109        assert!(duration.as_millis() >= 10 && duration.as_millis() <= 20);
110    }
111
112    #[test]
113    #[cfg_attr(tarpaulin, ignore)]
114    fn test_bench_compare() {
115        // Test that bench_compare returns reasonable measurements
116        // Note: We can't reliably test timing relationships in CI due to
117        // thread scheduling variance, so just verify the function works
118        // This test is ignored in tarpaulin because coverage instrumentation
119        // adds overhead that makes timing measurements unreliable
120        let (time_slow, time_fast, speedup) = bench_compare(
121            || {
122                // Use busy-wait instead of sleep for more deterministic timing
123                let start = std::time::Instant::now();
124                let mut sum = 0u64;
125                while start.elapsed() < Duration::from_micros(100) {
126                    sum = sum.wrapping_add(1);
127                }
128            },
129            || {
130                // Shorter busy-wait
131                let start = std::time::Instant::now();
132                let mut sum = 0u64;
133                while start.elapsed() < Duration::from_micros(50) {
134                    sum = sum.wrapping_add(1);
135                }
136            },
137            3,
138        );
139
140        // Just verify we got reasonable measurements (not zero, not absurd)
141        assert!(
142            time_slow > Duration::from_nanos(1),
143            "Slow function should take measurable time, got {:?}",
144            time_slow
145        );
146        assert!(
147            time_fast > Duration::from_nanos(1),
148            "Fast function should take measurable time, got {:?}",
149            time_fast
150        );
151        assert!(
152            speedup > 0.0 && speedup < 1000.0,
153            "Speedup should be reasonable, got {}",
154            speedup
155        );
156    }
157}