solana/measure/src/macros.rs

189 lines
5.5 KiB
Rust

/// Measure this expression
///
/// Use `measure!()` when you have an expression that you want to measure. `measure!()` will start
/// a new [`Measure`], evaluate your expression, stop the [`Measure`], and then return the
/// [`Measure`] object along with your expression's return value.
///
/// Use `measure_us!()` when you want to measure an expression in microseconds.
///
/// [`Measure`]: crate::measure::Measure
///
/// # Examples
///
/// ```
/// // Measure functions
/// # use solana_measure::{measure, measure_us};
/// # fn foo() {}
/// # fn bar(x: i32) {}
/// # fn add(x: i32, y: i32) -> i32 {x + y}
/// let (result, measure) = measure!(foo(), "foo takes no parameters");
/// let (result, measure) = measure!(bar(42), "bar takes one parameter");
/// let (result, measure) = measure!(add(1, 2), "add takes two parameters and returns a value");
/// let (result, measure_us) = measure_us!(add(1, 2));
/// # assert_eq!(result, 1 + 2);
/// ```
///
/// ```
/// // Measure methods
/// # use solana_measure::{measure, measure_us};
/// # struct Foo {
/// # f: i32,
/// # }
/// # impl Foo {
/// # fn frobnicate(&self, bar: i32) -> i32 {
/// # self.f * bar
/// # }
/// # }
/// let foo = Foo { f: 42 };
/// let (result, measure) = measure!(foo.frobnicate(2), "measure methods");
/// let (result, measure_us) = measure_us!(foo.frobnicate(2));
/// # assert_eq!(result, 42 * 2);
/// ```
///
/// ```
/// // Measure expression blocks
/// # use solana_measure::measure;
/// # fn complex_calculation() -> i32 { 42 }
/// # fn complex_transform(x: i32) -> i32 { x + 3 }
/// # fn record_result(y: i32) {}
/// let (result, measure) = measure!(
/// {
/// let x = complex_calculation();
/// # assert_eq!(x, 42);
/// let y = complex_transform(x);
/// # assert_eq!(y, 42 + 3);
/// record_result(y);
/// y
/// },
/// "measure a block of many operations",
/// );
/// # assert_eq!(result, 42 + 3);
/// ```
///
/// ```
/// // The `name` parameter is optional
/// # use solana_measure::{measure, measure_us};
/// # fn meow() {};
/// let (result, measure) = measure!(meow());
/// let (result, measure_us) = measure_us!(meow());
/// ```
#[macro_export]
macro_rules! measure {
($val:expr, $name:tt $(,)?) => {{
let mut measure = $crate::measure::Measure::start($name);
let result = $val;
measure.stop();
(result, measure)
}};
($val:expr) => {
measure!($val, "")
};
}
#[macro_export]
macro_rules! measure_us {
($val:expr) => {{
let start = std::time::Instant::now();
let result = $val;
(result, solana_sdk::timing::duration_as_us(&start.elapsed()))
}};
}
#[cfg(test)]
mod tests {
use std::{thread::sleep, time::Duration};
fn my_multiply(x: i32, y: i32) -> i32 {
x * y
}
fn square(x: i32) -> i32 {
my_multiply(x, x)
}
struct SomeStruct {
x: i32,
}
impl SomeStruct {
fn add_to(&self, x: i32) -> i32 {
x + self.x
}
}
#[test]
fn test_measure_macro() {
// Ensure that the measurement side actually works
{
let (_result, measure) = measure!(sleep(Duration::from_secs(1)), "test");
assert!(measure.as_s() >= 0.99f32 && measure.as_s() <= 1.01f32);
assert!(measure.as_ms() >= 990 && measure.as_ms() <= 1_010);
assert!(measure.as_us() >= 999_000 && measure.as_us() <= 1_010_000);
}
// Ensure that the macro can be called with functions
{
let (result, _measure) = measure!(my_multiply(3, 4), "test");
assert_eq!(result, 3 * 4);
let (result, _measure) = measure!(square(5), "test");
assert_eq!(result, 5 * 5)
}
// Ensure that the macro can be called with methods
{
let some_struct = SomeStruct { x: 42 };
let (result, _measure) = measure!(some_struct.add_to(4), "test");
assert_eq!(result, 42 + 4);
}
// Ensure that the macro can be called with blocks
{
let (result, _measure) = measure!({ 1 + 2 }, "test");
assert_eq!(result, 3);
}
// Ensure that the macro can be called with a trailing comma
{
let (result, _measure) = measure!(square(5), "test",);
assert_eq!(result, 5 * 5)
}
// Ensure that the macro can be called without a name
{
let (result, _measure) = measure!(square(5));
assert_eq!(result, 5 * 5)
}
}
#[test]
fn test_measure_us_macro() {
// Ensure that the measurement side actually works
{
let (_result, measure) = measure_us!(sleep(Duration::from_secs(1)));
assert!((999_000..=1_010_000).contains(&measure));
}
// Ensure that the macro can be called with functions
{
let (result, _measure) = measure_us!(my_multiply(3, 4));
assert_eq!(result, 3 * 4);
let (result, _measure) = measure_us!(square(5));
assert_eq!(result, 5 * 5)
}
// Ensure that the macro can be called with methods
{
let some_struct = SomeStruct { x: 42 };
let (result, _measure) = measure_us!(some_struct.add_to(4));
assert_eq!(result, 42 + 4);
}
// Ensure that the macro can be called with blocks
{
let (result, _measure) = measure_us!({ 1 + 2 });
assert_eq!(result, 3);
}
}
}