From 43471e0f2c0db60660935bb80e56a3ed65b78293 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 25 Apr 2024 16:31:25 +0200 Subject: [PATCH] test(transport): assert maximum bandwidth on gbit link This commit adds a basic smoke test using the `test-fixture` simulator, asserting the expected bandwidth on a 1 gbit link. Given https://github.com/mozilla/neqo/issues/733, the current expected bandwidth is limited by the fixed sized stream receive buffer (1MiB). --- neqo-transport/tests/network.rs | 44 ++++++++++++++++++++++++++++++++ test-fixture/src/sim/mod.rs | 4 ++- test-fixture/src/sim/taildrop.rs | 10 ++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/neqo-transport/tests/network.rs b/neqo-transport/tests/network.rs index 68a835a436..b3e69a72bd 100644 --- a/neqo-transport/tests/network.rs +++ b/neqo-transport/tests/network.rs @@ -196,3 +196,47 @@ fn transfer_fixed_seed() { sim.seed_str("117f65d90ee5c1a7fb685f3af502c7730ba5d31866b758d98f5e3c2117cf9b86"); sim.run(); } + +#[test] +#[allow(clippy::cast_precision_loss)] +fn gbit_bandwidth() { + const MIB: usize = 1024 * 1024; + const TRANSFER_AMOUNT: usize = 100 * MIB; + + for upload in [false, true] { + let sim = Simulator::new( + "gbit-bandwidth", + boxed![ + ConnectionNode::default_client(if upload { + boxed![SendData::new(TRANSFER_AMOUNT)] + } else { + boxed![ReceiveData::new(TRANSFER_AMOUNT)] + }), + TailDrop::gbit_link(), + ConnectionNode::default_server(if upload { + boxed![ReceiveData::new(TRANSFER_AMOUNT)] + } else { + boxed![SendData::new(TRANSFER_AMOUNT)] + }), + TailDrop::gbit_link() + ], + ); + + let simulated_time = sim.setup().run(); + let bandwidth = TRANSFER_AMOUNT as f64 * 8.0 / simulated_time.as_secs_f64(); + + // Given Neqo's current static stream receive buffer of 1MiB, maximum + // bandwidth is below gbit link bandwidth. + // + // Tracked in https://github.com/mozilla/neqo/issues/733. + let maximum_bandwidth = MIB as f64 * 8.0 / 0.1; // bandwidth-delay-product / delay = bandwidth + let expected_utilization = 0.5; + + assert!( + maximum_bandwidth * expected_utilization < bandwidth, + "with upload {upload} expected to reach {expected_utilization} of maximum bandwidth ({} Mbit/s) but got {} Mbit/s", + maximum_bandwidth / MIB as f64, + bandwidth / MIB as f64, + ); + } +} diff --git a/test-fixture/src/sim/mod.rs b/test-fixture/src/sim/mod.rs index 14c21dd75c..b9bdc09237 100644 --- a/test-fixture/src/sim/mod.rs +++ b/test-fixture/src/sim/mod.rs @@ -293,7 +293,8 @@ pub struct ReadySimulator { } impl ReadySimulator { - pub fn run(mut self) { + #[allow(clippy::must_use_candidate)] + pub fn run(mut self) -> Duration { let real_start = Instant::now(); let end = self.sim.process_loop(self.start, self.now); let sim_time = end - self.now; @@ -303,5 +304,6 @@ impl ReadySimulator { wall = real_start.elapsed(), ); self.sim.print_summary(); + sim_time } } diff --git a/test-fixture/src/sim/taildrop.rs b/test-fixture/src/sim/taildrop.rs index c85042546d..583aba5d96 100644 --- a/test-fixture/src/sim/taildrop.rs +++ b/test-fixture/src/sim/taildrop.rs @@ -90,6 +90,16 @@ impl TailDrop { Self::new(200_000, 8_192, Duration::from_millis(50)) } + /// A tail drop queue on a 1 Gbps link with the default forward delay of + /// 50ms and a buffer equal to the bandwidth-delay-product. + #[must_use] + pub const fn gbit_link() -> Self { + let rate = 1_000_000_000 / 8; + let delay = Duration::from_millis(50); + let capacity = rate / 20; // rate * 0.05 + Self::new(rate, capacity, delay) + } + /// How "big" is this datagram, accounting for overheads. /// This approximates by using the same overhead for storing in the queue /// and for sending on the wire.