diff --git a/measure/src/measure.rs b/measure/src/measure.rs index b44fb6ec03..3b34260523 100644 --- a/measure/src/measure.rs +++ b/measure/src/measure.rs @@ -35,6 +35,41 @@ impl Measure { pub fn as_s(&self) -> f32 { self.duration as f32 / (1000.0f32 * 1000.0f32 * 1000.0f32) } + + /// Measure this function + /// + /// Use `Measure::this()` when you have a function that you want to measure. `this()` will + /// start a new `Measure`, call your function, stop the measure, then return the `Measure` + /// object along with your function's return value. + /// + /// If your function takes more than one parameter, you will need to wrap your function in a + /// closure, and wrap the arguments in a tuple. The same thing applies to methods. See the + /// tests for more details. + /// + /// # Examples + /// + /// ```ignore + /// // Call a function with a single argument + /// let (result, measure) = Measure::this(my_function, fizz, "my_func"); + + /// // Call a function with multiple arguments + /// let (result, measure) = Measure::this(|(arg1, arg2)| my_function(arg1, arg2), ("abc", 123), "my_func"); + /// ``` + /// + /// ```ignore + /// /// Call a method + /// struct Foo { ... } + /// impl Foo { fn bar(&self, some_arg: i32) { ... } } + /// + /// let foo = Foo { }; + /// let (result, measure) = Measure::this(|this, arg| Foo::bar(&this, arg), (&foo, arg), "bar"); + /// ``` + pub fn this R>(func: F, args: T, name: &'static str) -> (R, Self) { + let mut measure = Self::start(name); + let result = func(args); + measure.stop(); + (result, measure) + } } impl fmt::Display for Measure { @@ -102,4 +137,91 @@ mod tests { let measure = Measure::start("test_not_stopped"); assert_eq!(format!("{}", measure), "test_not_stopped running"); } + + fn my_multiply(x: i32, y: i32) -> i32 { + x * y + } + + fn my_multiply_tuple(args: (i32, i32)) -> i32 { + let (x, y) = args; + my_multiply(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_with() { + // Ensure that the measurement side actually works + { + let (_result, measure) = Measure::this(|s| sleep(Duration::from_secs(s)), 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 this() can be called with a simple closure + { + let expected = 1; + let (actual, _measure) = Measure::this(|x| x, expected, "test"); + assert_eq!(actual, expected); + } + + // Ensure that this() can be called with a tuple + { + let (result, _measure) = Measure::this(|(x, y)| x + y, (1, 2), "test"); + assert_eq!(result, 1 + 2); + } + + // Ensure that this() can be called with a normal function + { + let (result, _measure) = Measure::this(|(x, y)| my_multiply(x, y), (3, 4), "test"); + assert_eq!(result, 3 * 4); + } + + // Ensure that this() can be called with a normal function with one argument + { + let (result, _measure) = Measure::this(square, 5, "test"); + assert_eq!(result, 5 * 5) + } + + // Ensure that this() can be called with a normal function + { + let (result, _measure) = Measure::this(my_multiply_tuple, (3, 4), "test"); + assert_eq!(result, 3 * 4); + } + + // Ensure that this() can be called with a method (and self) + { + let some_struct = SomeStruct { x: 42 }; + let (result, _measure) = Measure::this( + |(obj, x)| SomeStruct::add_to(&obj, x), + (some_struct, 4), + "test", + ); + assert_eq!(result, 42 + 4); + } + + // Ensure that this() can be called with a method (and &self) + { + let some_struct = SomeStruct { x: 42 }; + let (result, _measure) = Measure::this( + |(obj, x)| SomeStruct::add_to(&obj, x), + (&some_struct, 4), + "test", + ); + assert_eq!(result, 42 + 4); + assert_eq!(some_struct.add_to(6), 42 + 6); + } + } }