Skip to content

Commit c59e284

Browse files
committed
added BBRv3 algorithm
1 parent 958ce8a commit c59e284

File tree

4 files changed

+1398
-0
lines changed

4 files changed

+1398
-0
lines changed

perf/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ pub fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
201201
pub enum CongestionAlgorithm {
202202
Cubic,
203203
Bbr,
204+
Bbr3,
204205
NewReno,
205206
}
206207

@@ -209,6 +210,7 @@ impl CongestionAlgorithm {
209210
match self {
210211
CongestionAlgorithm::Cubic => Arc::new(congestion::CubicConfig::default()),
211212
CongestionAlgorithm::Bbr => Arc::new(congestion::BbrConfig::default()),
213+
CongestionAlgorithm::Bbr3 => Arc::new(congestion::Bbr3Config::default()),
212214
CongestionAlgorithm::NewReno => Arc::new(congestion::NewRenoConfig::default()),
213215
}
214216
}

quinn-proto/src/congestion.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ use std::any::Any;
66
use std::sync::Arc;
77

88
mod bbr;
9+
mod bbr3;
910
mod cubic;
1011
mod new_reno;
1112

1213
pub use bbr::{Bbr, BbrConfig};
14+
pub use bbr3::{Bbr3, Bbr3Config};
1315
pub use cubic::{Cubic, CubicConfig};
1416
pub use new_reno::{NewReno, NewRenoConfig};
1517

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Based on Google code released under BSD license here:
3+
* https://groups.google.com/forum/#!topic/bbr-dev/3RTgkzi5ZD8
4+
*/
5+
6+
/*
7+
* Kathleen Nichols' algorithm for tracking the minimum (or maximum)
8+
* value of a data stream over some fixed time interval. (E.g.,
9+
* the minimum RTT over the past five minutes.) It uses constant
10+
* space and constant time per update yet almost always delivers
11+
* the same minimum as an implementation that has to keep all the
12+
* data in the window.
13+
*
14+
* The algorithm keeps track of the best, 2nd best & 3rd best min
15+
* values, maintaining an invariant that the measurement time of
16+
* the n'th best >= n-1'th best. It also makes sure that the three
17+
* values are widely separated in the time window since that bounds
18+
* the worse case error when that data is monotonically increasing
19+
* over the window.
20+
*
21+
* Upon getting a new min, we can forget everything earlier because
22+
* it has no value - the new min is <= everything else in the window
23+
* by definition and it samples the most recent. So we restart fresh on
24+
* every new min and overwrites 2nd & 3rd choices. The same property
25+
* holds for 2nd & 3rd best.
26+
*/
27+
28+
use std::fmt::Debug;
29+
30+
#[derive(Copy, Clone, Debug)]
31+
pub(super) struct MinMax {
32+
/// round count, not a timestamp
33+
window: u64,
34+
samples: [MinMaxSample; 3],
35+
}
36+
37+
impl MinMax {
38+
pub(super) fn new(window: u64) -> Self {
39+
Self {
40+
window,
41+
samples: [Default::default(); 3],
42+
}
43+
}
44+
pub(super) fn get(&self) -> u64 {
45+
self.samples[0].value
46+
}
47+
48+
fn fill(&mut self, sample: MinMaxSample) {
49+
self.samples.fill(sample);
50+
}
51+
52+
/// update_min is also defined in the original source, but removed here since it is not used.
53+
pub(super) fn update_max(&mut self, current_round: u64, measurement: u64) {
54+
let sample = MinMaxSample {
55+
time: current_round,
56+
value: measurement,
57+
};
58+
59+
if self.samples[0].value == 0 /* uninitialised */
60+
|| /* found new max? */ sample.value >= self.samples[0].value
61+
|| /* nothing left in window? */ sample.time - self.samples[2].time > self.window
62+
{
63+
self.fill(sample); /* forget earlier samples */
64+
return;
65+
}
66+
67+
if sample.value >= self.samples[1].value {
68+
self.samples[2] = sample;
69+
self.samples[1] = sample;
70+
} else if sample.value >= self.samples[2].value {
71+
self.samples[2] = sample;
72+
}
73+
74+
self.subwin_update(sample);
75+
}
76+
77+
/* As time advances, update the 1st, 2nd, and 3rd choices. */
78+
fn subwin_update(&mut self, sample: MinMaxSample) {
79+
let dt = sample.time - self.samples[0].time;
80+
if dt > self.window {
81+
/*
82+
* Passed entire window without a new sample so make 2nd
83+
* choice the new sample & 3rd choice the new 2nd choice.
84+
* we may have to iterate this since our 2nd choice
85+
* may also be outside the window (we checked on entry
86+
* that the third choice was in the window).
87+
*/
88+
self.samples[0] = self.samples[1];
89+
self.samples[1] = self.samples[2];
90+
self.samples[2] = sample;
91+
if sample.time - self.samples[0].time > self.window {
92+
self.samples[0] = self.samples[1];
93+
self.samples[1] = self.samples[2];
94+
self.samples[2] = sample;
95+
}
96+
} else if self.samples[1].time == self.samples[0].time && dt > self.window / 4 {
97+
/*
98+
* We've passed a quarter of the window without a new sample
99+
* so take a 2nd choice from the 2nd quarter of the window.
100+
*/
101+
self.samples[2] = sample;
102+
self.samples[1] = sample;
103+
} else if self.samples[2].time == self.samples[1].time && dt > self.window / 2 {
104+
/*
105+
* We've passed half the window without finding a new sample
106+
* so take a 3rd choice from the last half of the window
107+
*/
108+
self.samples[2] = sample;
109+
}
110+
}
111+
}
112+
113+
impl Default for MinMax {
114+
fn default() -> Self {
115+
Self {
116+
window: 10,
117+
samples: [Default::default(); 3],
118+
}
119+
}
120+
}
121+
122+
#[derive(Debug, Copy, Clone, Default)]
123+
struct MinMaxSample {
124+
/// round number, not a timestamp
125+
time: u64,
126+
value: u64,
127+
}
128+
129+
#[cfg(test)]
130+
mod test {
131+
use super::*;
132+
133+
#[test]
134+
fn test() {
135+
let round = 25;
136+
let mut min_max = MinMax::default();
137+
min_max.update_max(round + 1, 100);
138+
assert_eq!(100, min_max.get());
139+
min_max.update_max(round + 3, 120);
140+
assert_eq!(120, min_max.get());
141+
min_max.update_max(round + 5, 160);
142+
assert_eq!(160, min_max.get());
143+
min_max.update_max(round + 7, 100);
144+
assert_eq!(160, min_max.get());
145+
min_max.update_max(round + 10, 100);
146+
assert_eq!(160, min_max.get());
147+
min_max.update_max(round + 14, 100);
148+
assert_eq!(160, min_max.get());
149+
min_max.update_max(round + 16, 100);
150+
assert_eq!(100, min_max.get());
151+
min_max.update_max(round + 18, 130);
152+
assert_eq!(130, min_max.get());
153+
}
154+
}

0 commit comments

Comments
 (0)