1use crate::{MetricsError, Result};
14use std::sync::atomic::{AtomicU64, Ordering};
15use std::time::{Duration, Instant};
16
17#[repr(align(64))]
22pub struct Timer {
23 count: AtomicU64,
25 total_nanos: AtomicU64,
27 min_nanos: AtomicU64,
29 max_nanos: AtomicU64,
31 created_at: Instant,
33}
34
35pub struct RunningTimer<'a> {
39 timer: &'a Timer,
40 start_time: Instant,
41 stopped: bool,
42}
43
44#[derive(Debug, Clone)]
46pub struct TimerStats {
47 pub count: u64,
49 pub total: Duration,
51 pub average: Duration,
53 pub min: Duration,
55 pub max: Duration,
57 pub age: Duration,
59 pub rate_per_second: f64,
61}
62
63impl Timer {
64 #[inline]
66 pub fn new() -> Self {
67 Self {
68 count: AtomicU64::new(0),
69 total_nanos: AtomicU64::new(0),
70 min_nanos: AtomicU64::new(u64::MAX),
71 max_nanos: AtomicU64::new(0),
72 created_at: Instant::now(),
73 }
74 }
75
76 #[inline(always)]
91 pub fn try_record_ns(&self, duration_ns: u64) -> Result<()> {
92 let total = self.total_nanos.load(Ordering::Relaxed);
94 if total.checked_add(duration_ns).is_none() {
95 return Err(MetricsError::Overflow);
96 }
97
98 let cnt = self.count.load(Ordering::Relaxed);
100 if cnt == u64::MAX {
101 return Err(MetricsError::Overflow);
102 }
103
104 self.total_nanos.fetch_add(duration_ns, Ordering::Relaxed);
106 self.count.fetch_add(1, Ordering::Relaxed);
107
108 self.update_min(duration_ns);
110 self.update_max(duration_ns);
111 Ok(())
112 }
113
114 #[inline(always)]
116 pub fn start(&self) -> RunningTimer<'_> {
117 RunningTimer {
118 timer: self,
119 start_time: Instant::now(),
120 stopped: false,
121 }
122 }
123
124 #[inline]
126 pub fn record(&self, duration: Duration) {
127 let duration_ns = duration.as_nanos() as u64;
128 self.record_ns(duration_ns);
129 }
130
131 #[inline]
144 pub fn try_record(&self, duration: Duration) -> Result<()> {
145 let duration_ns = duration.as_nanos() as u64;
146 self.try_record_ns(duration_ns)
147 }
148
149 #[inline(always)]
151 pub fn record_ns(&self, duration_ns: u64) {
152 self.total_nanos.fetch_add(duration_ns, Ordering::Relaxed);
154 self.count.fetch_add(1, Ordering::Relaxed);
155
156 self.update_min(duration_ns);
158
159 self.update_max(duration_ns);
161 }
162
163 #[inline]
165 pub fn record_batch(&self, durations: &[Duration]) {
166 if durations.is_empty() {
167 return;
168 }
169
170 let mut total_ns: u64 = 0;
171 let mut local_min = u64::MAX;
172 let mut local_max = 0u64;
173
174 for duration in durations {
175 let ns = duration.as_nanos() as u64;
176 total_ns = total_ns.saturating_add(ns);
180 local_min = local_min.min(ns);
181 local_max = local_max.max(ns);
182 }
183
184 self.total_nanos.fetch_add(total_ns, Ordering::Relaxed);
185 self.count
186 .fetch_add(durations.len() as u64, Ordering::Relaxed);
187
188 if local_min < u64::MAX {
189 self.update_min(local_min);
190 }
191 if local_max > 0 {
192 self.update_max(local_max);
193 }
194 }
195
196 #[inline]
210 pub fn try_record_batch(&self, durations: &[Duration]) -> Result<()> {
211 if durations.is_empty() {
212 return Ok(());
213 }
214
215 let mut total_ns: u64 = 0;
216 let mut local_min = u64::MAX;
217 let mut local_max = 0u64;
218
219 for d in durations {
220 let ns = d.as_nanos() as u64;
221 total_ns = total_ns.checked_add(ns).ok_or(MetricsError::Overflow)?;
222 local_min = local_min.min(ns);
223 local_max = local_max.max(ns);
224 }
225
226 let current_total = self.total_nanos.load(Ordering::Relaxed);
228 if current_total.checked_add(total_ns).is_none() {
229 return Err(MetricsError::Overflow);
230 }
231 let current_count = self.count.load(Ordering::Relaxed);
232 let add_count = durations.len() as u64;
233 if current_count.checked_add(add_count).is_none() {
234 return Err(MetricsError::Overflow);
235 }
236
237 self.total_nanos.fetch_add(total_ns, Ordering::Relaxed);
239 self.count.fetch_add(add_count, Ordering::Relaxed);
240 if local_min < u64::MAX {
241 self.update_min(local_min);
242 }
243 if local_max > 0 {
244 self.update_max(local_max);
245 }
246 Ok(())
247 }
248
249 #[must_use]
254 #[inline(always)]
255 pub fn count(&self) -> u64 {
256 self.count.load(Ordering::Relaxed)
257 }
258
259 #[must_use]
264 #[inline]
265 pub fn total(&self) -> Duration {
266 Duration::from_nanos(self.total_nanos.load(Ordering::Relaxed))
267 }
268
269 #[must_use]
274 #[inline]
275 pub fn average(&self) -> Duration {
276 let count = self.count();
277 let total_ns = self.total_nanos.load(Ordering::Relaxed);
278 total_ns
279 .checked_div(count)
280 .map(Duration::from_nanos)
281 .unwrap_or(Duration::ZERO)
282 }
283
284 #[must_use]
288 #[inline]
289 pub fn min(&self) -> Duration {
290 let min_ns = self.min_nanos.load(Ordering::Relaxed);
291 if min_ns == u64::MAX {
292 Duration::ZERO
293 } else {
294 Duration::from_nanos(min_ns)
295 }
296 }
297
298 #[must_use]
302 #[inline]
303 pub fn max(&self) -> Duration {
304 Duration::from_nanos(self.max_nanos.load(Ordering::Relaxed))
305 }
306
307 #[inline]
309 pub fn reset(&self) {
310 self.total_nanos.store(0, Ordering::SeqCst);
311 self.count.store(0, Ordering::SeqCst);
312 self.min_nanos.store(u64::MAX, Ordering::SeqCst);
313 self.max_nanos.store(0, Ordering::SeqCst);
314 }
315
316 #[must_use]
321 pub fn stats(&self) -> TimerStats {
322 let count = self.count();
323 let total_ns = self.total_nanos.load(Ordering::Relaxed);
324 let min_ns = self.min_nanos.load(Ordering::Relaxed);
325 let max_ns = self.max_nanos.load(Ordering::Relaxed);
326
327 let total = Duration::from_nanos(total_ns);
328 let average = total_ns
329 .checked_div(count)
330 .map(Duration::from_nanos)
331 .unwrap_or(Duration::ZERO);
332
333 let min = if min_ns == u64::MAX {
334 Duration::ZERO
335 } else {
336 Duration::from_nanos(min_ns)
337 };
338
339 let max = Duration::from_nanos(max_ns);
340 let age = self.created_at.elapsed();
341
342 let rate_per_second = if age.as_secs_f64() > 0.0 {
343 count as f64 / age.as_secs_f64()
344 } else {
345 0.0
346 };
347
348 TimerStats {
349 count,
350 total,
351 average,
352 min,
353 max,
354 age,
355 rate_per_second,
356 }
357 }
358
359 #[must_use]
364 #[inline]
365 pub fn age(&self) -> Duration {
366 self.created_at.elapsed()
367 }
368
369 #[must_use]
373 #[inline]
374 pub fn is_empty(&self) -> bool {
375 self.count() == 0
376 }
377
378 #[must_use]
382 #[inline]
383 pub fn rate_per_second(&self) -> f64 {
384 let age_seconds = self.age().as_secs_f64();
385 if age_seconds > 0.0 {
386 self.count() as f64 / age_seconds
387 } else {
388 0.0
389 }
390 }
391
392 #[inline(always)]
395 fn update_min(&self, value: u64) {
396 loop {
397 let current = self.min_nanos.load(Ordering::Relaxed);
398 if value >= current {
399 break; }
401
402 match self.min_nanos.compare_exchange_weak(
403 current,
404 value,
405 Ordering::Relaxed,
406 Ordering::Relaxed,
407 ) {
408 Ok(_) => break,
409 Err(_) => continue, }
411 }
412 }
413
414 #[inline(always)]
415 fn update_max(&self, value: u64) {
416 loop {
417 let current = self.max_nanos.load(Ordering::Relaxed);
418 if value <= current {
419 break; }
421
422 match self.max_nanos.compare_exchange_weak(
423 current,
424 value,
425 Ordering::Relaxed,
426 Ordering::Relaxed,
427 ) {
428 Ok(_) => break,
429 Err(_) => continue, }
431 }
432 }
433}
434
435impl Default for Timer {
436 #[inline]
437 fn default() -> Self {
438 Self::new()
439 }
440}
441
442impl std::fmt::Display for Timer {
443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444 let stats = self.stats();
445 write!(f, "Timer(count: {}, avg: {:?})", stats.count, stats.average)
446 }
447}
448
449impl std::fmt::Debug for Timer {
450 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451 let stats = self.stats();
452 f.debug_struct("Timer")
453 .field("count", &stats.count)
454 .field("total", &stats.total)
455 .field("average", &stats.average)
456 .field("min", &stats.min)
457 .field("max", &stats.max)
458 .field("rate_per_second", &stats.rate_per_second)
459 .finish()
460 }
461}
462
463impl<'a> RunningTimer<'a> {
468 #[must_use]
473 #[inline]
474 pub fn elapsed(&self) -> Duration {
475 self.start_time.elapsed()
476 }
477
478 #[inline]
480 pub fn stop(mut self) {
481 if !self.stopped {
482 let elapsed = self.start_time.elapsed();
483 self.timer.record(elapsed);
484 self.stopped = true;
485 }
486 }
488}
489
490impl<'a> Drop for RunningTimer<'a> {
491 #[inline]
492 fn drop(&mut self) {
493 if !self.stopped {
494 let elapsed = self.start_time.elapsed();
495 self.timer.record(elapsed);
496 }
497 }
498}
499
500pub mod utils {
502 use super::*;
503
504 #[inline]
506 pub fn time_fn<T>(f: impl FnOnce() -> T) -> (T, Duration) {
507 let start = Instant::now();
508 let result = f();
509 let duration = start.elapsed();
510 (result, duration)
511 }
512
513 #[inline]
515 pub fn time_and_record<T>(timer: &Timer, f: impl FnOnce() -> T) -> T {
516 let _timing_guard = timer.start();
517 f() }
519
520 #[inline]
522 pub fn scoped_timer(timer: &Timer) -> RunningTimer<'_> {
523 timer.start()
524 }
525
526 pub fn benchmark<F>(name: &str, iterations: usize, f: F) -> Duration
528 where
529 F: Fn(),
530 {
531 let timer = Timer::new();
532
533 for _ in 0..iterations {
534 let _guard = timer.start();
535 f();
536 }
537
538 let stats = timer.stats();
539 println!(
540 "Benchmark '{}': {} iterations, avg: {:?}, total: {:?}",
541 name, iterations, stats.average, stats.total
542 );
543
544 stats.average
545 }
546}
547
548#[macro_export]
550macro_rules! time_block {
551 ($timer:expr, $block:block) => {{
552 let _timing_guard = $timer.start();
553 $block
554 }};
555}
556
557#[macro_export]
558macro_rules! time_fn {
573 ($func:expr) => {{
574 let start = std::time::Instant::now();
575 let result = $func;
576 let duration = start.elapsed();
577 (result, duration)
578 }};
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584 use std::sync::Arc;
585 use std::thread;
586
587 #[test]
588 fn test_basic_operations() {
589 let timer = Timer::new();
590
591 assert!(timer.is_empty());
592 assert_eq!(timer.count(), 0);
593 assert_eq!(timer.total(), Duration::ZERO);
594 assert_eq!(timer.average(), Duration::ZERO);
595
596 timer.record(Duration::from_millis(100));
598
599 assert!(!timer.is_empty());
600 assert_eq!(timer.count(), 1);
601 assert!(timer.total() >= Duration::from_millis(99)); assert!(timer.average() >= Duration::from_millis(99));
603 }
604
605 #[test]
606 fn test_running_timer() {
607 let timer = Timer::new();
608
609 {
610 let running = timer.start();
611 thread::sleep(Duration::from_millis(10));
612 assert!(running.elapsed() >= Duration::from_millis(9));
613 } assert_eq!(timer.count(), 1);
616 assert!(timer.average() >= Duration::from_millis(9));
617 }
618
619 #[test]
620 fn test_manual_stop() {
621 let timer = Timer::new();
622
623 let running = timer.start();
624 thread::sleep(Duration::from_millis(5));
625 running.stop(); assert_eq!(timer.count(), 1);
628 assert!(timer.average() >= Duration::from_millis(4));
629 }
630
631 #[test]
632 fn test_batch_recording() {
633 let timer = Timer::new();
634
635 let durations = vec![
636 Duration::from_millis(10),
637 Duration::from_millis(20),
638 Duration::from_millis(30),
639 ];
640
641 timer.record_batch(&durations);
642
643 assert_eq!(timer.count(), 3);
644 assert_eq!(timer.min(), Duration::from_millis(10));
645 assert_eq!(timer.max(), Duration::from_millis(30));
646 assert_eq!(timer.total(), Duration::from_millis(60));
647 assert_eq!(timer.average(), Duration::from_millis(20));
648 }
649
650 #[test]
651 fn test_min_max_tracking() {
652 let timer = Timer::new();
653
654 timer.record(Duration::from_millis(50));
655 timer.record(Duration::from_millis(10));
656 timer.record(Duration::from_millis(100));
657 timer.record(Duration::from_millis(25));
658
659 assert_eq!(timer.count(), 4);
660 assert_eq!(timer.min(), Duration::from_millis(10));
661 assert_eq!(timer.max(), Duration::from_millis(100));
662
663 let avg = timer.average();
665 assert!(
666 avg >= Duration::from_millis(46) && avg <= Duration::from_millis(47),
667 "Average {avg:?} should be between 46ms and 47ms",
668 );
669 }
670
671 #[test]
672 fn test_reset() {
673 let timer = Timer::new();
674
675 timer.record(Duration::from_millis(100));
676 assert!(!timer.is_empty());
677
678 timer.reset();
679 assert!(timer.is_empty());
680 assert_eq!(timer.count(), 0);
681 assert_eq!(timer.total(), Duration::ZERO);
682 assert_eq!(timer.min(), Duration::ZERO);
683 assert_eq!(timer.max(), Duration::ZERO);
684 }
685
686 #[test]
687 fn test_statistics() {
688 let timer = Timer::new();
689
690 for i in 1..=10 {
691 timer.record(Duration::from_millis(i * 10));
692 }
693
694 let stats = timer.stats();
695 assert_eq!(stats.count, 10);
696 assert_eq!(stats.total, Duration::from_millis(550)); assert_eq!(stats.average, Duration::from_millis(55));
698 assert_eq!(stats.min, Duration::from_millis(10));
699 assert_eq!(stats.max, Duration::from_millis(100));
700 assert!(stats.rate_per_second > 0.0);
701 assert!(stats.age > Duration::from_nanos(0));
702 }
703
704 #[test]
705 fn test_high_concurrency() {
706 let timer = Arc::new(Timer::new());
707 let num_threads = 50;
708 let recordings_per_thread = 1000;
709
710 let handles: Vec<_> = (0..num_threads)
711 .map(|thread_id| {
712 let timer = Arc::clone(&timer);
713 thread::spawn(move || {
714 for i in 0..recordings_per_thread {
715 let duration_ms = (thread_id * recordings_per_thread + i) % 100 + 1;
717 timer.record(Duration::from_millis(duration_ms));
718 }
719 })
720 })
721 .collect();
722
723 for handle in handles {
724 handle.join().unwrap();
725 }
726
727 let stats = timer.stats();
728 assert_eq!(stats.count, num_threads * recordings_per_thread);
729 assert!(stats.min > Duration::ZERO);
730 assert!(stats.max > Duration::ZERO);
731 assert!(stats.average > Duration::ZERO);
732 assert!(stats.rate_per_second > 0.0);
733 }
734
735 #[test]
736 fn test_concurrent_timing() {
737 let timer = Arc::new(Timer::new());
738 let num_threads = 20;
739
740 let handles: Vec<_> = (0..num_threads)
741 .map(|_| {
742 let timer = Arc::clone(&timer);
743 thread::spawn(move || {
744 for _ in 0..100 {
745 let _guard = timer.start();
746 thread::sleep(Duration::from_micros(100)); }
748 })
749 })
750 .collect();
751
752 for handle in handles {
753 handle.join().unwrap();
754 }
755
756 let stats = timer.stats();
757 assert_eq!(stats.count, num_threads * 100);
758 assert!(stats.average >= Duration::from_micros(50)); }
760
761 #[test]
762 fn test_utility_functions() {
763 let (result, duration) = utils::time_fn(|| {
765 thread::sleep(Duration::from_millis(10));
766 42
767 });
768
769 assert_eq!(result, 42);
770 assert!(duration >= Duration::from_millis(9));
771
772 let timer = Timer::new();
774 let result = utils::time_and_record(&timer, || {
775 thread::sleep(Duration::from_millis(5));
776 "hello"
777 });
778
779 assert_eq!(result, "hello");
780 assert_eq!(timer.count(), 1);
781 assert!(timer.average() >= Duration::from_millis(4));
782
783 let avg_duration = utils::benchmark("test_function", 10, || {
785 thread::sleep(Duration::from_millis(1));
786 });
787
788 assert!(avg_duration >= Duration::from_millis(1));
789 }
790
791 #[test]
792 fn test_macros() {
793 let timer = Timer::new();
794
795 let result = time_block!(timer, {
797 thread::sleep(Duration::from_millis(5));
798 "result"
799 });
800
801 assert_eq!(result, "result");
802 assert_eq!(timer.count(), 1);
803
804 let (result, duration) = time_fn!({
806 thread::sleep(Duration::from_millis(2));
807 100
808 });
809
810 assert_eq!(result, 100);
811 assert!(duration >= Duration::from_millis(1));
812 }
813
814 #[test]
815 fn test_display_and_debug() {
816 let timer = Timer::new();
817 timer.record(Duration::from_millis(100));
818
819 let display_str = format!("{timer}");
820 assert!(display_str.contains("Timer"));
821 assert!(display_str.contains("count: 1"));
822
823 let debug_str = format!("{timer:?}");
824 assert!(debug_str.contains("Timer"));
825 assert!(debug_str.contains("count"));
826 assert!(debug_str.contains("average"));
827 }
828
829 #[test]
830 fn test_record_batch_saturates_on_overflow_without_panic() {
831 let timer = Timer::new();
835 let huge = Duration::from_nanos(u64::MAX / 2);
836 timer.record_batch(&[huge, huge, huge]);
838 assert_eq!(timer.count(), 3);
839 assert_eq!(timer.total().as_nanos() as u64, u64::MAX);
841 }
842
843 #[test]
844 fn test_empty_batch_handling() {
845 let timer = Timer::new();
846 timer.record_batch(&[]);
847 assert!(timer.is_empty());
848
849 assert!(timer.try_record_batch(&[]).is_ok());
850 assert!(timer.is_empty());
851 }
852
853 #[test]
854 fn test_nested_timers_and_rate() {
855 let timer = Timer::new();
856
857 {
858 let _outer = timer.start();
859 thread::sleep(Duration::from_millis(2));
860 {
861 let _inner = timer.start();
862 thread::sleep(Duration::from_millis(2));
863 } } assert_eq!(timer.count(), 2);
868
869 let rate = timer.rate_per_second();
871 assert!(rate >= 0.0);
872
873 let stats = timer.stats();
875 assert!(stats.rate_per_second >= 0.0);
876 }
877}
878
879#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
880#[allow(unused_imports)]
881mod benchmarks {
882 use super::*;
883 use std::time::Instant;
884
885 #[cfg_attr(not(feature = "bench-tests"), ignore)]
886 #[test]
887 fn bench_timer_record() {
888 let timer = Timer::new();
889 let duration = Duration::from_nanos(1000);
890 let iterations = 1_000_000;
891
892 let start = Instant::now();
893 for _ in 0..iterations {
894 timer.record(duration);
895 }
896 let elapsed = start.elapsed();
897
898 println!(
899 "Timer record: {:.2} ns/op",
900 elapsed.as_nanos() as f64 / iterations as f64
901 );
902
903 assert_eq!(timer.count(), iterations);
904 assert!(elapsed.as_nanos() / (iterations as u128) < 300);
906 }
907
908 #[cfg_attr(not(feature = "bench-tests"), ignore)]
909 #[test]
910 fn bench_running_timer() {
911 let timer = Timer::new();
912 let iterations = 100_000;
913
914 let start = Instant::now();
915 for _ in 0..iterations {
916 let guard = timer.start();
917 let _ = guard.elapsed();
919 guard.stop();
920 }
921 let elapsed = start.elapsed();
922
923 println!(
924 "Running timer: {:.2} ns/op",
925 elapsed.as_nanos() as f64 / iterations as f64
926 );
927
928 assert_eq!(timer.count(), iterations);
929 assert!(elapsed.as_nanos() / (iterations as u128) < 1500);
931 }
932
933 #[cfg_attr(not(feature = "bench-tests"), ignore)]
934 #[test]
935 fn bench_timer_stats() {
936 let timer = Timer::new();
937
938 for i in 0..1000 {
940 timer.record(Duration::from_nanos(i * 1000));
941 }
942
943 let iterations = 1_000_000;
944 let start = Instant::now();
945
946 for _ in 0..iterations {
947 let _ = timer.stats();
948 }
949
950 let elapsed = start.elapsed();
951 println!(
952 "Timer stats: {:.2} ns/op",
953 elapsed.as_nanos() as f64 / iterations as f64
954 );
955
956 assert!(elapsed.as_nanos() / iterations < 300);
958 }
959}