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 = 0u64;
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 += ns;
177 local_min = local_min.min(ns);
178 local_max = local_max.max(ns);
179 }
180
181 self.total_nanos.fetch_add(total_ns, Ordering::Relaxed);
182 self.count
183 .fetch_add(durations.len() as u64, Ordering::Relaxed);
184
185 if local_min < u64::MAX {
186 self.update_min(local_min);
187 }
188 if local_max > 0 {
189 self.update_max(local_max);
190 }
191 }
192
193 #[inline]
207 pub fn try_record_batch(&self, durations: &[Duration]) -> Result<()> {
208 if durations.is_empty() {
209 return Ok(());
210 }
211
212 let mut total_ns: u64 = 0;
213 let mut local_min = u64::MAX;
214 let mut local_max = 0u64;
215
216 for d in durations {
217 let ns = d.as_nanos() as u64;
218 total_ns = total_ns.checked_add(ns).ok_or(MetricsError::Overflow)?;
219 local_min = local_min.min(ns);
220 local_max = local_max.max(ns);
221 }
222
223 let current_total = self.total_nanos.load(Ordering::Relaxed);
225 if current_total.checked_add(total_ns).is_none() {
226 return Err(MetricsError::Overflow);
227 }
228 let current_count = self.count.load(Ordering::Relaxed);
229 let add_count = durations.len() as u64;
230 if current_count.checked_add(add_count).is_none() {
231 return Err(MetricsError::Overflow);
232 }
233
234 self.total_nanos.fetch_add(total_ns, Ordering::Relaxed);
236 self.count.fetch_add(add_count, Ordering::Relaxed);
237 if local_min < u64::MAX {
238 self.update_min(local_min);
239 }
240 if local_max > 0 {
241 self.update_max(local_max);
242 }
243 Ok(())
244 }
245
246 #[inline(always)]
248 pub fn count(&self) -> u64 {
249 self.count.load(Ordering::Relaxed)
250 }
251
252 #[inline]
254 pub fn total(&self) -> Duration {
255 Duration::from_nanos(self.total_nanos.load(Ordering::Relaxed))
256 }
257
258 #[inline]
260 pub fn average(&self) -> Duration {
261 let count = self.count();
262 if count == 0 {
263 return Duration::ZERO;
264 }
265
266 let total_ns = self.total_nanos.load(Ordering::Relaxed);
267 Duration::from_nanos(total_ns / count)
268 }
269
270 #[inline]
272 pub fn min(&self) -> Duration {
273 let min_ns = self.min_nanos.load(Ordering::Relaxed);
274 if min_ns == u64::MAX {
275 Duration::ZERO
276 } else {
277 Duration::from_nanos(min_ns)
278 }
279 }
280
281 #[inline]
283 pub fn max(&self) -> Duration {
284 Duration::from_nanos(self.max_nanos.load(Ordering::Relaxed))
285 }
286
287 #[inline]
289 pub fn reset(&self) {
290 self.total_nanos.store(0, Ordering::SeqCst);
291 self.count.store(0, Ordering::SeqCst);
292 self.min_nanos.store(u64::MAX, Ordering::SeqCst);
293 self.max_nanos.store(0, Ordering::SeqCst);
294 }
295
296 pub fn stats(&self) -> TimerStats {
298 let count = self.count();
299 let total_ns = self.total_nanos.load(Ordering::Relaxed);
300 let min_ns = self.min_nanos.load(Ordering::Relaxed);
301 let max_ns = self.max_nanos.load(Ordering::Relaxed);
302
303 let total = Duration::from_nanos(total_ns);
304 let average = if count > 0 {
305 Duration::from_nanos(total_ns / count)
306 } else {
307 Duration::ZERO
308 };
309
310 let min = if min_ns == u64::MAX {
311 Duration::ZERO
312 } else {
313 Duration::from_nanos(min_ns)
314 };
315
316 let max = Duration::from_nanos(max_ns);
317 let age = self.created_at.elapsed();
318
319 let rate_per_second = if age.as_secs_f64() > 0.0 {
320 count as f64 / age.as_secs_f64()
321 } else {
322 0.0
323 };
324
325 TimerStats {
326 count,
327 total,
328 average,
329 min,
330 max,
331 age,
332 rate_per_second,
333 }
334 }
335
336 #[inline]
338 pub fn age(&self) -> Duration {
339 self.created_at.elapsed()
340 }
341
342 #[inline]
344 pub fn is_empty(&self) -> bool {
345 self.count() == 0
346 }
347
348 #[inline]
350 pub fn rate_per_second(&self) -> f64 {
351 let age_seconds = self.age().as_secs_f64();
352 if age_seconds > 0.0 {
353 self.count() as f64 / age_seconds
354 } else {
355 0.0
356 }
357 }
358
359 #[inline(always)]
362 fn update_min(&self, value: u64) {
363 loop {
364 let current = self.min_nanos.load(Ordering::Relaxed);
365 if value >= current {
366 break; }
368
369 match self.min_nanos.compare_exchange_weak(
370 current,
371 value,
372 Ordering::Relaxed,
373 Ordering::Relaxed,
374 ) {
375 Ok(_) => break,
376 Err(_) => continue, }
378 }
379 }
380
381 #[inline(always)]
382 fn update_max(&self, value: u64) {
383 loop {
384 let current = self.max_nanos.load(Ordering::Relaxed);
385 if value <= current {
386 break; }
388
389 match self.max_nanos.compare_exchange_weak(
390 current,
391 value,
392 Ordering::Relaxed,
393 Ordering::Relaxed,
394 ) {
395 Ok(_) => break,
396 Err(_) => continue, }
398 }
399 }
400}
401
402impl Default for Timer {
403 #[inline]
404 fn default() -> Self {
405 Self::new()
406 }
407}
408
409impl std::fmt::Display for Timer {
410 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411 let stats = self.stats();
412 write!(f, "Timer(count: {}, avg: {:?})", stats.count, stats.average)
413 }
414}
415
416impl std::fmt::Debug for Timer {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 let stats = self.stats();
419 f.debug_struct("Timer")
420 .field("count", &stats.count)
421 .field("total", &stats.total)
422 .field("average", &stats.average)
423 .field("min", &stats.min)
424 .field("max", &stats.max)
425 .field("rate_per_second", &stats.rate_per_second)
426 .finish()
427 }
428}
429
430unsafe impl Send for Timer {}
432unsafe impl Sync for Timer {}
433
434impl<'a> RunningTimer<'a> {
436 #[inline]
438 pub fn elapsed(&self) -> Duration {
439 self.start_time.elapsed()
440 }
441
442 #[inline]
444 pub fn stop(mut self) {
445 if !self.stopped {
446 let elapsed = self.start_time.elapsed();
447 self.timer.record(elapsed);
448 self.stopped = true;
449 }
450 }
452}
453
454impl<'a> Drop for RunningTimer<'a> {
455 #[inline]
456 fn drop(&mut self) {
457 if !self.stopped {
458 let elapsed = self.start_time.elapsed();
459 self.timer.record(elapsed);
460 }
461 }
462}
463
464pub mod utils {
466 use super::*;
467
468 #[inline]
470 pub fn time_fn<T>(f: impl FnOnce() -> T) -> (T, Duration) {
471 let start = Instant::now();
472 let result = f();
473 let duration = start.elapsed();
474 (result, duration)
475 }
476
477 #[inline]
479 pub fn time_and_record<T>(timer: &Timer, f: impl FnOnce() -> T) -> T {
480 let _timing_guard = timer.start();
481 f() }
483
484 #[inline]
486 pub fn scoped_timer(timer: &Timer) -> RunningTimer<'_> {
487 timer.start()
488 }
489
490 pub fn benchmark<F>(name: &str, iterations: usize, f: F) -> Duration
492 where
493 F: Fn(),
494 {
495 let timer = Timer::new();
496
497 for _ in 0..iterations {
498 let _guard = timer.start();
499 f();
500 }
501
502 let stats = timer.stats();
503 println!(
504 "Benchmark '{}': {} iterations, avg: {:?}, total: {:?}",
505 name, iterations, stats.average, stats.total
506 );
507
508 stats.average
509 }
510}
511
512#[macro_export]
514macro_rules! time_block {
515 ($timer:expr, $block:block) => {{
516 let _timing_guard = $timer.start();
517 $block
518 }};
519}
520
521#[macro_export]
522macro_rules! time_fn {
537 ($func:expr) => {{
538 let start = std::time::Instant::now();
539 let result = $func;
540 let duration = start.elapsed();
541 (result, duration)
542 }};
543}
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548 use std::sync::Arc;
549 use std::thread;
550
551 #[test]
552 fn test_basic_operations() {
553 let timer = Timer::new();
554
555 assert!(timer.is_empty());
556 assert_eq!(timer.count(), 0);
557 assert_eq!(timer.total(), Duration::ZERO);
558 assert_eq!(timer.average(), Duration::ZERO);
559
560 timer.record(Duration::from_millis(100));
562
563 assert!(!timer.is_empty());
564 assert_eq!(timer.count(), 1);
565 assert!(timer.total() >= Duration::from_millis(99)); assert!(timer.average() >= Duration::from_millis(99));
567 }
568
569 #[test]
570 fn test_running_timer() {
571 let timer = Timer::new();
572
573 {
574 let running = timer.start();
575 thread::sleep(Duration::from_millis(10));
576 assert!(running.elapsed() >= Duration::from_millis(9));
577 } assert_eq!(timer.count(), 1);
580 assert!(timer.average() >= Duration::from_millis(9));
581 }
582
583 #[test]
584 fn test_manual_stop() {
585 let timer = Timer::new();
586
587 let running = timer.start();
588 thread::sleep(Duration::from_millis(5));
589 running.stop(); assert_eq!(timer.count(), 1);
592 assert!(timer.average() >= Duration::from_millis(4));
593 }
594
595 #[test]
596 fn test_batch_recording() {
597 let timer = Timer::new();
598
599 let durations = vec![
600 Duration::from_millis(10),
601 Duration::from_millis(20),
602 Duration::from_millis(30),
603 ];
604
605 timer.record_batch(&durations);
606
607 assert_eq!(timer.count(), 3);
608 assert_eq!(timer.min(), Duration::from_millis(10));
609 assert_eq!(timer.max(), Duration::from_millis(30));
610 assert_eq!(timer.total(), Duration::from_millis(60));
611 assert_eq!(timer.average(), Duration::from_millis(20));
612 }
613
614 #[test]
615 fn test_min_max_tracking() {
616 let timer = Timer::new();
617
618 timer.record(Duration::from_millis(50));
619 timer.record(Duration::from_millis(10));
620 timer.record(Duration::from_millis(100));
621 timer.record(Duration::from_millis(25));
622
623 assert_eq!(timer.count(), 4);
624 assert_eq!(timer.min(), Duration::from_millis(10));
625 assert_eq!(timer.max(), Duration::from_millis(100));
626
627 let avg = timer.average();
629 assert!(
630 avg >= Duration::from_millis(46) && avg <= Duration::from_millis(47),
631 "Average {avg:?} should be between 46ms and 47ms",
632 );
633 }
634
635 #[test]
636 fn test_reset() {
637 let timer = Timer::new();
638
639 timer.record(Duration::from_millis(100));
640 assert!(!timer.is_empty());
641
642 timer.reset();
643 assert!(timer.is_empty());
644 assert_eq!(timer.count(), 0);
645 assert_eq!(timer.total(), Duration::ZERO);
646 assert_eq!(timer.min(), Duration::ZERO);
647 assert_eq!(timer.max(), Duration::ZERO);
648 }
649
650 #[test]
651 fn test_statistics() {
652 let timer = Timer::new();
653
654 for i in 1..=10 {
655 timer.record(Duration::from_millis(i * 10));
656 }
657
658 let stats = timer.stats();
659 assert_eq!(stats.count, 10);
660 assert_eq!(stats.total, Duration::from_millis(550)); assert_eq!(stats.average, Duration::from_millis(55));
662 assert_eq!(stats.min, Duration::from_millis(10));
663 assert_eq!(stats.max, Duration::from_millis(100));
664 assert!(stats.rate_per_second > 0.0);
665 assert!(stats.age > Duration::from_nanos(0));
666 }
667
668 #[test]
669 fn test_high_concurrency() {
670 let timer = Arc::new(Timer::new());
671 let num_threads = 50;
672 let recordings_per_thread = 1000;
673
674 let handles: Vec<_> = (0..num_threads)
675 .map(|thread_id| {
676 let timer = Arc::clone(&timer);
677 thread::spawn(move || {
678 for i in 0..recordings_per_thread {
679 let duration_ms = (thread_id * recordings_per_thread + i) % 100 + 1;
681 timer.record(Duration::from_millis(duration_ms));
682 }
683 })
684 })
685 .collect();
686
687 for handle in handles {
688 handle.join().unwrap();
689 }
690
691 let stats = timer.stats();
692 assert_eq!(stats.count, num_threads * recordings_per_thread);
693 assert!(stats.min > Duration::ZERO);
694 assert!(stats.max > Duration::ZERO);
695 assert!(stats.average > Duration::ZERO);
696 assert!(stats.rate_per_second > 0.0);
697 }
698
699 #[test]
700 fn test_concurrent_timing() {
701 let timer = Arc::new(Timer::new());
702 let num_threads = 20;
703
704 let handles: Vec<_> = (0..num_threads)
705 .map(|_| {
706 let timer = Arc::clone(&timer);
707 thread::spawn(move || {
708 for _ in 0..100 {
709 let _guard = timer.start();
710 thread::sleep(Duration::from_micros(100)); }
712 })
713 })
714 .collect();
715
716 for handle in handles {
717 handle.join().unwrap();
718 }
719
720 let stats = timer.stats();
721 assert_eq!(stats.count, num_threads * 100);
722 assert!(stats.average >= Duration::from_micros(50)); }
724
725 #[test]
726 fn test_utility_functions() {
727 let (result, duration) = utils::time_fn(|| {
729 thread::sleep(Duration::from_millis(10));
730 42
731 });
732
733 assert_eq!(result, 42);
734 assert!(duration >= Duration::from_millis(9));
735
736 let timer = Timer::new();
738 let result = utils::time_and_record(&timer, || {
739 thread::sleep(Duration::from_millis(5));
740 "hello"
741 });
742
743 assert_eq!(result, "hello");
744 assert_eq!(timer.count(), 1);
745 assert!(timer.average() >= Duration::from_millis(4));
746
747 let avg_duration = utils::benchmark("test_function", 10, || {
749 thread::sleep(Duration::from_millis(1));
750 });
751
752 assert!(avg_duration >= Duration::from_millis(1));
753 }
754
755 #[test]
756 fn test_macros() {
757 let timer = Timer::new();
758
759 let result = time_block!(timer, {
761 thread::sleep(Duration::from_millis(5));
762 "result"
763 });
764
765 assert_eq!(result, "result");
766 assert_eq!(timer.count(), 1);
767
768 let (result, duration) = time_fn!({
770 thread::sleep(Duration::from_millis(2));
771 100
772 });
773
774 assert_eq!(result, 100);
775 assert!(duration >= Duration::from_millis(1));
776 }
777
778 #[test]
779 fn test_display_and_debug() {
780 let timer = Timer::new();
781 timer.record(Duration::from_millis(100));
782
783 let display_str = format!("{timer}");
784 assert!(display_str.contains("Timer"));
785 assert!(display_str.contains("count: 1"));
786
787 let debug_str = format!("{timer:?}");
788 assert!(debug_str.contains("Timer"));
789 assert!(debug_str.contains("count"));
790 assert!(debug_str.contains("average"));
791 }
792
793 #[test]
794 fn test_empty_batch_handling() {
795 let timer = Timer::new();
796 timer.record_batch(&[]);
797 assert!(timer.is_empty());
798
799 assert!(timer.try_record_batch(&[]).is_ok());
800 assert!(timer.is_empty());
801 }
802
803 #[test]
804 fn test_nested_timers_and_rate() {
805 let timer = Timer::new();
806
807 {
808 let _outer = timer.start();
809 thread::sleep(Duration::from_millis(2));
810 {
811 let _inner = timer.start();
812 thread::sleep(Duration::from_millis(2));
813 } } assert_eq!(timer.count(), 2);
818
819 let rate = timer.rate_per_second();
821 assert!(rate >= 0.0);
822
823 let stats = timer.stats();
825 assert!(stats.rate_per_second >= 0.0);
826 }
827}
828
829#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
830#[allow(unused_imports)]
831mod benchmarks {
832 use super::*;
833 use std::time::Instant;
834
835 #[cfg_attr(not(feature = "bench-tests"), ignore)]
836 #[test]
837 fn bench_timer_record() {
838 let timer = Timer::new();
839 let duration = Duration::from_nanos(1000);
840 let iterations = 1_000_000;
841
842 let start = Instant::now();
843 for _ in 0..iterations {
844 timer.record(duration);
845 }
846 let elapsed = start.elapsed();
847
848 println!(
849 "Timer record: {:.2} ns/op",
850 elapsed.as_nanos() as f64 / iterations as f64
851 );
852
853 assert_eq!(timer.count(), iterations);
854 assert!(elapsed.as_nanos() / (iterations as u128) < 300);
856 }
857
858 #[cfg_attr(not(feature = "bench-tests"), ignore)]
859 #[test]
860 fn bench_running_timer() {
861 let timer = Timer::new();
862 let iterations = 100_000;
863
864 let start = Instant::now();
865 for _ in 0..iterations {
866 let guard = timer.start();
867 let _ = guard.elapsed();
869 guard.stop();
870 }
871 let elapsed = start.elapsed();
872
873 println!(
874 "Running timer: {:.2} ns/op",
875 elapsed.as_nanos() as f64 / iterations as f64
876 );
877
878 assert_eq!(timer.count(), iterations);
879 assert!(elapsed.as_nanos() / (iterations as u128) < 1500);
881 }
882
883 #[cfg_attr(not(feature = "bench-tests"), ignore)]
884 #[test]
885 fn bench_timer_stats() {
886 let timer = Timer::new();
887
888 for i in 0..1000 {
890 timer.record(Duration::from_nanos(i * 1000));
891 }
892
893 let iterations = 1_000_000;
894 let start = Instant::now();
895
896 for _ in 0..iterations {
897 let _ = timer.stats();
898 }
899
900 let elapsed = start.elapsed();
901 println!(
902 "Timer stats: {:.2} ns/op",
903 elapsed.as_nanos() as f64 / iterations as f64
904 );
905
906 assert!(elapsed.as_nanos() / iterations < 300);
908 }
909}