Skip to main content

qubit_clock/meter/
nano_time_meter.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Nanosecond-precision time meter implementation.
11//!
12//! This module provides [`NanoTimeMeter`], a high-precision time
13//! measurement tool with nanosecond precision. For millisecond precision,
14//! use [`TimeMeter`](super::TimeMeter).
15//!
16
17use crate::meter::format::{
18    format_duration_nanos,
19    format_speed,
20};
21use crate::{
22    NanoClock,
23    NanoMonotonicClock,
24};
25use chrono::Duration;
26
27/// The number of nanoseconds in one millisecond.
28const NANOS_PER_MILLISECOND: i128 = 1_000_000;
29
30/// The number of nanoseconds in one second.
31const NANOS_PER_SECOND: i128 = 1_000_000_000;
32
33/// Converts an `i128` value to `i64` using saturation.
34///
35/// Values above `i64::MAX` are clamped to `i64::MAX`, and values below
36/// `i64::MIN` are clamped to `i64::MIN`.
37#[inline]
38fn saturating_i64_from_i128(value: i128) -> i64 {
39    if value > i64::MAX as i128 {
40        i64::MAX
41    } else if value < i64::MIN as i128 {
42        i64::MIN
43    } else {
44        value as i64
45    }
46}
47
48/// Converts nanoseconds to a chrono `Duration`, clamping only at chrono bounds.
49fn duration_from_nanos(nanos: i128) -> Duration {
50    let max_nanos = i128::from(i64::MAX) * NANOS_PER_MILLISECOND;
51    let min_nanos = -i128::from(i64::MAX) * NANOS_PER_MILLISECOND;
52
53    if nanos >= max_nanos {
54        return Duration::MAX;
55    }
56    if nanos <= min_nanos {
57        return Duration::MIN;
58    }
59
60    let seconds = nanos.div_euclid(NANOS_PER_SECOND);
61    let sub_nanos = nanos.rem_euclid(NANOS_PER_SECOND) as u32;
62    Duration::new(seconds as i64, sub_nanos)
63        .expect("nanos within chrono bounds should construct Duration")
64}
65
66/// A time meter for measuring elapsed time with nanosecond precision.
67///
68/// This is the high-precision version of [`TimeMeter`](super::TimeMeter),
69/// specifically designed for scenarios requiring nanosecond-level
70/// precision, such as microbenchmarking and high-frequency operation
71/// performance analysis.
72///
73/// # Differences from TimeMeter
74///
75/// - **Precision**: NanoTimeMeter provides nanosecond precision,
76///   TimeMeter provides millisecond precision
77/// - **Performance**: NanoTimeMeter has slightly higher computational
78///   overhead, but offers higher precision
79/// - **Use Cases**: NanoTimeMeter is suitable for microbenchmarking,
80///   TimeMeter is suitable for general business monitoring
81///
82/// # Thread Safety
83///
84/// This type is not thread-safe. If you need to use it in a
85/// multi-threaded environment, you should create separate instances for
86/// each thread or use external synchronization mechanisms.
87///
88/// # Examples
89///
90/// ```
91/// use qubit_clock::meter::NanoTimeMeter;
92///
93/// // Basic usage
94/// let mut meter = NanoTimeMeter::new();
95/// meter.start();
96/// // Perform high-frequency operations
97/// meter.stop();
98/// println!("Elapsed: {} nanos", meter.nanos());
99/// println!("Elapsed: {}", meter.readable_duration());
100/// ```
101///
102pub struct NanoTimeMeter<C: NanoClock> {
103    /// The clock used by this meter.
104    clock: C,
105    /// Start timestamp in nanoseconds. `None` means not started.
106    /// Uses i128 to avoid overflow.
107    start_time: Option<i128>,
108    /// End timestamp in nanoseconds. `None` means not stopped.
109    /// Uses i128 to avoid overflow.
110    end_time: Option<i128>,
111}
112
113impl<C: NanoClock> NanoTimeMeter<C> {
114    /// Creates a new nano time meter with the specified clock.
115    ///
116    /// # Arguments
117    ///
118    /// * `clock` - The clock to use for time measurement
119    ///
120    /// # Returns
121    ///
122    /// A new `NanoTimeMeter` instance
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// use qubit_clock::{NanoMonotonicClock, meter::NanoTimeMeter};
128    ///
129    /// let clock = NanoMonotonicClock::new();
130    /// let meter = NanoTimeMeter::with_clock(clock);
131    /// ```
132    #[inline]
133    pub fn with_clock(clock: C) -> Self {
134        NanoTimeMeter {
135            clock,
136            start_time: None,
137            end_time: None,
138        }
139    }
140
141    /// Creates a new nano time meter with the specified clock and starts
142    /// it immediately.
143    ///
144    /// # Arguments
145    ///
146    /// * `clock` - The clock to use for time measurement
147    ///
148    /// # Returns
149    ///
150    /// A new `NanoTimeMeter` instance that has already been started
151    ///
152    /// # Examples
153    ///
154    /// ```
155    /// use qubit_clock::{NanoMonotonicClock, meter::NanoTimeMeter};
156    ///
157    /// let clock = NanoMonotonicClock::new();
158    /// let meter = NanoTimeMeter::with_clock_started(clock);
159    /// ```
160    #[inline]
161    pub fn with_clock_started(clock: C) -> Self {
162        let mut meter = Self::with_clock(clock);
163        meter.start();
164        meter
165    }
166
167    /// Starts this meter.
168    ///
169    /// Records the current time as the start timestamp. If the meter has
170    /// already been started, this operation will restart timing.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use qubit_clock::meter::NanoTimeMeter;
176    ///
177    /// let mut meter = NanoTimeMeter::new();
178    /// meter.start();
179    /// ```
180    #[inline]
181    pub fn start(&mut self) {
182        self.start_time = Some(self.clock.nanos());
183        self.end_time = None;
184    }
185
186    /// Stops this meter.
187    ///
188    /// Records the current time as the end timestamp. After calling this
189    /// method, `duration()` will return a fixed time interval until
190    /// `start()` or `reset()` is called again.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use qubit_clock::meter::NanoTimeMeter;
196    ///
197    /// let mut meter = NanoTimeMeter::start_now();
198    /// // Do some work
199    /// meter.stop();
200    /// ```
201    #[inline]
202    pub fn stop(&mut self) {
203        self.end_time = Some(self.clock.nanos());
204    }
205
206    /// Resets this meter.
207    ///
208    /// Clears the start and end timestamps, restoring the meter to its
209    /// initial state. After reset, you need to call `start()` again to
210    /// begin a new time measurement.
211    ///
212    /// # Examples
213    ///
214    /// ```
215    /// use qubit_clock::meter::NanoTimeMeter;
216    ///
217    /// let mut meter = NanoTimeMeter::start_now();
218    /// // Do some work
219    /// meter.stop();
220    /// meter.reset();
221    /// ```
222    #[inline]
223    pub fn reset(&mut self) {
224        self.start_time = None;
225        self.end_time = None;
226    }
227
228    /// Resets and immediately starts this meter.
229    ///
230    /// This is equivalent to calling `reset()` followed by `start()`.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use qubit_clock::meter::NanoTimeMeter;
236    ///
237    /// let mut meter = NanoTimeMeter::start_now();
238    /// // Do some work
239    /// meter.restart();
240    /// // Do more work
241    /// ```
242    #[inline]
243    pub fn restart(&mut self) {
244        self.reset();
245        self.start();
246    }
247
248    /// Returns the elapsed duration in nanoseconds.
249    ///
250    /// If the meter has been stopped (by calling `stop()`), returns the
251    /// time interval from start to stop. If the meter has not been
252    /// stopped, returns the time interval from start to the current
253    /// moment.
254    ///
255    /// If the meter has not been started (by calling `start()`), returns
256    /// 0.
257    ///
258    /// To avoid overflow issues, this method uses safe arithmetic
259    /// operations.
260    ///
261    /// # Returns
262    ///
263    /// The elapsed duration in nanoseconds
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// use qubit_clock::meter::NanoTimeMeter;
269    ///
270    /// let mut meter = NanoTimeMeter::start_now();
271    /// // Do some work
272    /// assert!(meter.nanos() > 0);
273    /// ```
274    #[inline]
275    pub fn nanos(&self) -> i128 {
276        let start = match self.start_time {
277            Some(t) => t,
278            None => return 0,
279        };
280        let end = self.end_time.unwrap_or_else(|| self.clock.nanos());
281        end.saturating_sub(start)
282    }
283
284    /// Returns the elapsed duration in microseconds.
285    ///
286    /// This method is based on the result of `nanos()`, converting
287    /// nanoseconds to microseconds.
288    ///
289    /// # Returns
290    ///
291    /// The elapsed duration in microseconds
292    ///
293    /// # Examples
294    ///
295    /// ```
296    /// use qubit_clock::meter::NanoTimeMeter;
297    ///
298    /// let mut meter = NanoTimeMeter::start_now();
299    /// // Do some work
300    /// assert!(meter.micros() >= 0);
301    /// ```
302    #[inline]
303    pub fn micros(&self) -> i128 {
304        self.nanos() / 1_000
305    }
306
307    /// Returns the elapsed duration in milliseconds.
308    ///
309    /// This method is based on the result of `nanos()`, converting
310    /// nanoseconds to whole milliseconds by truncating toward zero.
311    ///
312    /// If the meter has not been started (by calling `start()`), returns
313    /// 0.
314    ///
315    /// # Returns
316    ///
317    /// The elapsed duration in milliseconds
318    ///
319    /// # Examples
320    ///
321    /// ```
322    /// use qubit_clock::meter::NanoTimeMeter;
323    /// use std::thread;
324    /// use std::time::Duration;
325    ///
326    /// let mut meter = NanoTimeMeter::start_now();
327    /// thread::sleep(Duration::from_millis(100));
328    /// assert!(meter.millis() >= 100);
329    /// ```
330    #[inline]
331    pub fn millis(&self) -> i64 {
332        saturating_i64_from_i128(self.nanos() / NANOS_PER_MILLISECOND)
333    }
334
335    /// Returns the elapsed duration in seconds.
336    ///
337    /// This method is based on the result of `nanos()`, converting
338    /// nanoseconds to whole seconds by truncating toward zero.
339    ///
340    /// # Returns
341    ///
342    /// The elapsed duration in seconds
343    ///
344    /// # Examples
345    ///
346    /// ```
347    /// use qubit_clock::meter::NanoTimeMeter;
348    /// use std::thread;
349    /// use std::time::Duration;
350    ///
351    /// let mut meter = NanoTimeMeter::start_now();
352    /// thread::sleep(Duration::from_secs(1));
353    /// assert!(meter.seconds() >= 1);
354    /// ```
355    #[inline]
356    pub fn seconds(&self) -> i64 {
357        saturating_i64_from_i128(self.nanos() / NANOS_PER_SECOND)
358    }
359
360    /// Returns the elapsed duration in minutes.
361    ///
362    /// This method is based on the result of `nanos()`, converting
363    /// nanoseconds to whole minutes by truncating toward zero.
364    ///
365    /// # Returns
366    ///
367    /// The elapsed duration in minutes
368    ///
369    /// # Examples
370    ///
371    /// ```
372    /// use qubit_clock::meter::NanoTimeMeter;
373    ///
374    /// let mut meter = NanoTimeMeter::new();
375    /// meter.start();
376    /// // Simulate some time
377    /// meter.stop();
378    /// ```
379    #[inline]
380    pub fn minutes(&self) -> i64 {
381        saturating_i64_from_i128(self.nanos() / (60 * NANOS_PER_SECOND))
382    }
383
384    /// Returns the elapsed duration as a `Duration` object.
385    ///
386    /// If the meter has been stopped (by calling `stop()`), returns the
387    /// time interval from start to stop. If the meter has not been
388    /// stopped, returns the time interval from start to the current
389    /// moment.
390    ///
391    /// If the meter has not been started (by calling `start()`), returns
392    /// a zero duration.
393    ///
394    /// The returned `Duration` object has nanosecond precision.
395    ///
396    /// # Returns
397    ///
398    /// The elapsed duration as a `Duration` object (nanosecond precision)
399    ///
400    /// # Examples
401    ///
402    /// ```
403    /// use qubit_clock::meter::NanoTimeMeter;
404    ///
405    /// let mut meter = NanoTimeMeter::start_now();
406    /// let duration = meter.duration();
407    /// ```
408    #[inline]
409    pub fn duration(&self) -> Duration {
410        duration_from_nanos(self.nanos())
411    }
412
413    /// Returns a human-readable string representation of the elapsed
414    /// duration.
415    ///
416    /// Formats the duration into an easy-to-read string, such as
417    /// "1h 23m 45s" or "2.5s".
418    ///
419    /// # Returns
420    ///
421    /// A human-readable string representation of the duration
422    ///
423    /// # Examples
424    ///
425    /// ```
426    /// use qubit_clock::meter::NanoTimeMeter;
427    ///
428    /// let mut meter = NanoTimeMeter::start_now();
429    /// // Do some work
430    /// meter.stop();
431    /// println!("Elapsed: {}", meter.readable_duration());
432    /// ```
433    #[inline]
434    pub fn readable_duration(&self) -> String {
435        format_duration_nanos(self.nanos())
436    }
437
438    /// Calculates the per-second speed for a given count.
439    ///
440    /// Computes the average count processed per second during the elapsed
441    /// time. Useful for performance monitoring and speed analysis.
442    ///
443    /// # Arguments
444    ///
445    /// * `count` - The count value to calculate speed for
446    ///
447    /// # Returns
448    ///
449    /// The per-second speed, or `None` if the elapsed time is zero
450    ///
451    /// # Examples
452    ///
453    /// ```
454    /// use qubit_clock::meter::NanoTimeMeter;
455    /// use std::thread;
456    /// use std::time::Duration;
457    ///
458    /// let mut meter = NanoTimeMeter::start_now();
459    /// thread::sleep(Duration::from_secs(1));
460    /// meter.stop();
461    /// if let Some(speed) = meter.speed_per_second(1000) {
462    ///     println!("Speed: {:.2} items/s", speed);
463    /// }
464    /// ```
465    #[inline]
466    pub fn speed_per_second(&self, count: usize) -> Option<f64> {
467        let elapsed_nanos = self.nanos();
468        if elapsed_nanos <= 0 {
469            None
470        } else {
471            Some((count as f64 * 1_000_000_000.0) / elapsed_nanos as f64)
472        }
473    }
474
475    /// Calculates the per-minute speed for a given count.
476    ///
477    /// Computes the average count processed per minute during the elapsed
478    /// time. Useful for performance monitoring and speed analysis.
479    ///
480    /// # Arguments
481    ///
482    /// * `count` - The count value to calculate speed for
483    ///
484    /// # Returns
485    ///
486    /// The per-minute speed, or `None` if the elapsed time is zero
487    ///
488    /// # Examples
489    ///
490    /// ```
491    /// use qubit_clock::meter::NanoTimeMeter;
492    /// use std::thread;
493    /// use std::time::Duration;
494    ///
495    /// let mut meter = NanoTimeMeter::start_now();
496    /// thread::sleep(Duration::from_secs(1));
497    /// meter.stop();
498    /// if let Some(speed) = meter.speed_per_minute(1000) {
499    ///     println!("Speed: {:.2} items/m", speed);
500    /// }
501    /// ```
502    #[inline]
503    pub fn speed_per_minute(&self, count: usize) -> Option<f64> {
504        let elapsed_nanos = self.nanos();
505        if elapsed_nanos <= 0 {
506            None
507        } else {
508            Some((count as f64 * 60_000_000_000.0) / elapsed_nanos as f64)
509        }
510    }
511
512    /// Returns a formatted string of the per-second speed for a given
513    /// count.
514    ///
515    /// # Arguments
516    ///
517    /// * `count` - The count value to calculate speed for
518    ///
519    /// # Returns
520    ///
521    /// A string in the format "{speed}/s", or "N/A" if the elapsed time
522    /// is zero
523    ///
524    /// # Examples
525    ///
526    /// ```
527    /// use qubit_clock::meter::NanoTimeMeter;
528    ///
529    /// let mut meter = NanoTimeMeter::start_now();
530    /// // Do some work
531    /// meter.stop();
532    /// println!("Speed: {}", meter.formatted_speed_per_second(1000));
533    /// ```
534    #[inline]
535    pub fn formatted_speed_per_second(&self, count: usize) -> String {
536        match self.speed_per_second(count) {
537            Some(speed) => format_speed(speed, "/s"),
538            None => "N/A".to_string(),
539        }
540    }
541
542    /// Returns a formatted string of the per-minute speed for a given
543    /// count.
544    ///
545    /// # Arguments
546    ///
547    /// * `count` - The count value to calculate speed for
548    ///
549    /// # Returns
550    ///
551    /// A string in the format "{speed}/m", or "N/A" if the elapsed time
552    /// is zero
553    ///
554    /// # Examples
555    ///
556    /// ```
557    /// use qubit_clock::meter::NanoTimeMeter;
558    ///
559    /// let mut meter = NanoTimeMeter::start_now();
560    /// // Do some work
561    /// meter.stop();
562    /// println!("Speed: {}", meter.formatted_speed_per_minute(1000));
563    /// ```
564    #[inline]
565    pub fn formatted_speed_per_minute(&self, count: usize) -> String {
566        match self.speed_per_minute(count) {
567            Some(speed) => format_speed(speed, "/m"),
568            None => "N/A".to_string(),
569        }
570    }
571
572    /// Checks if the meter is currently running.
573    ///
574    /// # Returns
575    ///
576    /// `true` if the meter has been started but not stopped, `false`
577    /// otherwise
578    ///
579    /// # Examples
580    ///
581    /// ```
582    /// use qubit_clock::meter::NanoTimeMeter;
583    ///
584    /// let mut meter = NanoTimeMeter::new();
585    /// assert!(!meter.is_running());
586    /// meter.start();
587    /// assert!(meter.is_running());
588    /// meter.stop();
589    /// assert!(!meter.is_running());
590    /// ```
591    #[inline]
592    pub fn is_running(&self) -> bool {
593        self.start_time.is_some() && self.end_time.is_none()
594    }
595
596    /// Checks if the meter has been stopped.
597    ///
598    /// # Returns
599    ///
600    /// `true` if the meter has been stopped, `false` otherwise
601    ///
602    /// # Examples
603    ///
604    /// ```
605    /// use qubit_clock::meter::NanoTimeMeter;
606    ///
607    /// let mut meter = NanoTimeMeter::start_now();
608    /// assert!(!meter.is_stopped());
609    /// meter.stop();
610    /// assert!(meter.is_stopped());
611    /// ```
612    #[inline]
613    pub fn is_stopped(&self) -> bool {
614        self.end_time.is_some()
615    }
616
617    /// Returns a reference to the clock used by this meter.
618    ///
619    /// # Returns
620    ///
621    /// A reference to the clock
622    ///
623    /// # Examples
624    ///
625    /// ```
626    /// use qubit_clock::meter::NanoTimeMeter;
627    ///
628    /// let meter = NanoTimeMeter::new();
629    /// let clock = meter.clock();
630    /// ```
631    #[inline]
632    pub fn clock(&self) -> &C {
633        &self.clock
634    }
635
636    /// Returns a mutable reference to the clock used by this meter.
637    ///
638    /// # Returns
639    ///
640    /// A mutable reference to the clock
641    ///
642    /// # Examples
643    ///
644    /// ```
645    /// use qubit_clock::meter::NanoTimeMeter;
646    ///
647    /// let mut meter = NanoTimeMeter::new();
648    /// let clock = meter.clock_mut();
649    /// ```
650    #[inline]
651    pub fn clock_mut(&mut self) -> &mut C {
652        &mut self.clock
653    }
654}
655
656impl NanoTimeMeter<NanoMonotonicClock> {
657    /// Creates a new nano time meter using the default
658    /// `NanoMonotonicClock`.
659    ///
660    /// The default clock uses `NanoMonotonicClock`, which is based on
661    /// `Instant` and is not affected by system time adjustments, making
662    /// it more suitable for high-precision time measurement.
663    ///
664    /// # Returns
665    ///
666    /// A new `NanoTimeMeter` instance
667    ///
668    /// # Examples
669    ///
670    /// ```
671    /// use qubit_clock::meter::NanoTimeMeter;
672    ///
673    /// let meter = NanoTimeMeter::new();
674    /// ```
675    #[inline]
676    pub fn new() -> Self {
677        Self::with_clock(NanoMonotonicClock::new())
678    }
679
680    /// Creates a new nano time meter using the default
681    /// `NanoMonotonicClock` and starts it immediately.
682    ///
683    /// # Returns
684    ///
685    /// A new `NanoTimeMeter` instance that has already been started
686    ///
687    /// # Examples
688    ///
689    /// ```
690    /// use qubit_clock::meter::NanoTimeMeter;
691    ///
692    /// let meter = NanoTimeMeter::start_now();
693    /// ```
694    #[inline]
695    pub fn start_now() -> Self {
696        Self::with_clock_started(NanoMonotonicClock::new())
697    }
698}
699
700impl Default for NanoTimeMeter<NanoMonotonicClock> {
701    #[inline]
702    fn default() -> Self {
703        Self::new()
704    }
705}