Skip to main content

qubit_clock/meter/
nano_time_meter.rs

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