1use crate::{MetricsError, Result};
15use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
16use std::time::{Duration, Instant};
17
18#[repr(align(64))]
23pub struct RateMeter {
24 total_events: AtomicU64,
26 current_second_events: AtomicU32,
28 current_minute_events: AtomicU32,
30 current_hour_events: AtomicU32,
32 last_second: AtomicU64,
34 last_minute: AtomicU64,
36 last_hour: AtomicU64,
38 window_ns: u64,
40 created_at: Instant,
42}
43
44#[derive(Debug, Clone)]
46pub struct RateStats {
47 pub total_events: u64,
49 pub per_second: f64,
51 pub per_minute: f64,
53 pub per_hour: f64,
55 pub average_rate: f64,
57 pub age: Duration,
59 pub window_fill: f64,
61}
62
63impl RateMeter {
64 #[inline]
66 pub fn new() -> Self {
67 Self::with_window(Duration::from_secs(1))
68 }
69
70 #[inline]
72 pub fn with_window(window: Duration) -> Self {
73 Self {
74 total_events: AtomicU64::new(0),
75 current_second_events: AtomicU32::new(0),
76 current_minute_events: AtomicU32::new(0),
77 current_hour_events: AtomicU32::new(0),
78 last_second: AtomicU64::new(0),
79 last_minute: AtomicU64::new(0),
80 last_hour: AtomicU64::new(0),
81 window_ns: window.as_nanos() as u64,
82 created_at: Instant::now(),
83 }
84 }
85
86 #[inline(always)]
93 pub fn tick(&self) {
94 self.tick_n(1);
95 }
96
97 #[inline(always)]
110 pub fn try_tick(&self) -> Result<()> {
111 self.try_tick_n(1)
112 }
113
114 #[inline(always)]
116 pub fn tick_n(&self, n: u32) {
117 if n == 0 {
118 return;
119 }
120
121 self.total_events.fetch_add(n as u64, Ordering::Relaxed);
123
124 let now = self.get_unix_timestamp();
126 self.update_windows(now, n);
127 }
128
129 #[inline(always)]
144 pub fn try_tick_n(&self, n: u32) -> Result<()> {
145 if n == 0 {
146 return Ok(());
147 }
148
149 let total = self.total_events.load(Ordering::Relaxed);
151 if total.checked_add(n as u64).is_none() {
152 return Err(MetricsError::Overflow);
153 }
154
155 let sec = self.current_second_events.load(Ordering::Relaxed);
158 if sec.checked_add(n).is_none() {
159 return Err(MetricsError::Overflow);
160 }
161 let min = self.current_minute_events.load(Ordering::Relaxed);
162 if min.checked_add(n).is_none() {
163 return Err(MetricsError::Overflow);
164 }
165 let hour = self.current_hour_events.load(Ordering::Relaxed);
166 if hour.checked_add(n).is_none() {
167 return Err(MetricsError::Overflow);
168 }
169
170 self.total_events.fetch_add(n as u64, Ordering::Relaxed);
172 let now = self.get_unix_timestamp();
173 self.update_windows(now, n);
174 Ok(())
175 }
176
177 #[must_use]
182 #[inline]
183 pub fn rate(&self) -> f64 {
184 let now = self.get_unix_timestamp();
185 self.update_windows(now, 0);
186
187 let events = self.current_second_events.load(Ordering::Relaxed);
188 events as f64
189 }
190
191 #[must_use]
195 #[inline]
196 pub fn rate_per_second(&self) -> f64 {
197 self.rate()
198 }
199
200 #[must_use]
205 #[inline]
206 pub fn rate_per_minute(&self) -> f64 {
207 let now = self.get_unix_timestamp();
208 self.update_windows(now, 0);
209
210 let events = self.current_minute_events.load(Ordering::Relaxed);
211 events as f64
212 }
213
214 #[must_use]
219 #[inline]
220 pub fn rate_per_hour(&self) -> f64 {
221 let now = self.get_unix_timestamp();
222 self.update_windows(now, 0);
223
224 let events = self.current_hour_events.load(Ordering::Relaxed);
225 events as f64
226 }
227
228 #[must_use]
233 #[inline(always)]
234 pub fn total(&self) -> u64 {
235 self.total_events.load(Ordering::Relaxed)
236 }
237
238 #[must_use]
243 #[inline]
244 pub fn exceeds_rate(&self, limit: f64) -> bool {
245 self.rate() > limit
246 }
247
248 #[must_use]
253 #[inline]
254 pub fn can_allow(&self, n: u32, limit: f64) -> bool {
255 let current_rate = self.rate();
256 (current_rate + n as f64) <= limit
257 }
258
259 #[must_use]
264 #[inline]
265 pub fn tick_if_under_limit(&self, limit: f64) -> bool {
266 if self.can_allow(1, limit) {
267 self.tick();
268 true
269 } else {
270 false
271 }
272 }
273
274 #[inline]
289 pub fn try_tick_if_under_limit(&self, limit: f64) -> Result<bool> {
290 if self.can_allow(1, limit) {
291 self.try_tick()?;
292 Ok(true)
293 } else {
294 Ok(false)
295 }
296 }
297
298 #[must_use]
303 #[inline]
304 pub fn tick_burst_if_under_limit(&self, n: u32, limit: f64) -> bool {
305 if self.can_allow(n, limit) {
306 self.tick_n(n);
307 true
308 } else {
309 false
310 }
311 }
312
313 #[inline]
315 pub fn reset(&self) {
316 let now = self.get_unix_timestamp();
317
318 self.total_events.store(0, Ordering::SeqCst);
319 self.current_second_events.store(0, Ordering::SeqCst);
320 self.current_minute_events.store(0, Ordering::SeqCst);
321 self.current_hour_events.store(0, Ordering::SeqCst);
322 self.last_second.store(now, Ordering::SeqCst);
323 self.last_minute.store(now / 60, Ordering::SeqCst);
324 self.last_hour.store(now / 3600, Ordering::SeqCst);
325 }
326
327 #[must_use]
332 pub fn stats(&self) -> RateStats {
333 let now = self.get_unix_timestamp();
334 self.update_windows(now, 0);
335
336 let total_events = self.total();
337 let per_second = self.current_second_events.load(Ordering::Relaxed) as f64;
338 let per_minute = self.current_minute_events.load(Ordering::Relaxed) as f64;
339 let per_hour = self.current_hour_events.load(Ordering::Relaxed) as f64;
340
341 let age = self.created_at.elapsed();
342 let average_rate = if age.as_secs_f64() > 0.0 {
343 total_events as f64 / age.as_secs_f64()
344 } else {
345 0.0
346 };
347
348 let window_fill = if self.window_ns > 0 {
350 let window_seconds = self.window_ns as f64 / 1_000_000_000.0;
351 let elapsed_in_window = age.as_secs_f64().min(window_seconds);
352 (elapsed_in_window / window_seconds * 100.0).min(100.0)
353 } else {
354 100.0
355 };
356
357 RateStats {
358 total_events,
359 per_second,
360 per_minute,
361 per_hour,
362 average_rate,
363 age,
364 window_fill,
365 }
366 }
367
368 #[must_use]
373 #[inline]
374 pub fn age(&self) -> Duration {
375 self.created_at.elapsed()
376 }
377
378 #[must_use]
382 #[inline]
383 pub fn is_empty(&self) -> bool {
384 self.total() == 0
385 }
386
387 #[inline(always)]
390 fn get_unix_timestamp(&self) -> u64 {
391 std::time::SystemTime::now()
395 .duration_since(std::time::UNIX_EPOCH)
396 .unwrap_or_default()
397 .as_secs()
398 }
399
400 #[inline]
401 fn update_windows(&self, now: u64, new_events: u32) {
402 let current_second = now;
404 let last_second = self.last_second.load(Ordering::Relaxed);
405
406 if current_second != last_second {
407 if self
409 .last_second
410 .compare_exchange(
411 last_second,
412 current_second,
413 Ordering::Relaxed,
414 Ordering::Relaxed,
415 )
416 .is_ok()
417 {
418 self.current_second_events
419 .store(new_events, Ordering::Relaxed);
420 } else {
421 self.current_second_events
423 .fetch_add(new_events, Ordering::Relaxed);
424 }
425 } else if new_events > 0 {
426 self.current_second_events
428 .fetch_add(new_events, Ordering::Relaxed);
429 }
430
431 let current_minute = now / 60;
433 let last_minute = self.last_minute.load(Ordering::Relaxed);
434
435 if current_minute != last_minute {
436 if self
437 .last_minute
438 .compare_exchange(
439 last_minute,
440 current_minute,
441 Ordering::Relaxed,
442 Ordering::Relaxed,
443 )
444 .is_ok()
445 {
446 self.current_minute_events
447 .store(new_events, Ordering::Relaxed);
448 } else {
449 self.current_minute_events
450 .fetch_add(new_events, Ordering::Relaxed);
451 }
452 } else if new_events > 0 {
453 self.current_minute_events
454 .fetch_add(new_events, Ordering::Relaxed);
455 }
456
457 let current_hour = now / 3600;
459 let last_hour = self.last_hour.load(Ordering::Relaxed);
460
461 if current_hour != last_hour {
462 if self
463 .last_hour
464 .compare_exchange(
465 last_hour,
466 current_hour,
467 Ordering::Relaxed,
468 Ordering::Relaxed,
469 )
470 .is_ok()
471 {
472 self.current_hour_events
473 .store(new_events, Ordering::Relaxed);
474 } else {
475 self.current_hour_events
476 .fetch_add(new_events, Ordering::Relaxed);
477 }
478 } else if new_events > 0 {
479 self.current_hour_events
480 .fetch_add(new_events, Ordering::Relaxed);
481 }
482 }
483}
484
485impl Default for RateMeter {
486 #[inline]
487 fn default() -> Self {
488 Self::new()
489 }
490}
491
492impl std::fmt::Display for RateMeter {
493 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
494 write!(f, "RateMeter({:.1}/s, {} total)", self.rate(), self.total())
495 }
496}
497
498impl std::fmt::Debug for RateMeter {
499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500 let stats = self.stats();
501 f.debug_struct("RateMeter")
502 .field("total_events", &stats.total_events)
503 .field("per_second", &stats.per_second)
504 .field("per_minute", &stats.per_minute)
505 .field("average_rate", &stats.average_rate)
506 .field("age", &stats.age)
507 .finish()
508 }
509}
510
511unsafe impl Send for RateMeter {}
513unsafe impl Sync for RateMeter {}
514
515pub mod specialized {
517 use super::*;
518
519 #[repr(align(64))]
521 pub struct ApiRateLimiter {
522 meter: RateMeter,
523 limit: AtomicU32, }
525
526 impl ApiRateLimiter {
527 #[inline]
529 pub fn new(requests_per_second: u32) -> Self {
530 Self {
531 meter: RateMeter::new(),
532 limit: AtomicU32::new(requests_per_second),
533 }
534 }
535
536 #[inline]
538 pub fn try_request(&self) -> bool {
539 let limit = self.limit.load(Ordering::Relaxed) as f64;
540 self.meter.tick_if_under_limit(limit)
541 }
542
543 #[inline]
545 pub fn try_requests(&self, n: u32) -> bool {
546 let limit = self.limit.load(Ordering::Relaxed) as f64;
547 self.meter.tick_burst_if_under_limit(n, limit)
548 }
549
550 #[inline]
552 pub fn set_limit(&self, requests_per_second: u32) {
553 self.limit.store(requests_per_second, Ordering::Relaxed);
554 }
555
556 #[inline]
558 pub fn get_limit(&self) -> u32 {
559 self.limit.load(Ordering::Relaxed)
560 }
561
562 #[inline]
564 pub fn current_rate(&self) -> f64 {
565 self.meter.rate()
566 }
567
568 #[inline]
570 pub fn total_requests(&self) -> u64 {
571 self.meter.total()
572 }
573
574 #[inline]
576 pub fn is_over_limit(&self) -> bool {
577 let limit = self.limit.load(Ordering::Relaxed) as f64;
578 self.meter.rate() > limit
579 }
580
581 #[inline]
583 pub fn reset(&self) {
584 self.meter.reset();
585 }
586 }
587
588 impl Default for ApiRateLimiter {
589 fn default() -> Self {
590 Self::new(1000)
591 } }
593
594 #[repr(align(64))]
596 pub struct ThroughputMeter {
597 meter: RateMeter,
598 }
599
600 impl ThroughputMeter {
601 #[inline]
603 pub fn new() -> Self {
604 Self {
605 meter: RateMeter::new(),
606 }
607 }
608
609 #[inline(always)]
611 pub fn record_bytes(&self, bytes: u64) {
612 self.meter.tick_n(bytes as u32);
613 }
614
615 #[inline]
617 pub fn bytes_per_second(&self) -> f64 {
618 self.meter.rate()
619 }
620
621 #[inline]
623 pub fn kb_per_second(&self) -> f64 {
624 self.meter.rate() / 1024.0
625 }
626
627 #[inline]
629 pub fn mb_per_second(&self) -> f64 {
630 self.meter.rate() / (1024.0 * 1024.0)
631 }
632
633 #[inline]
635 pub fn gb_per_second(&self) -> f64 {
636 self.meter.rate() / (1024.0 * 1024.0 * 1024.0)
637 }
638
639 #[inline]
641 pub fn total_bytes(&self) -> u64 {
642 self.meter.total()
643 }
644
645 #[inline]
647 pub fn reset(&self) {
648 self.meter.reset();
649 }
650 }
651
652 impl Default for ThroughputMeter {
653 fn default() -> Self {
654 Self::new()
655 }
656 }
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662 use std::sync::Arc;
663 use std::thread;
664
665 #[test]
666 fn test_basic_operations() {
667 let meter = RateMeter::new();
668
669 assert!(meter.is_empty());
670 assert_eq!(meter.total(), 0);
671 assert_eq!(meter.rate(), 0.0);
672
673 meter.tick();
674 assert!(!meter.is_empty());
675 assert_eq!(meter.total(), 1);
676
677 meter.tick_n(5);
678 assert_eq!(meter.total(), 6);
679 }
680
681 #[test]
682 fn test_rate_calculations() {
683 let meter = RateMeter::new();
684
685 for _ in 0..100 {
687 meter.tick();
688 }
689
690 let rate = meter.rate();
691 assert_eq!(rate, 100.0);
692 assert_eq!(meter.rate_per_second(), 100.0);
693 }
694
695 #[test]
696 fn test_multiple_windows() {
697 let meter = RateMeter::new();
698
699 for _ in 0..60 {
701 meter.tick();
702 }
703
704 let stats = meter.stats();
705 assert_eq!(stats.total_events, 60);
706 assert_eq!(stats.per_second, 60.0);
707 assert_eq!(stats.per_minute, 60.0);
708 assert_eq!(stats.per_hour, 60.0);
709 }
710
711 #[test]
712 fn test_rate_limiting() {
713 let meter = RateMeter::new();
714
715 assert!(meter.tick_if_under_limit(10.0));
717 assert!(meter.tick_if_under_limit(10.0));
718
719 meter.tick_n(8);
721
722 assert!(!meter.tick_if_under_limit(10.0));
724 assert!(meter.exceeds_rate(9.0));
725 assert!(!meter.exceeds_rate(11.0));
726 }
727
728 #[test]
729 fn test_burst_rate_limiting() {
730 let meter = RateMeter::new();
731
732 assert!(meter.tick_burst_if_under_limit(5, 10.0));
734 assert_eq!(meter.total(), 5);
735
736 assert!(!meter.tick_burst_if_under_limit(10, 10.0));
738 assert_eq!(meter.total(), 5); assert!(meter.tick_burst_if_under_limit(3, 10.0));
742 assert_eq!(meter.total(), 8);
743 }
744
745 #[test]
746 fn test_can_allow() {
747 let meter = RateMeter::new();
748
749 meter.tick_n(5);
750
751 assert!(meter.can_allow(3, 10.0)); assert!(!meter.can_allow(6, 10.0)); assert!(meter.can_allow(5, 10.0)); }
755
756 #[test]
757 fn test_reset() {
758 let meter = RateMeter::new();
759
760 meter.tick_n(100);
761 assert_eq!(meter.total(), 100);
762 assert!(meter.rate() > 0.0);
763
764 meter.reset();
765 assert_eq!(meter.total(), 0);
766 assert_eq!(meter.rate(), 0.0);
767 assert!(meter.is_empty());
768 }
769
770 #[test]
771 fn test_statistics() {
772 let meter = RateMeter::new();
773
774 meter.tick_n(50);
775
776 let stats = meter.stats();
777 assert_eq!(stats.total_events, 50);
778 assert_eq!(stats.per_second, 50.0);
779 assert!(stats.average_rate > 0.0);
780 assert!(stats.age > Duration::from_nanos(0));
781 assert!(stats.window_fill >= 0.0);
782 }
783
784 #[test]
785 fn test_api_rate_limiter() {
786 let limiter = specialized::ApiRateLimiter::new(10);
787
788 for _ in 0..10 {
790 assert!(limiter.try_request());
791 }
792
793 assert!(!limiter.try_request());
795
796 assert_eq!(limiter.current_rate(), 10.0);
798 assert_eq!(limiter.total_requests(), 10);
799 assert_eq!(limiter.get_limit(), 10);
800
801 limiter.set_limit(20);
803 assert_eq!(limiter.get_limit(), 20);
804 assert!(!limiter.is_over_limit()); limiter.reset();
808 assert!(limiter.try_requests(5));
809 assert_eq!(limiter.total_requests(), 5);
810
811 assert!(!limiter.try_requests(20)); assert_eq!(limiter.total_requests(), 5); }
814
815 #[test]
816 fn test_throughput_meter() {
817 let meter = specialized::ThroughputMeter::new();
818
819 meter.record_bytes(1024); assert_eq!(meter.bytes_per_second(), 1024.0);
821 assert_eq!(meter.kb_per_second(), 1.0);
822 assert_eq!(meter.total_bytes(), 1024);
823
824 meter.record_bytes(1024 * 1024); assert_eq!(meter.total_bytes(), 1024 + 1024 * 1024);
826 assert!((meter.mb_per_second() - 1.001).abs() < 0.01);
827 }
828
829 #[test]
830 fn test_high_concurrency() {
831 let meter = Arc::new(RateMeter::new());
832 let num_threads = 50;
833 let ticks_per_thread = 1000;
834
835 let handles: Vec<_> = (0..num_threads)
836 .map(|_| {
837 let meter = Arc::clone(&meter);
838 thread::spawn(move || {
839 for _ in 0..ticks_per_thread {
840 meter.tick();
841 }
842 })
843 })
844 .collect();
845
846 for handle in handles {
847 handle.join().unwrap();
848 }
849
850 assert_eq!(meter.total(), num_threads * ticks_per_thread);
851
852 let stats = meter.stats();
853 assert!(stats.average_rate > 0.0);
854 assert_eq!(stats.total_events, num_threads * ticks_per_thread);
855 }
856
857 #[test]
858 fn test_concurrent_rate_limiting() {
859 let limiter = Arc::new(specialized::ApiRateLimiter::new(100));
860 let num_threads = 20;
861
862 let handles: Vec<_> = (0..num_threads)
863 .map(|_| {
864 let limiter = Arc::clone(&limiter);
865 thread::spawn(move || {
866 let mut successful = 0;
867 for _ in 0..10 {
868 if limiter.try_request() {
869 successful += 1;
870 }
871 }
872 successful
873 })
874 })
875 .collect();
876
877 let total_successful: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();
878
879 let total_attempts = num_threads * 10;
884 let strict_cap = 2 * 100; let upper_bound = if cfg!(coverage) {
886 total_attempts.min(strict_cap.max(160))
887 } else {
888 total_attempts.min(strict_cap)
889 };
890 assert!(
891 total_successful <= upper_bound,
892 "total_successful={total_successful} > upper_bound={upper_bound}",
893 );
894 assert!(
895 total_successful >= 90,
896 "total_successful={total_successful} < lower_bound=90",
897 ); }
899
900 #[test]
901 fn test_display_and_debug() {
902 let meter = RateMeter::new();
903 meter.tick_n(42);
904
905 let display_str = format!("{meter}");
906 assert!(display_str.contains("RateMeter"));
907 assert!(display_str.contains("42 total"));
908
909 let debug_str = format!("{meter:?}");
910 assert!(debug_str.contains("RateMeter"));
911 assert!(debug_str.contains("total_events"));
912 }
913
914 #[test]
915 fn test_custom_window() {
916 let meter = RateMeter::with_window(Duration::from_secs(5));
917
918 meter.tick_n(10);
919 assert_eq!(meter.total(), 10);
920 assert_eq!(meter.rate(), 10.0);
921
922 let stats = meter.stats();
923 assert!(stats.window_fill >= 0.0);
924 }
925
926 #[test]
928 fn test_try_tick_and_try_tick_n_ok() {
929 let meter = RateMeter::new();
930 assert!(meter.try_tick().is_ok());
931 assert!(meter.try_tick_n(5).is_ok());
932 assert_eq!(meter.total(), 6);
933 }
934
935 #[test]
936 fn test_try_tick_n_total_overflow() {
937 let meter = RateMeter::new();
938 meter.total_events.store(u64::MAX - 1, Ordering::Relaxed);
940 let err = meter.try_tick_n(2).unwrap_err();
942 assert_eq!(err, MetricsError::Overflow);
943 }
944
945 #[test]
946 fn test_try_tick_n_window_overflow() {
947 let meter = RateMeter::new();
948 let now = meter.get_unix_timestamp();
950 meter.last_second.store(now, Ordering::Relaxed);
951 meter.last_minute.store(now / 60, Ordering::Relaxed);
952 meter.last_hour.store(now / 3600, Ordering::Relaxed);
953
954 meter
955 .current_second_events
956 .store(u32::MAX - 1, Ordering::Relaxed);
957 meter
958 .current_minute_events
959 .store(u32::MAX - 1, Ordering::Relaxed);
960 meter
961 .current_hour_events
962 .store(u32::MAX - 1, Ordering::Relaxed);
963
964 let err = meter.try_tick_n(2).unwrap_err();
966 assert_eq!(err, MetricsError::Overflow);
967 }
968
969 #[test]
970 fn test_try_tick_if_under_limit() {
971 let meter = RateMeter::new();
972 assert!(meter.try_tick_if_under_limit(10.0).unwrap());
974 assert!(meter.try_tick_n(8).is_ok());
976 assert!(meter.try_tick_if_under_limit(10.0).unwrap());
978 assert!(!meter.try_tick_if_under_limit(10.0).unwrap());
980 }
981}
982
983#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
984#[allow(unused_imports)]
985mod benchmarks {
986 use super::*;
987 use std::time::Instant;
988
989 #[cfg_attr(not(feature = "bench-tests"), ignore)]
990 #[test]
991 fn bench_rate_meter_tick() {
992 let meter = RateMeter::new();
993 let iterations = 10_000_000;
994
995 let start = Instant::now();
996 for _ in 0..iterations {
997 meter.tick();
998 }
999 let elapsed = start.elapsed();
1000
1001 println!(
1002 "RateMeter tick: {:.2} ns/op",
1003 elapsed.as_nanos() as f64 / iterations as f64
1004 );
1005
1006 assert_eq!(meter.total(), iterations);
1007 assert!(elapsed.as_nanos() / (iterations as u128) < 400);
1009 }
1010
1011 #[cfg_attr(not(feature = "bench-tests"), ignore)]
1012 #[test]
1013 fn bench_rate_meter_tick_n() {
1014 let meter = RateMeter::new();
1015 let iterations = 1_000_000;
1016
1017 let start = Instant::now();
1018 for i in 0..iterations {
1019 meter.tick_n((i % 10) + 1);
1020 }
1021 let elapsed = start.elapsed();
1022
1023 println!(
1024 "RateMeter tick_n: {:.2} ns/op",
1025 elapsed.as_nanos() as f64 / iterations as f64
1026 );
1027
1028 assert!(elapsed.as_nanos() / (iterations as u128) < 500);
1030 }
1031
1032 #[cfg_attr(not(feature = "bench-tests"), ignore)]
1033 #[test]
1034 fn bench_rate_calculation() {
1035 let meter = RateMeter::new();
1036
1037 meter.tick_n(1000);
1039
1040 let iterations = 1_000_000;
1041 let start = Instant::now();
1042
1043 for _ in 0..iterations {
1044 let _ = meter.rate();
1045 }
1046
1047 let elapsed = start.elapsed();
1048 println!(
1049 "RateMeter rate: {:.2} ns/op",
1050 elapsed.as_nanos() as f64 / iterations as f64
1051 );
1052
1053 assert!(elapsed.as_nanos() / iterations < 300);
1055 }
1056
1057 #[cfg_attr(not(feature = "bench-tests"), ignore)]
1058 #[test]
1059 fn bench_api_rate_limiter() {
1060 let limiter = specialized::ApiRateLimiter::new(1_000_000); let iterations = 1_000_000;
1062
1063 let start = Instant::now();
1064 for _ in 0..iterations {
1065 let _ = limiter.try_request();
1066 }
1067 let elapsed = start.elapsed();
1068
1069 println!(
1070 "ApiRateLimiter try_request: {:.2} ns/op",
1071 elapsed.as_nanos() as f64 / iterations as f64
1072 );
1073
1074 assert!(elapsed.as_nanos() / iterations < 1000);
1076 }
1077}