Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement BBR #950

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 71 additions & 43 deletions src/minmax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,33 @@
// every new min and overwrites 2nd & 3rd choices. The same property
// holds for 2nd & 3rd best.

use std::time::Duration;
use std::time::Instant;
use std::ops::Div;
use std::ops::Sub;

#[derive(Copy, Clone)]
struct MinmaxSample<T> {
time: Instant,
value: T,
struct MinmaxSample<I, V> {
time: I,
value: V,
}

pub struct Minmax<T> {
estimate: [MinmaxSample<T>; 3],
pub struct Minmax<I, V> {
estimate: [MinmaxSample<I, V>; 3],
}

impl<T: PartialOrd + Copy> Minmax<T> {
pub fn new(val: T) -> Self {
impl<D, I, V> Minmax<I, V>
where
D: Copy + PartialEq + PartialOrd + Div<u32, Output = D>,
I: Copy + PartialEq + Sub<I, Output = D>,
V: Copy + PartialOrd,
{
pub fn new(time: I, val: V) -> Self {
Minmax {
estimate: [MinmaxSample {
time: Instant::now(),
value: val,
}; 3],
estimate: [MinmaxSample { time, value: val }; 3],
}
}

/// Resets the estimates to the given value.
pub fn reset(&mut self, time: Instant, meas: T) -> T {
pub fn reset(&mut self, time: I, meas: V) -> V {
let val = MinmaxSample { time, value: meas };

for i in self.estimate.iter_mut() {
Expand All @@ -87,10 +89,10 @@ impl<T: PartialOrd + Copy> Minmax<T> {
}

/// Updates the min estimate based on the given measurement, and returns it.
pub fn running_min(&mut self, win: Duration, time: Instant, meas: T) -> T {
pub fn running_min(&mut self, win: D, time: I, meas: V) -> V {
let val = MinmaxSample { time, value: meas };

let delta_time = time.duration_since(self.estimate[2].time);
let delta_time = time - self.estimate[2].time;

// Reset if there's nothing in the window or a new min value is found.
if val.value <= self.estimate[0].value || delta_time > win {
Expand All @@ -108,10 +110,10 @@ impl<T: PartialOrd + Copy> Minmax<T> {
}

/// Updates the max estimate based on the given measurement, and returns it.
pub fn _running_max(&mut self, win: Duration, time: Instant, meas: T) -> T {
pub fn running_max(&mut self, win: D, time: I, meas: V) -> V {
let val = MinmaxSample { time, value: meas };

let delta_time = time.duration_since(self.estimate[2].time);
let delta_time = time - self.estimate[2].time;

// Reset if there's nothing in the window or a new max value is found.
if val.value >= self.estimate[0].value || delta_time > win {
Expand All @@ -129,10 +131,10 @@ impl<T: PartialOrd + Copy> Minmax<T> {
}

/// As time advances, update the 1st, 2nd and 3rd estimates.
fn subwin_update(&mut self, win: Duration, time: Instant, meas: T) -> T {
fn subwin_update(&mut self, win: D, time: I, meas: V) -> V {
let val = MinmaxSample { time, value: meas };

let delta_time = time.duration_since(self.estimate[0].time);
let delta_time = time - self.estimate[0].time;

if delta_time > win {
// Passed entire window without a new val so make 2nd estimate the
Expand All @@ -143,20 +145,20 @@ impl<T: PartialOrd + Copy> Minmax<T> {
self.estimate[1] = self.estimate[2];
self.estimate[2] = val;

if time.duration_since(self.estimate[0].time) > win {
if time - self.estimate[0].time > win {
self.estimate[0] = self.estimate[1];
self.estimate[1] = self.estimate[2];
self.estimate[2] = val;
}
} else if self.estimate[1].time == self.estimate[0].time &&
delta_time > win.div_f32(4.0)
delta_time > win / 4
{
// We've passed a quarter of the window without a new val so take a
// 2nd estimate from the 2nd quarter of the window.
self.estimate[2] = val;
self.estimate[1] = val;
} else if self.estimate[2].time == self.estimate[1].time &&
delta_time > win.div_f32(2.0)
delta_time > win / 2
{
// We've passed half the window without finding a new val so take a
// 3rd estimate from the last half of the window.
Expand All @@ -170,10 +172,12 @@ impl<T: PartialOrd + Copy> Minmax<T> {
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
use std::time::Instant;

#[test]
fn reset_filter_rtt() {
let mut f = Minmax::new(Duration::new(0, 0));
let mut f = Minmax::new(Instant::now(), Duration::new(0, 0));
let now = Instant::now();
let rtt = Duration::from_millis(50);

Expand All @@ -192,7 +196,7 @@ mod tests {

#[test]
fn reset_filter_bandwidth() {
let mut f = Minmax::new(0);
let mut f = Minmax::new(Instant::now(), 0);
let now = Instant::now();
let bw = 2000;

Expand All @@ -211,7 +215,7 @@ mod tests {

#[test]
fn get_windowed_min_rtt() {
let mut f = Minmax::new(Duration::new(0, 0));
let mut f = Minmax::new(Instant::now(), Duration::new(0, 0));
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let win = Duration::from_millis(500);
Expand All @@ -235,7 +239,7 @@ mod tests {

#[test]
fn get_windowed_min_bandwidth() {
let mut f = Minmax::new(0);
let mut f = Minmax::new(Instant::now(), 0);
let bw_200 = 200;
let bw_500 = 500;
let win = Duration::from_millis(500);
Expand All @@ -259,7 +263,7 @@ mod tests {

#[test]
fn get_windowed_max_rtt() {
let mut f = Minmax::new(Duration::new(0, 0));
let mut f = Minmax::new(Instant::now(), Duration::new(0, 0));
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let win = Duration::from_millis(500);
Expand All @@ -269,21 +273,21 @@ mod tests {
assert_eq!(rtt_max, rtt_24);

time += Duration::from_millis(250);
rtt_max = f._running_max(win, time, rtt_25);
rtt_max = f.running_max(win, time, rtt_25);
assert_eq!(rtt_max, rtt_25);
assert_eq!(f.estimate[1].value, rtt_25);
assert_eq!(f.estimate[2].value, rtt_25);

time += Duration::from_millis(600);
rtt_max = f._running_max(win, time, rtt_24);
rtt_max = f.running_max(win, time, rtt_24);
assert_eq!(rtt_max, rtt_24);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_24);
}

#[test]
fn get_windowed_max_bandwidth() {
let mut f = Minmax::new(0);
let mut f = Minmax::new(Instant::now(), 0);
let bw_200 = 200;
let bw_500 = 500;
let win = Duration::from_millis(500);
Expand All @@ -293,21 +297,21 @@ mod tests {
assert_eq!(bw_max, bw_200);

time += Duration::from_millis(5000);
bw_max = f._running_max(win, time, bw_500);
bw_max = f.running_max(win, time, bw_500);
assert_eq!(bw_max, bw_500);
assert_eq!(f.estimate[1].value, bw_500);
assert_eq!(f.estimate[2].value, bw_500);

time += Duration::from_millis(600);
bw_max = f._running_max(win, time, bw_200);
bw_max = f.running_max(win, time, bw_200);
assert_eq!(bw_max, bw_200);
assert_eq!(f.estimate[1].value, bw_200);
assert_eq!(f.estimate[2].value, bw_200);
}

#[test]
fn get_windowed_min_estimates_rtt() {
let mut f = Minmax::new(Duration::new(0, 0));
let mut f = Minmax::new(Instant::now(), Duration::new(0, 0));
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let rtt_23 = Duration::from_millis(23);
Expand Down Expand Up @@ -339,7 +343,7 @@ mod tests {

#[test]
fn get_windowed_min_estimates_bandwidth() {
let mut f = Minmax::new(0);
let mut f = Minmax::new(Instant::now(), 0);
let bw_500 = 500;
let bw_400 = 400;
let bw_300 = 300;
Expand Down Expand Up @@ -371,7 +375,7 @@ mod tests {

#[test]
fn get_windowed_max_estimates_rtt() {
let mut f = Minmax::new(Duration::new(0, 0));
let mut f = Minmax::new(Instant::now(), Duration::new(0, 0));
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let rtt_23 = Duration::from_millis(23);
Expand All @@ -383,27 +387,27 @@ mod tests {
assert_eq!(rtt_max, rtt_25);

time += Duration::from_millis(300);
rtt_max = f._running_max(win, time, rtt_24);
rtt_max = f.running_max(win, time, rtt_24);
assert_eq!(rtt_max, rtt_25);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_24);

time += Duration::from_millis(300);
rtt_max = f._running_max(win, time, rtt_23);
rtt_max = f.running_max(win, time, rtt_23);
assert_eq!(rtt_max, rtt_25);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_23);

time += Duration::from_millis(300);
rtt_max = f._running_max(win, time, rtt_26);
rtt_max = f.running_max(win, time, rtt_26);
assert_eq!(rtt_max, rtt_26);
assert_eq!(f.estimate[1].value, rtt_26);
assert_eq!(f.estimate[2].value, rtt_26);
}

#[test]
fn get_windowed_max_estimates_bandwidth() {
let mut f = Minmax::new(0);
let mut f = Minmax::new(Instant::now(), 0);
let bw_500 = 500;
let bw_400 = 400;
let bw_300 = 300;
Expand All @@ -415,21 +419,45 @@ mod tests {
assert_eq!(bw_max, bw_500);

time += Duration::from_millis(300);
bw_max = f._running_max(win, time, bw_400);
bw_max = f.running_max(win, time, bw_400);
assert_eq!(bw_max, bw_500);
assert_eq!(f.estimate[1].value, bw_400);
assert_eq!(f.estimate[2].value, bw_400);

time += Duration::from_millis(300);
bw_max = f._running_max(win, time, bw_300);
bw_max = f.running_max(win, time, bw_300);
assert_eq!(bw_max, bw_500);
assert_eq!(f.estimate[1].value, bw_400);
assert_eq!(f.estimate[2].value, bw_300);

time += Duration::from_millis(300);
bw_max = f._running_max(win, time, bw_600);
bw_max = f.running_max(win, time, bw_600);
assert_eq!(bw_max, bw_600);
assert_eq!(f.estimate[1].value, bw_600);
assert_eq!(f.estimate[2].value, bw_600);
}

#[test]
fn get_windowed_min_bandwidth_ints() {
let mut f = Minmax::new(0 as u32, 0);
let bw_200 = 200;
let bw_500 = 500;
let win = 2;
let mut time = 0;

let mut bw_min = f.reset(time, bw_500);
assert_eq!(bw_min, bw_500);

time += 1;
bw_min = f.running_min(win, time, bw_200);
assert_eq!(bw_min, bw_200);
assert_eq!(f.estimate[1].value, bw_200);
assert_eq!(f.estimate[2].value, bw_200);

time += 3;
bw_min = f.running_min(win, time, bw_500);
assert_eq!(bw_min, bw_500);
assert_eq!(f.estimate[1].value, bw_500);
assert_eq!(f.estimate[2].value, bw_500);
}
}
Loading