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 #[inline]
179 pub fn rate(&self) -> f64 {
180 let now = self.get_unix_timestamp();
181 self.update_windows(now, 0);
182
183 let events = self.current_second_events.load(Ordering::Relaxed);
184 events as f64
185 }
186
187 #[inline]
189 pub fn rate_per_second(&self) -> f64 {
190 self.rate()
191 }
192
193 #[inline]
195 pub fn rate_per_minute(&self) -> f64 {
196 let now = self.get_unix_timestamp();
197 self.update_windows(now, 0);
198
199 let events = self.current_minute_events.load(Ordering::Relaxed);
200 events as f64
201 }
202
203 #[inline]
205 pub fn rate_per_hour(&self) -> f64 {
206 let now = self.get_unix_timestamp();
207 self.update_windows(now, 0);
208
209 let events = self.current_hour_events.load(Ordering::Relaxed);
210 events as f64
211 }
212
213 #[inline(always)]
215 pub fn total(&self) -> u64 {
216 self.total_events.load(Ordering::Relaxed)
217 }
218
219 #[inline]
221 pub fn exceeds_rate(&self, limit: f64) -> bool {
222 self.rate() > limit
223 }
224
225 #[inline]
227 pub fn can_allow(&self, n: u32, limit: f64) -> bool {
228 let current_rate = self.rate();
229 (current_rate + n as f64) <= limit
230 }
231
232 #[inline]
234 pub fn tick_if_under_limit(&self, limit: f64) -> bool {
235 if self.can_allow(1, limit) {
236 self.tick();
237 true
238 } else {
239 false
240 }
241 }
242
243 #[inline]
258 pub fn try_tick_if_under_limit(&self, limit: f64) -> Result<bool> {
259 if self.can_allow(1, limit) {
260 self.try_tick()?;
261 Ok(true)
262 } else {
263 Ok(false)
264 }
265 }
266
267 #[inline]
269 pub fn tick_burst_if_under_limit(&self, n: u32, limit: f64) -> bool {
270 if self.can_allow(n, limit) {
271 self.tick_n(n);
272 true
273 } else {
274 false
275 }
276 }
277
278 #[inline]
280 pub fn reset(&self) {
281 let now = self.get_unix_timestamp();
282
283 self.total_events.store(0, Ordering::SeqCst);
284 self.current_second_events.store(0, Ordering::SeqCst);
285 self.current_minute_events.store(0, Ordering::SeqCst);
286 self.current_hour_events.store(0, Ordering::SeqCst);
287 self.last_second.store(now, Ordering::SeqCst);
288 self.last_minute.store(now / 60, Ordering::SeqCst);
289 self.last_hour.store(now / 3600, Ordering::SeqCst);
290 }
291
292 pub fn stats(&self) -> RateStats {
294 let now = self.get_unix_timestamp();
295 self.update_windows(now, 0);
296
297 let total_events = self.total();
298 let per_second = self.current_second_events.load(Ordering::Relaxed) as f64;
299 let per_minute = self.current_minute_events.load(Ordering::Relaxed) as f64;
300 let per_hour = self.current_hour_events.load(Ordering::Relaxed) as f64;
301
302 let age = self.created_at.elapsed();
303 let average_rate = if age.as_secs_f64() > 0.0 {
304 total_events as f64 / age.as_secs_f64()
305 } else {
306 0.0
307 };
308
309 let window_fill = if self.window_ns > 0 {
311 let window_seconds = self.window_ns as f64 / 1_000_000_000.0;
312 let elapsed_in_window = age.as_secs_f64().min(window_seconds);
313 (elapsed_in_window / window_seconds * 100.0).min(100.0)
314 } else {
315 100.0
316 };
317
318 RateStats {
319 total_events,
320 per_second,
321 per_minute,
322 per_hour,
323 average_rate,
324 age,
325 window_fill,
326 }
327 }
328
329 #[inline]
331 pub fn age(&self) -> Duration {
332 self.created_at.elapsed()
333 }
334
335 #[inline]
337 pub fn is_empty(&self) -> bool {
338 self.total() == 0
339 }
340
341 #[inline(always)]
344 fn get_unix_timestamp(&self) -> u64 {
345 self.created_at.elapsed().as_secs()
346 + std::time::SystemTime::now()
347 .duration_since(std::time::UNIX_EPOCH)
348 .unwrap_or_default()
349 .as_secs()
350 }
351
352 #[inline]
353 fn update_windows(&self, now: u64, new_events: u32) {
354 let current_second = now;
356 let last_second = self.last_second.load(Ordering::Relaxed);
357
358 if current_second != last_second {
359 if self
361 .last_second
362 .compare_exchange(
363 last_second,
364 current_second,
365 Ordering::Relaxed,
366 Ordering::Relaxed,
367 )
368 .is_ok()
369 {
370 self.current_second_events
371 .store(new_events, Ordering::Relaxed);
372 } else {
373 self.current_second_events
375 .fetch_add(new_events, Ordering::Relaxed);
376 }
377 } else if new_events > 0 {
378 self.current_second_events
380 .fetch_add(new_events, Ordering::Relaxed);
381 }
382
383 let current_minute = now / 60;
385 let last_minute = self.last_minute.load(Ordering::Relaxed);
386
387 if current_minute != last_minute {
388 if self
389 .last_minute
390 .compare_exchange(
391 last_minute,
392 current_minute,
393 Ordering::Relaxed,
394 Ordering::Relaxed,
395 )
396 .is_ok()
397 {
398 self.current_minute_events
399 .store(new_events, Ordering::Relaxed);
400 } else {
401 self.current_minute_events
402 .fetch_add(new_events, Ordering::Relaxed);
403 }
404 } else if new_events > 0 {
405 self.current_minute_events
406 .fetch_add(new_events, Ordering::Relaxed);
407 }
408
409 let current_hour = now / 3600;
411 let last_hour = self.last_hour.load(Ordering::Relaxed);
412
413 if current_hour != last_hour {
414 if self
415 .last_hour
416 .compare_exchange(
417 last_hour,
418 current_hour,
419 Ordering::Relaxed,
420 Ordering::Relaxed,
421 )
422 .is_ok()
423 {
424 self.current_hour_events
425 .store(new_events, Ordering::Relaxed);
426 } else {
427 self.current_hour_events
428 .fetch_add(new_events, Ordering::Relaxed);
429 }
430 } else if new_events > 0 {
431 self.current_hour_events
432 .fetch_add(new_events, Ordering::Relaxed);
433 }
434 }
435}
436
437impl Default for RateMeter {
438 #[inline]
439 fn default() -> Self {
440 Self::new()
441 }
442}
443
444impl std::fmt::Display for RateMeter {
445 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446 write!(f, "RateMeter({:.1}/s, {} total)", self.rate(), self.total())
447 }
448}
449
450impl std::fmt::Debug for RateMeter {
451 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452 let stats = self.stats();
453 f.debug_struct("RateMeter")
454 .field("total_events", &stats.total_events)
455 .field("per_second", &stats.per_second)
456 .field("per_minute", &stats.per_minute)
457 .field("average_rate", &stats.average_rate)
458 .field("age", &stats.age)
459 .finish()
460 }
461}
462
463unsafe impl Send for RateMeter {}
465unsafe impl Sync for RateMeter {}
466
467pub mod specialized {
469 use super::*;
470
471 #[repr(align(64))]
473 pub struct ApiRateLimiter {
474 meter: RateMeter,
475 limit: AtomicU32, }
477
478 impl ApiRateLimiter {
479 #[inline]
481 pub fn new(requests_per_second: u32) -> Self {
482 Self {
483 meter: RateMeter::new(),
484 limit: AtomicU32::new(requests_per_second),
485 }
486 }
487
488 #[inline]
490 pub fn try_request(&self) -> bool {
491 let limit = self.limit.load(Ordering::Relaxed) as f64;
492 self.meter.tick_if_under_limit(limit)
493 }
494
495 #[inline]
497 pub fn try_requests(&self, n: u32) -> bool {
498 let limit = self.limit.load(Ordering::Relaxed) as f64;
499 self.meter.tick_burst_if_under_limit(n, limit)
500 }
501
502 #[inline]
504 pub fn set_limit(&self, requests_per_second: u32) {
505 self.limit.store(requests_per_second, Ordering::Relaxed);
506 }
507
508 #[inline]
510 pub fn get_limit(&self) -> u32 {
511 self.limit.load(Ordering::Relaxed)
512 }
513
514 #[inline]
516 pub fn current_rate(&self) -> f64 {
517 self.meter.rate()
518 }
519
520 #[inline]
522 pub fn total_requests(&self) -> u64 {
523 self.meter.total()
524 }
525
526 #[inline]
528 pub fn is_over_limit(&self) -> bool {
529 let limit = self.limit.load(Ordering::Relaxed) as f64;
530 self.meter.rate() > limit
531 }
532
533 #[inline]
535 pub fn reset(&self) {
536 self.meter.reset();
537 }
538 }
539
540 impl Default for ApiRateLimiter {
541 fn default() -> Self {
542 Self::new(1000)
543 } }
545
546 #[repr(align(64))]
548 pub struct ThroughputMeter {
549 meter: RateMeter,
550 }
551
552 impl ThroughputMeter {
553 #[inline]
555 pub fn new() -> Self {
556 Self {
557 meter: RateMeter::new(),
558 }
559 }
560
561 #[inline(always)]
563 pub fn record_bytes(&self, bytes: u64) {
564 self.meter.tick_n(bytes as u32);
565 }
566
567 #[inline]
569 pub fn bytes_per_second(&self) -> f64 {
570 self.meter.rate()
571 }
572
573 #[inline]
575 pub fn kb_per_second(&self) -> f64 {
576 self.meter.rate() / 1024.0
577 }
578
579 #[inline]
581 pub fn mb_per_second(&self) -> f64 {
582 self.meter.rate() / (1024.0 * 1024.0)
583 }
584
585 #[inline]
587 pub fn gb_per_second(&self) -> f64 {
588 self.meter.rate() / (1024.0 * 1024.0 * 1024.0)
589 }
590
591 #[inline]
593 pub fn total_bytes(&self) -> u64 {
594 self.meter.total()
595 }
596
597 #[inline]
599 pub fn reset(&self) {
600 self.meter.reset();
601 }
602 }
603
604 impl Default for ThroughputMeter {
605 fn default() -> Self {
606 Self::new()
607 }
608 }
609}
610
611#[cfg(test)]
612mod tests {
613 use super::*;
614 use std::sync::Arc;
615 use std::thread;
616
617 #[test]
618 fn test_basic_operations() {
619 let meter = RateMeter::new();
620
621 assert!(meter.is_empty());
622 assert_eq!(meter.total(), 0);
623 assert_eq!(meter.rate(), 0.0);
624
625 meter.tick();
626 assert!(!meter.is_empty());
627 assert_eq!(meter.total(), 1);
628
629 meter.tick_n(5);
630 assert_eq!(meter.total(), 6);
631 }
632
633 #[test]
634 fn test_rate_calculations() {
635 let meter = RateMeter::new();
636
637 for _ in 0..100 {
639 meter.tick();
640 }
641
642 let rate = meter.rate();
643 assert_eq!(rate, 100.0);
644 assert_eq!(meter.rate_per_second(), 100.0);
645 }
646
647 #[test]
648 fn test_multiple_windows() {
649 let meter = RateMeter::new();
650
651 for _ in 0..60 {
653 meter.tick();
654 }
655
656 let stats = meter.stats();
657 assert_eq!(stats.total_events, 60);
658 assert_eq!(stats.per_second, 60.0);
659 assert_eq!(stats.per_minute, 60.0);
660 assert_eq!(stats.per_hour, 60.0);
661 }
662
663 #[test]
664 fn test_rate_limiting() {
665 let meter = RateMeter::new();
666
667 assert!(meter.tick_if_under_limit(10.0));
669 assert!(meter.tick_if_under_limit(10.0));
670
671 meter.tick_n(8);
673
674 assert!(!meter.tick_if_under_limit(10.0));
676 assert!(meter.exceeds_rate(9.0));
677 assert!(!meter.exceeds_rate(11.0));
678 }
679
680 #[test]
681 fn test_burst_rate_limiting() {
682 let meter = RateMeter::new();
683
684 assert!(meter.tick_burst_if_under_limit(5, 10.0));
686 assert_eq!(meter.total(), 5);
687
688 assert!(!meter.tick_burst_if_under_limit(10, 10.0));
690 assert_eq!(meter.total(), 5); assert!(meter.tick_burst_if_under_limit(3, 10.0));
694 assert_eq!(meter.total(), 8);
695 }
696
697 #[test]
698 fn test_can_allow() {
699 let meter = RateMeter::new();
700
701 meter.tick_n(5);
702
703 assert!(meter.can_allow(3, 10.0)); assert!(!meter.can_allow(6, 10.0)); assert!(meter.can_allow(5, 10.0)); }
707
708 #[test]
709 fn test_reset() {
710 let meter = RateMeter::new();
711
712 meter.tick_n(100);
713 assert_eq!(meter.total(), 100);
714 assert!(meter.rate() > 0.0);
715
716 meter.reset();
717 assert_eq!(meter.total(), 0);
718 assert_eq!(meter.rate(), 0.0);
719 assert!(meter.is_empty());
720 }
721
722 #[test]
723 fn test_statistics() {
724 let meter = RateMeter::new();
725
726 meter.tick_n(50);
727
728 let stats = meter.stats();
729 assert_eq!(stats.total_events, 50);
730 assert_eq!(stats.per_second, 50.0);
731 assert!(stats.average_rate > 0.0);
732 assert!(stats.age > Duration::from_nanos(0));
733 assert!(stats.window_fill >= 0.0);
734 }
735
736 #[test]
737 fn test_api_rate_limiter() {
738 let limiter = specialized::ApiRateLimiter::new(10);
739
740 for _ in 0..10 {
742 assert!(limiter.try_request());
743 }
744
745 assert!(!limiter.try_request());
747
748 assert_eq!(limiter.current_rate(), 10.0);
750 assert_eq!(limiter.total_requests(), 10);
751 assert_eq!(limiter.get_limit(), 10);
752
753 limiter.set_limit(20);
755 assert_eq!(limiter.get_limit(), 20);
756 assert!(!limiter.is_over_limit()); limiter.reset();
760 assert!(limiter.try_requests(5));
761 assert_eq!(limiter.total_requests(), 5);
762
763 assert!(!limiter.try_requests(20)); assert_eq!(limiter.total_requests(), 5); }
766
767 #[test]
768 fn test_throughput_meter() {
769 let meter = specialized::ThroughputMeter::new();
770
771 meter.record_bytes(1024); assert_eq!(meter.bytes_per_second(), 1024.0);
773 assert_eq!(meter.kb_per_second(), 1.0);
774 assert_eq!(meter.total_bytes(), 1024);
775
776 meter.record_bytes(1024 * 1024); assert_eq!(meter.total_bytes(), 1024 + 1024 * 1024);
778 assert!((meter.mb_per_second() - 1.001).abs() < 0.01);
779 }
780
781 #[test]
782 fn test_high_concurrency() {
783 let meter = Arc::new(RateMeter::new());
784 let num_threads = 50;
785 let ticks_per_thread = 1000;
786
787 let handles: Vec<_> = (0..num_threads)
788 .map(|_| {
789 let meter = Arc::clone(&meter);
790 thread::spawn(move || {
791 for _ in 0..ticks_per_thread {
792 meter.tick();
793 }
794 })
795 })
796 .collect();
797
798 for handle in handles {
799 handle.join().unwrap();
800 }
801
802 assert_eq!(meter.total(), num_threads * ticks_per_thread);
803
804 let stats = meter.stats();
805 assert!(stats.average_rate > 0.0);
806 assert_eq!(stats.total_events, num_threads * ticks_per_thread);
807 }
808
809 #[test]
810 fn test_concurrent_rate_limiting() {
811 let limiter = Arc::new(specialized::ApiRateLimiter::new(100));
812 let num_threads = 20;
813
814 let handles: Vec<_> = (0..num_threads)
815 .map(|_| {
816 let limiter = Arc::clone(&limiter);
817 thread::spawn(move || {
818 let mut successful = 0;
819 for _ in 0..10 {
820 if limiter.try_request() {
821 successful += 1;
822 }
823 }
824 successful
825 })
826 })
827 .collect();
828
829 let total_successful: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();
830
831 let upper_bound = if cfg!(coverage) { 160 } else { 120 };
835 assert!(
836 total_successful <= upper_bound,
837 "total_successful={} > upper_bound={}",
838 total_successful,
839 upper_bound,
840 );
841 assert!(
842 total_successful >= 90,
843 "total_successful={} < lower_bound=90",
844 total_successful,
845 ); }
847
848 #[test]
849 fn test_display_and_debug() {
850 let meter = RateMeter::new();
851 meter.tick_n(42);
852
853 let display_str = format!("{meter}");
854 assert!(display_str.contains("RateMeter"));
855 assert!(display_str.contains("42 total"));
856
857 let debug_str = format!("{meter:?}");
858 assert!(debug_str.contains("RateMeter"));
859 assert!(debug_str.contains("total_events"));
860 }
861
862 #[test]
863 fn test_custom_window() {
864 let meter = RateMeter::with_window(Duration::from_secs(5));
865
866 meter.tick_n(10);
867 assert_eq!(meter.total(), 10);
868 assert_eq!(meter.rate(), 10.0);
869
870 let stats = meter.stats();
871 assert!(stats.window_fill >= 0.0);
872 }
873
874 #[test]
876 fn test_try_tick_and_try_tick_n_ok() {
877 let meter = RateMeter::new();
878 assert!(meter.try_tick().is_ok());
879 assert!(meter.try_tick_n(5).is_ok());
880 assert_eq!(meter.total(), 6);
881 }
882
883 #[test]
884 fn test_try_tick_n_total_overflow() {
885 let meter = RateMeter::new();
886 meter.total_events.store(u64::MAX - 1, Ordering::Relaxed);
888 let err = meter.try_tick_n(2).unwrap_err();
890 assert_eq!(err, MetricsError::Overflow);
891 }
892
893 #[test]
894 fn test_try_tick_n_window_overflow() {
895 let meter = RateMeter::new();
896 let now = meter.get_unix_timestamp();
898 meter.last_second.store(now, Ordering::Relaxed);
899 meter.last_minute.store(now / 60, Ordering::Relaxed);
900 meter.last_hour.store(now / 3600, Ordering::Relaxed);
901
902 meter
903 .current_second_events
904 .store(u32::MAX - 1, Ordering::Relaxed);
905 meter
906 .current_minute_events
907 .store(u32::MAX - 1, Ordering::Relaxed);
908 meter
909 .current_hour_events
910 .store(u32::MAX - 1, Ordering::Relaxed);
911
912 let err = meter.try_tick_n(2).unwrap_err();
914 assert_eq!(err, MetricsError::Overflow);
915 }
916
917 #[test]
918 fn test_try_tick_if_under_limit() {
919 let meter = RateMeter::new();
920 assert!(meter.try_tick_if_under_limit(10.0).unwrap());
922 assert!(meter.try_tick_n(8).is_ok());
924 assert!(meter.try_tick_if_under_limit(10.0).unwrap());
926 assert!(!meter.try_tick_if_under_limit(10.0).unwrap());
928 }
929}
930
931#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
932#[allow(unused_imports)]
933mod benchmarks {
934 use super::*;
935 use std::time::Instant;
936
937 #[cfg_attr(not(feature = "bench-tests"), ignore)]
938 #[test]
939 fn bench_rate_meter_tick() {
940 let meter = RateMeter::new();
941 let iterations = 10_000_000;
942
943 let start = Instant::now();
944 for _ in 0..iterations {
945 meter.tick();
946 }
947 let elapsed = start.elapsed();
948
949 println!(
950 "RateMeter tick: {:.2} ns/op",
951 elapsed.as_nanos() as f64 / iterations as f64
952 );
953
954 assert_eq!(meter.total(), iterations);
955 assert!(elapsed.as_nanos() / (iterations as u128) < 400);
957 }
958
959 #[cfg_attr(not(feature = "bench-tests"), ignore)]
960 #[test]
961 fn bench_rate_meter_tick_n() {
962 let meter = RateMeter::new();
963 let iterations = 1_000_000;
964
965 let start = Instant::now();
966 for i in 0..iterations {
967 meter.tick_n((i % 10) + 1);
968 }
969 let elapsed = start.elapsed();
970
971 println!(
972 "RateMeter tick_n: {:.2} ns/op",
973 elapsed.as_nanos() as f64 / iterations as f64
974 );
975
976 assert!(elapsed.as_nanos() / (iterations as u128) < 500);
978 }
979
980 #[cfg_attr(not(feature = "bench-tests"), ignore)]
981 #[test]
982 fn bench_rate_calculation() {
983 let meter = RateMeter::new();
984
985 meter.tick_n(1000);
987
988 let iterations = 1_000_000;
989 let start = Instant::now();
990
991 for _ in 0..iterations {
992 let _ = meter.rate();
993 }
994
995 let elapsed = start.elapsed();
996 println!(
997 "RateMeter rate: {:.2} ns/op",
998 elapsed.as_nanos() as f64 / iterations as f64
999 );
1000
1001 assert!(elapsed.as_nanos() / iterations < 300);
1003 }
1004
1005 #[cfg_attr(not(feature = "bench-tests"), ignore)]
1006 #[test]
1007 fn bench_api_rate_limiter() {
1008 let limiter = specialized::ApiRateLimiter::new(1_000_000); let iterations = 1_000_000;
1010
1011 let start = Instant::now();
1012 for _ in 0..iterations {
1013 let _ = limiter.try_request();
1014 }
1015 let elapsed = start.elapsed();
1016
1017 println!(
1018 "ApiRateLimiter try_request: {:.2} ns/op",
1019 elapsed.as_nanos() as f64 / iterations as f64
1020 );
1021
1022 assert!(elapsed.as_nanos() / iterations < 1000);
1024 }
1025}