prism3_clock/meter/
nano_time_meter.rs

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