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