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