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}