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}