Skip to content

Commit 1344129

Browse files
committed
address review comments
1 parent 80433f7 commit 1344129

2 files changed

Lines changed: 34 additions & 39 deletions

File tree

neqo-transport/src/cc/search.rs

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub enum Outcome {
2727
/// Not enough data to run SEARCH evaluation (expected early in the connection or after reset).
2828
WarmingUp,
2929
/// Can't run SEARCH evaluation because RTT inflated past the point where there isn't enough
30-
/// data to look back one RTT. Provides the amount of bins that SEARCH tried to look back.
30+
/// data to look back one RTT. Provides the number of bins that SEARCH tried to look back.
3131
RttInflated(usize),
3232
/// Haven't sent data for the last RTT thus can't evaluate.
3333
ZeroSent,
@@ -56,7 +56,7 @@ pub struct Search {
5656
/// Tracking amount of sent bytes on this connection. Is incremented on every sent packet.
5757
sent_bytes: usize,
5858
/// The RTT used to initialize SEARCH (set in [`Self::initialize`]).
59-
initial_rtt: Duration,
59+
initial_rtt: Option<Duration>,
6060
}
6161

6262
impl Display for Search {
@@ -98,7 +98,7 @@ impl Search {
9898
bin_duration: Duration::from_millis(0),
9999
acked_bytes: 0,
100100
sent_bytes: 0,
101-
initial_rtt: Duration::ZERO,
101+
initial_rtt: None,
102102
}
103103
}
104104

@@ -108,7 +108,7 @@ impl Search {
108108
reason = "casting small constant usize to u32 for use in Duration::div"
109109
)]
110110
fn initialize(&mut self, initial_rtt: Duration, now: Instant) {
111-
self.initial_rtt = initial_rtt;
111+
self.initial_rtt = Some(initial_rtt);
112112
// BIN_DURATION = WINDOW_SIZE / W = initial_rtt * WINDOW_SIZE_FACTOR / W
113113
self.bin_duration =
114114
initial_rtt * Self::WINDOW_SIZE_FACTOR / Self::SCALE as u32 / Self::W as u32;
@@ -165,12 +165,8 @@ impl Search {
165165
Self::W
166166
);
167167
cc_stats.search_reset.count += 1;
168-
cc_stats.search_reset.max_passed_bins = Some(
169-
cc_stats
170-
.search_reset
171-
.max_passed_bins
172-
.map_or(passed_bins, |c| c.max(passed_bins)),
173-
);
168+
cc_stats.search_reset.max_passed_bins =
169+
cc_stats.search_reset.max_passed_bins.max(Some(passed_bins));
174170
self.reset();
175171
return None;
176172
}
@@ -291,39 +287,44 @@ impl Search {
291287
Outcome::Exit(curr_cwnd)
292288
}
293289

