From 0dec3a71494c69d8b8f05ab81b0778d88afb69ac Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Thu, 25 May 2023 11:02:31 -0400 Subject: [PATCH 1/2] Clock.delta_as_nanos, a delta that skips conversion to Duration This addresses https://github.com/metrics-rs/quanta/issues/84 and should results in a benchmark that's 2ns faster (on a m1 max macOS machine) than the corresponding Duration-converting benchmark. --- benches/timing.rs | 10 ++++++++++ src/lib.rs | 35 ++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/benches/timing.rs b/benches/timing.rs index fb36ead..10f4e07 100644 --- a/benches/timing.rs +++ b/benches/timing.rs @@ -43,6 +43,15 @@ fn time_quanta_raw_delta(b: &mut Bencher) { }) } +fn time_quanta_raw_delta_as_nanos(b: &mut Bencher) { + let clock = Clock::new(); + b.iter(|| { + let start = clock.raw(); + let end = clock.raw(); + clock.delta_as_nanos(start, end) + }) +} + fn time_quanta_now_delta(b: &mut Bencher) { let clock = Clock::new(); b.iter(|| { @@ -75,6 +84,7 @@ fn benchmark(c: &mut Criterion) { q_group.bench_function("quanta_raw", time_quanta_raw); q_group.bench_function("quanta_raw_scaled", time_quanta_raw_scaled); q_group.bench_function("quanta_raw_delta", time_quanta_raw_delta); + q_group.bench_function("quanta_raw_delta_as_nanos", time_quanta_raw_delta_as_nanos); q_group.bench_function("quanta_recent", time_quanta_recent); q_group.bench_function("quanta_instant_recent", time_quanta_instant_recent); q_group.finish(); diff --git a/src/lib.rs b/src/lib.rs index 463d66b..d052316 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -388,29 +388,46 @@ impl Clock { Instant(scaled) } - /// Calculates the delta between two measurements, and scales to reference time. + /// Calculates the delta in nanoseconds scaled to reference time in nanoseconds + /// between two measurements. /// - /// This method is slightly faster when you know you need the delta between two raw - /// measurements, or a start/end measurement, than using [`scaled`] for both conversions. + /// This method is very similar to [`delta`] but reduces overhead + /// for high-frequency measurements that work with nanosecond + /// counts internally, as it avoids the conversion of the delta + /// into [`Duration`]. /// - /// [`scaled`]: Clock::scaled - pub fn delta(&self, start: u64, end: u64) -> Duration { + /// [`delta`]: Clock::delta + pub fn delta_as_nanos(&self, start: u64, end: u64) -> u64 { // Safety: we want wrapping_sub on the end/start delta calculation so that two measurements // split across a rollover boundary still return the right result. However, we also know // the TSC could potentially give us different values between cores/sockets, so we're just // doing our due diligence here to make sure we're not about to create some wacky duration. if end <= start { - return Duration::new(0, 0); + return 0; } let delta = end.wrapping_sub(start); - let scaled = match &self.inner { + match &self.inner { ClockType::Counter(_, _, calibration) => { mul_div_po2_u64(delta, calibration.scale_factor, calibration.scale_shift) } _ => delta, - }; - Duration::from_nanos(scaled) + } + } + + /// Calculates the delta between two measurements, and scales to reference time. + /// + /// This method is slightly faster when you know you need the delta between two raw + /// measurements, or a start/end measurement, than using [`scaled`] for both conversions. + /// + /// In code that uses `clock.delta(start, end).as_nanos()` with + /// high frequency, consider [`delta_as_nanos`] instead, as that + /// avoids the round-trip conversion through [`Duration`]. + /// + /// [`scaled`]: Clock::scaled + /// [`delta_as_nanos`]: Clock::delta_as_nanos + pub fn delta(&self, start: u64, end: u64) -> Duration { + Duration::from_nanos(self.delta_as_nanos(start, end)) } /// Gets the most recent current time, scaled to reference time. From 391043e3f64e5cee1868e1340b74662d6651f3a8 Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Fri, 26 May 2023 15:41:21 -0400 Subject: [PATCH 2/2] Doc improvements from code review Co-authored-by: Toby Lawrence --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d052316..91d4031 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -388,8 +388,7 @@ impl Clock { Instant(scaled) } - /// Calculates the delta in nanoseconds scaled to reference time in nanoseconds - /// between two measurements. + /// Calculates the delta, in nanoseconds, between two raw measurements. /// /// This method is very similar to [`delta`] but reduces overhead /// for high-frequency measurements that work with nanosecond @@ -415,14 +414,15 @@ impl Clock { } } - /// Calculates the delta between two measurements, and scales to reference time. + /// Calculates the delta between two raw measurements. /// /// This method is slightly faster when you know you need the delta between two raw /// measurements, or a start/end measurement, than using [`scaled`] for both conversions. /// - /// In code that uses `clock.delta(start, end).as_nanos()` with - /// high frequency, consider [`delta_as_nanos`] instead, as that - /// avoids the round-trip conversion through [`Duration`]. + /// In code that simply needs access to the whole number of nanoseconds + /// between the two measurements, consider [`Clock::delta_as_nanos`] + /// instead, which is slightly faster than having to call both this method + /// and [`Duration::as_nanos`]. /// /// [`scaled`]: Clock::scaled /// [`delta_as_nanos`]: Clock::delta_as_nanos