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