Skip to content

Commit c433337

Browse files
committed
fix(bench): use small warmup messages to avoid cwnd exhaustion
Stream transfers (>1.4KB) require the sender to call recv() to process ACKs from the receiver. Without this, LEDBAT flightsize never decreases and cwnd fills up, blocking further sends. The select! pattern to call send() and recv() concurrently fails due to borrow checker constraints. This fix uses small warmup messages (1000 bytes) that fit in a single packet and complete immediately without needing ACK processing. The warmup still establishes RTT estimates for LEDBAT, and the initial cwnd (38KB) provides plenty of room for the 16KB measured transfer. Changes: - transport_direct.rs: New direct benchmark (no criterion) for simpler async handling and better debugging - slow_start.rs: Use 1000 byte warmup messages instead of 16KB Both transport_direct and transport_ci benchmarks now pass reliably: - Cold start: 10/10 succeeded - Warm connection: 10/10 succeeded - All RTT scenarios: 5/5 succeeded
1 parent a299fd6 commit c433337

File tree

3 files changed

+516
-32
lines changed

3 files changed

+516
-32
lines changed

crates/core/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ required-features = ["bench"]
151151
name = "transport_manual"
152152
required-features = ["bench"]
153153

154+
# Direct benchmarks (no criterion, simpler async handling)
155+
[[bench]]
156+
name = "transport_direct"
157+
harness = false
158+
required-features = ["bench"]
159+
154160
# Full benchmark suite (manual, ~78 min, requires bench_full feature)
155161
[[bench]]
156162
name = "transport_full"

crates/core/benches/transport/slow_start.rs

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,18 @@ pub fn bench_cold_start_throughput(c: &mut Criterion) {
7474
// Spawn receiver task (peer A receives) - matching test pattern
7575
let receiver = tokio::spawn(async move {
7676
let conn_future = peer_a.connect(peer_b_pub, peer_b_addr).await;
77-
let mut conn = match tokio::time::timeout(Duration::from_secs(5), conn_future)
78-
.await
79-
{
80-
Ok(Ok(c)) => c,
81-
Ok(Err(e)) => {
82-
eprintln!("cold_start receiver connect failed: {:?}", e);
83-
return 0;
84-
}
85-
Err(_) => {
86-
eprintln!("cold_start receiver connect timeout");
87-
return 0;
88-
}
89-
};
77+
let mut conn =
78+
match tokio::time::timeout(Duration::from_secs(5), conn_future).await {
79+
Ok(Ok(c)) => c,
80+
Ok(Err(e)) => {
81+
eprintln!("cold_start receiver connect failed: {:?}", e);
82+
return 0;
83+
}
84+
Err(_) => {
85+
eprintln!("cold_start receiver connect timeout");
86+
return 0;
87+
}
88+
};
9089

9190
match tokio::time::timeout(Duration::from_secs(5), conn.recv()).await {
9291
Ok(Ok(received)) => {
@@ -107,19 +106,18 @@ pub fn bench_cold_start_throughput(c: &mut Criterion) {
107106
// Spawn sender task (peer B sends) - matching test pattern
108107
let sender = tokio::spawn(async move {
109108
let conn_future = peer_b.connect(peer_a_pub, peer_a_addr).await;
110-
let mut conn = match tokio::time::timeout(Duration::from_secs(5), conn_future)
111-
.await
112-
{
113-
Ok(Ok(c)) => c,
114-
Ok(Err(e)) => {
115-
eprintln!("cold_start sender connect failed: {:?}", e);
116-
return false;
117-
}
118-
Err(_) => {
119-
eprintln!("cold_start sender connect timeout");
120-
return false;
121-
}
122-
};
109+
let mut conn =
110+
match tokio::time::timeout(Duration::from_secs(5), conn_future).await {
111+
Ok(Ok(c)) => c,
112+
Ok(Err(e)) => {
113+
eprintln!("cold_start sender connect failed: {:?}", e);
114+
return false;
115+
}
116+
Err(_) => {
117+
eprintln!("cold_start sender connect timeout");
118+
return false;
119+
}
120+
};
123121

124122
// 100ms delay to ensure receiver is ready (required by mock transport)
125123
tokio::time::sleep(Duration::from_millis(100)).await;
@@ -156,13 +154,15 @@ pub fn bench_cold_start_throughput(c: &mut Criterion) {
156154
/// Benchmark warm connection throughput
157155
///
158156
/// Creates fresh connections inside each iteration to work with mock transport.
159-
/// Each iteration: connect → warmup (3 transfers) → measured transfer
157+
/// Each iteration: connect → warmup (3 small transfers) → measured transfer
160158
///
161159
/// This measures realistic throughput including LEDBAT warmup effects.
160+
/// Uses small warmup messages (<1.4KB) to avoid cwnd exhaustion, then measures
161+
/// full-size transfer throughput.
162162
///
163163
/// **Pattern**: Uses `tokio::spawn` for sender/receiver tasks with
164164
/// synchronization delay (100ms, required by mock transport).
165-
/// Warmup and measured transfers happen within spawned tasks.
165+
/// Small warmup transfers establish RTT estimates without blocking cwnd.
166166
pub fn bench_warm_connection_throughput(c: &mut Criterion) {
167167
use freenet::transport::mock_transport::create_mock_peer;
168168

@@ -204,7 +204,9 @@ pub fn bench_warm_connection_throughput(c: &mut Criterion) {
204204
};
205205

206206
const WARMUP_COUNT: usize = 3;
207-
let warmup_size = 16 * 1024; // 16KB warmup messages
207+
// Use small warmup messages that fit in a single packet (no streaming)
208+
// This avoids cwnd exhaustion while still warming LEDBAT RTT estimates
209+
let warmup_size = 1000; // < MAX_DATA_SIZE (~1463 bytes)
208210

209211
// Spawn receiver task - receives warmup + measured transfers
210212
let receiver = tokio::spawn(async move {
@@ -270,18 +272,19 @@ pub fn bench_warm_connection_throughput(c: &mut Criterion) {
270272
// 100ms delay to ensure receiver is ready (required by mock transport)
271273
tokio::time::sleep(Duration::from_millis(100)).await;
272274

273-
// Send warmup transfers
275+
// Send small warmup transfers (single packet each, no streaming)
274276
for i in 0..WARMUP_COUNT {
275277
let warmup_msg = vec![0xABu8; warmup_size];
276278
if let Err(e) = conn.send(warmup_msg).await {
277279
eprintln!("warm warmup send {} failed: {:?}", i, e);
278280
return false;
279281
}
280-
// Delay between warmup sends to let receiver process
282+
// Short delay between warmup sends
281283
tokio::time::sleep(Duration::from_millis(100)).await;
282284
}
283285

284-
// Send measured transfer
286+
// Send measured transfer (16KB - requires streaming)
287+
// Initial cwnd (38KB) minus small warmups (~3KB) leaves plenty of room
285288
let message = vec![0xABu8; transfer_size];
286289
match conn.send(message).await {
287290
Ok(()) => true,

0 commit comments

Comments
 (0)