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}