290+
/// Converts an RTT duration to the number of bins it spans (rounded up).
291+
fn rtt_to_bins(&self, rtt: Duration) -> usize {
292+
let rtt_ns = u64::try_from(rtt.as_nanos()).unwrap_or(u64::MAX);
293+
let bin_ns = u64::try_from(self.bin_duration.as_nanos()).unwrap_or(u64::MAX);
294+
usize::try_from(rtt_ns.div_ceil(bin_ns)).unwrap_or(usize::MAX)
295+
}
296+
294297
/// SEARCH suggests a drain-phase to slowly converge towards a BDP estimate. We're currently not
295298
/// implementing this, but do record the calculated targets for evaluation in this function.
296299
///
297300
/// The draft specifies using the initial rtt to approximate an 'empty-buffer BDP'.
298301
///
299302
/// In addition also record an estimate using the current rtt to approximate BDP with full
300303
/// buffers. This should estimate the upper end of the CUBIC curve during congestion avoidance.
301-
#[expect(
302-
clippy::cast_possible_truncation,
303-
reason = "casting small u128 division result to usize"
304-
)]
305-
const fn record_target_cwnd_estimates(
304+
fn record_target_cwnd_estimates(
306305
&self,
307306
curr_idx: usize,
308307
rtt: Duration,
309308
cc_stats: &mut CongestionControlStats,
310309
) {
311-
let initial_rtt_bins = self
312-
.initial_rtt
313-
.as_nanos()
314-
.div_ceil(self.bin_duration.as_nanos()) as usize;
310+
// Only compute estimates if we have an initial RTT. This is a pure precaution, since we
311+
// should always have an initial RTT before this method is called.
312+
let Some(initial_rtt) = self.initial_rtt else {
313+
return;
314+
};
315+
let initial_rtt_bins = self.rtt_to_bins(initial_rtt);
315316
let initial_rtt_cong_idx = curr_idx.saturating_sub(initial_rtt_bins);
316317
let empty_buffer_target = self.compute_delv(initial_rtt_cong_idx, curr_idx);
317318

318-
cc_stats.search_empty_buffer_target = Some(empty_buffer_target as usize);
319+
cc_stats.search_empty_buffer_target = Some(empty_buffer_target);
319320

320321
// Only compute full_buffer_target when the current RTT fits within the acked_bins
321322
// circular buffer (W + 1 slots). Beyond that, modulo indexing reads overwritten entries.
322-
let current_rtt_bins = rtt.as_nanos().div_ceil(self.bin_duration.as_nanos()) as usize;
323+
let current_rtt_bins = self.rtt_to_bins(rtt);
323324
if current_rtt_bins <= Self::W {
324325
let current_rtt_cong_idx = curr_idx.saturating_sub(current_rtt_bins);
325-
cc_stats.search_full_buffer_target =
326-
Some(self.compute_delv(current_rtt_cong_idx, curr_idx) as usize);
326+
let full_buffer_target = self.compute_delv(current_rtt_cong_idx, curr_idx);
327+
cc_stats.search_full_buffer_target = Some(full_buffer_target);
327328
}
328329
}
329330

@@ -391,8 +392,8 @@ impl SlowStart for Search {
391392
// Guard on the stats fields so post-reset ACKs don't overwrite the initial samples.
392393
if cc_stats.search_first_rtt.is_none() {
393394
cc_stats.search_first_rtt = Some(rtt);
394-
} else if cc_stats.search_second_rtt.is_none() {
395-
cc_stats.search_second_rtt = Some(rtt);
395+
} else {
396+
cc_stats.search_second_rtt.get_or_insert(rtt);
396397
}
397398

398399
// Initialize on first ACK.
@@ -430,19 +431,13 @@ impl SlowStart for Search {
430431
Some(cwnd)
431432
}
432433
Outcome::RttInflated(lookback_bins) => {
433-
cc_stats.search_lookback_bins_needed = Some(
434-
cc_stats
435-
.search_lookback_bins_needed
436-
.map_or(lookback_bins, |c| c.max(lookback_bins)),
437-
);
434+
cc_stats.search_lookback_bins_needed = cc_stats
435+
.search_lookback_bins_needed
436+
.max(Some(lookback_bins));
438437
None
439438
}
440439
Outcome::Continue(norm_diff) => {
441-
cc_stats.search_max_norm_diff = Some(
442-
cc_stats
443-
.search_max_norm_diff
444-
.map_or(norm_diff, |c| c.max(norm_diff)),
445-
);
440+
cc_stats.search_max_norm_diff = cc_stats.search_max_norm_diff.max(Some(norm_diff));
446441
None
447442
}
448443
Outcome::ZeroSent => {

neqo-transport/src/stats.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,11 @@ pub struct CongestionControlStats {
186186
/// enabled. Higher values indicate the heuristic spent more time throttling slow start growth.
187187
pub hystart_css_rounds_finished: usize,
188188
/// Drain-phase target estimate for the BDP with empty buffers. None if we haven't exited slow
189-
/// start through SEARCH.
190-
pub search_empty_buffer_target: Option<usize>,
189+
/// start through SEARCH. Is `u64` because Firefox uses it as such.
190+
pub search_empty_buffer_target: Option<u64>,
191191
/// Drain-phase target estimate for the BDP with full buffers. None if we haven't exited slow
192-
/// start through SEARCH.
193-
pub search_full_buffer_target: Option<usize>,
192+
/// start through SEARCH. Is `u64` because Firefox uses it as such.
193+
pub search_full_buffer_target: Option<u64>,
194194
/// Records the maximum value of lookback bins needed due to RTT inflation. Fires whenever
195195
/// SEARCH can't run because there is not enough data for lookback. Is `None` if SEARCH never
196196
/// ran into this issue.

0 commit comments

Comments
 (0)