Переглянути джерело

Add Measure::this() (#16953)

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.
Brooks Prumo 4 роки тому
батько
коміт
7e5964e339
1 змінених файлів з 122 додано та 0 видалено
  1. 122 0
      measure/src/measure.rs

+ 122 - 0
measure/src/measure.rs

@@ -35,6 +35,41 @@ impl Measure {
     pub fn as_s(&self) -> f32 {
     pub fn as_s(&self) -> f32 {
         self.duration as f32 / (1000.0f32 * 1000.0f32 * 1000.0f32)
         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<T, R, F: FnOnce(T) -> 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 {
 impl fmt::Display for Measure {
@@ -102,4 +137,91 @@ mod tests {
         let measure = Measure::start("test_not_stopped");
         let measure = Measure::start("test_not_stopped");
         assert_eq!(format!("{}", measure), "test_not_stopped running");
         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);
+        }
+    }
 }
 }