in neqo-transport/src/fc.rs [1120:1223]
fn auto_tuning_approximates_bandwidth_delay_product() -> Res<()> {
const DATA_FRAME_SIZE: u64 = 1_500;
/// Allow auto-tuning algorithm to be off from actual bandwidth-delay
/// product by up to 1KiB.
const TOLERANCE: u64 = 1024;
test_fixture::fixture_init();
// Run multiple iterations with randomized bandwidth and rtt.
for _ in 0..1_000 {
// Random bandwidth between 1 Mbit/s and 1 Gbit/s.
let bandwidth =
u64::from(u16::from_be_bytes(random::<2>()) % 1_000 + 1) * 1_000 * 1_000;
// Random delay between 1 ms and 256 ms.
let rtt = Duration::from_millis(u64::from(random::<1>()[0]) + 1);
let bdp = bandwidth * u64::try_from(rtt.as_millis()).unwrap() / 1_000 / 8;
let mut now = Instant::now();
let mut send_to_recv = VecDeque::new();
let mut recv_to_send = VecDeque::new();
let mut last_max_active = INITIAL_RECV_WINDOW_SIZE as u64;
let mut last_max_active_changed = now;
let mut sender_window = INITIAL_RECV_WINDOW_SIZE as u64;
let mut fc =
ReceiverFlowControl::new(StreamId::new(0), INITIAL_RECV_WINDOW_SIZE as u64);
loop {
// Sender receives window updates.
if recv_to_send.front().is_some_and(|(at, _)| *at <= now) {
let (_, update) = recv_to_send.pop_front().unwrap();
sender_window += update;
}
// Sender sends data frames.
let sender_progressed = if sender_window > 0 {
let to_send = min(DATA_FRAME_SIZE, sender_window);
send_to_recv.push_back((now, to_send));
sender_window -= to_send;
now += Duration::from_secs_f64(to_send as f64 * 8.0 / bandwidth as f64);
true
} else {
false
};
// Receiver receives data frames.
let mut receiver_progressed = false;
if send_to_recv.front().is_some_and(|(at, _)| *at <= now) {
let (_, data) = send_to_recv.pop_front().unwrap();
let consumed = fc.set_consumed(fc.retired() + data)?;
fc.add_retired(consumed);
// Receiver sends window updates.
let prev_max_allowed = fc.max_allowed;
if write_frames(&mut fc, rtt, now) == 1 {
recv_to_send.push_front((now, fc.max_allowed - prev_max_allowed));
receiver_progressed = true;
if last_max_active < fc.max_active() {
last_max_active = fc.max_active();
last_max_active_changed = now;
}
}
}
// When idle, travel in (simulated) time.
if !sender_progressed && !receiver_progressed {
now = [recv_to_send.front(), send_to_recv.front()]
.into_iter()
.flatten()
.map(|(at, _)| *at)
.min()
.expect("both are None");
}
// Consider auto-tuning done once receive window hasn't changed for 4 RTT.
if now.duration_since(last_max_active_changed) > 4 * rtt {
break;
}
}
let summary = format!(
"Got receive window of {} MiB on connection with bandwidth {} MBit/s ({bandwidth} Bit/s), delay {rtt:?}, bdp {} MiB.",
fc.max_active() / 1024 / 1024,
bandwidth / 1_000 / 1_000,
bdp / 1024 / 1024,
);
assert!(
fc.max_active() + TOLERANCE >= bdp || fc.max_active() == MAX_RECV_WINDOW_SIZE,
"{summary} Receive window is smaller than the bdp."
);
assert!(
fc.max_active - TOLERANCE <= bdp
|| fc.max_active == INITIAL_RECV_WINDOW_SIZE as u64,
"{summary} Receive window is larger than the bdp."
);
qdebug!("{summary}");
}
Ok(())
}