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}