use super::{RateCounter, RateCounterImmut};
use std::time::{Duration, Instant};
#[derive(Clone, Copy)]
pub struct DiscreteRateCounter {
updates_since_clear: u64,
time_at_last_clear: Instant,
rate: f64,
samples: u64,
}
impl DiscreteRateCounter {
pub fn new(samples: u64) -> Self {
DiscreteRateCounter {
updates_since_clear: 0,
time_at_last_clear: Instant::now(),
rate: 0.0,
samples: samples,
}
}
pub fn rate_age_cycles(&self) -> u64 {
self.updates_since_clear
}
pub fn rate_age_duration(&self) -> Duration {
self.time_at_last_clear.elapsed()
}
}
impl RateCounter for DiscreteRateCounter {
fn samples(&self) -> u64 {
self.samples
}
fn set_samples(&mut self, samples: u64) {
self.samples = samples
}
fn update(&mut self) {
self.updates_since_clear += 1;
if self.updates_since_clear >= self.samples {
let elapsed = self.time_at_last_clear.elapsed();
let real_time_since_clear =
elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 * 1.0e-9;
self.rate = self.updates_since_clear as f64 / real_time_since_clear;
self.time_at_last_clear = Instant::now();
self.updates_since_clear = 0;
}
}
fn rate(&self) -> f64 {
self.rate
}
}
impl RateCounterImmut for DiscreteRateCounter {
fn update_immut(self) -> Self {
let mut new = self;
new.update();
new
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_discrete_rate_counter() {
let mut c = DiscreteRateCounter::new(10);
assert!(
c.rate() == 0.0,
"Counter should have no data before it gets enough samples."
);
let sample_period = Duration::from_millis(10);
for i in 1..11 {
let start = Instant::now();
while start.elapsed() < sample_period {}
c.update();
assert!(
c.rate_age_cycles() == i % 10,
"Rate age not in sync with cycle loop! {} loops but ras = {}",
i,
c.rate_age_cycles()
);
}
let difference = 100.0 - c.rate();
println!("Rate was {}", c.rate());
assert!(
difference < 10.0,
"Counter rate should be closer to actual rate."
);
}
#[test]
fn test_discrete_rate_counter_immut() {
let mut c = DiscreteRateCounter::new(10);
assert!(
c.rate() == 0.0,
"Counter should have no data before it gets enough samples."
);
let sample_period = Duration::from_millis(10);
for i in 1..11 {
let start = Instant::now();
while start.elapsed() < sample_period {}
c = c.update_immut();
assert!(
c.rate_age_cycles() == i % 10,
"Rate age not in sync with cycle loop! {} loops but ras = {}",
i,
c.rate_age_cycles()
);
}
let difference = 100.0 - c.rate();
println!("Rate was {}", c.rate());
assert!(
difference < 10.0,
"Counter rate should be closer to actual rate."
);
}
}