netstack3_base/testutil/benchmarks.rs
1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Netstack3 benchmark utilities.
6
7/// Declare a benchmark function.
8///
9/// If `cfg(benchmark)` is enabled, a function named `name` is emitted and it
10/// receives [`RealBencher`].
11///
12/// If `cfg(test)` is enabled, a module named `name` with a single test called
13/// `test_bench` is emitted and it receives [`TestBencher`].
14///
15/// This setup allows for all benchmarking functions to run in unit test mode,
16/// allowing problems to be noticed sooner.
17///
18/// Note that `$fn` doesn't have to be a named function - it can also be an
19/// anonymous closure.
20#[macro_export]
21macro_rules! bench {
22 ($name:ident, $fn:expr) => {
23 #[cfg(benchmark)]
24 pub(crate) fn $name(b: &mut $crate::testutil::RealBencher) {
25 $fn(b);
26 }
27
28 #[cfg(test)]
29 mod $name {
30 use super::*;
31 #[test]
32 fn test_bench() {
33 $fn(&mut $crate::testutil::TestBencher);
34 }
35 }
36 };
37}
38
39/// A trait to allow faking of the type providing benchmarking.
40pub trait Bencher {
41 /// Benchmarks `inner` by running it multiple times.
42 fn iter<T, F: FnMut() -> T>(&mut self, inner: F);
43
44 /// Abstracts blackboxing.
45 ///
46 /// `black_box` prevents the compiler from optimizing a function with an
47 /// unused return type.
48 fn black_box<T>(placeholder: T) -> T;
49}
50
51/// An alias for the bencher used in real benchmarks.
52pub use criterion::Bencher as RealBencher;
53
54impl Bencher for RealBencher {
55 fn iter<T, F: FnMut() -> T>(&mut self, inner: F) {
56 criterion::Bencher::iter(self, inner)
57 }
58
59 #[inline(always)]
60 fn black_box<T>(placeholder: T) -> T {
61 criterion::black_box(placeholder)
62 }
63}
64
65/// A `Bencher` whose `iter` method runs the provided argument a small,
66/// fixed number of times.
67pub struct TestBencher;
68
69impl Bencher for TestBencher {
70 fn iter<T, F: FnMut() -> T>(&mut self, mut inner: F) {
71 const NUM_TEST_ITERS: u32 = 3;
72 for _ in 0..NUM_TEST_ITERS {
73 let _: T = inner();
74 }
75 }
76
77 #[inline(always)]
78 fn black_box<T>(placeholder: T) -> T {
79 placeholder
80 }
81}