@@ -18,7 +18,7 @@ use crate::{cc::classic_cc::SlowStart, packet, rtt::RttEstimate, stats::Congesti
1818
1919/// The outcome of a single SEARCH evaluation.
2020#[ derive( Debug , PartialEq , Eq ) ]
21- pub enum Result {
21+ pub enum Outcome {
2222 /// Evaluation ran and slow start should be exited with the provided cwnd.
2323 Exit ( usize ) ,
2424 /// Evaluation ran and slow start should be continued.
@@ -228,6 +228,53 @@ impl Search {
228228 sent / Self :: SCALE as u64
229229 }
230230
231+ /// Evaluates whether SEARCH should exit slow start.
232+ ///
233+ /// Returns [`Outcome::Exit`] with the current cwnd if the normalized delivery-rate
234+ /// difference exceeds [`Self::THRESH`], or a non-exit variant explaining why not.
235+ fn evaluate ( & self , rtt : Duration , curr_idx : usize , curr_cwnd : usize ) -> Outcome {
236+ // Compute how many bins fit in the last RTT. Integer division implicitly floors that value,
237+ // so `prev_idx` might be too recent by a fraction of a bin. Said fraction is scaled to
238+ // `0..[Self::SCALE]` for interpolation in `compute_sent`.
239+ let ( prev_idx, fraction) = self . calc_prev_idx ( rtt, curr_idx) ;
240+ qdebug ! ( "SEARCH: evaluate: prev_idx {prev_idx} curr_idx {curr_idx} fraction {fraction}" ) ;
241+
242+ if prev_idx <= Self :: W {
243+ qdebug ! ( "SEARCH: evaluate: not enough data for SEARCH evaluation (warming up)" ) ;
244+ return Outcome :: WarmingUp ;
245+ }
246+ if curr_idx - prev_idx >= Self :: EXTRA_BINS {
247+ qdebug ! ( "SEARCH: evaluate: not enough data for SEARCH evaluation (RTT inflated)" ) ;
248+ return Outcome :: RttInflated ;
249+ }
250+
251+ let curr_delv = self . compute_delv ( curr_idx - Self :: W , curr_idx) ;
252+ let prev_sent = self . compute_sent ( prev_idx - Self :: W , prev_idx, fraction) ;
253+ qdebug ! ( "SEARCH: evaluate: curr_delv {curr_delv} prev_sent {prev_sent}" ) ;
254+
255+ if prev_sent == 0 {
256+ qdebug ! ( "SEARCH: evaluate: prev_sent is zero, can't evaluate" ) ;
257+ return Outcome :: ZeroSent ;
258+ }
259+
260+ let diff = prev_sent. saturating_sub ( curr_delv) ;
261+ let norm_diff =
262+ usize:: try_from ( diff * Self :: SCALE as u64 / prev_sent) . unwrap_or ( usize:: MAX ) ;
263+
264+ if norm_diff < Self :: THRESH {
265+ qdebug ! (
266+ "SEARCH: evaluate: norm_diff {norm_diff} < THRESH {} --> continue" ,
267+ Self :: THRESH
268+ ) ;
269+ return Outcome :: Continue ;
270+ }
271+ qdebug ! (
272+ "SEARCH: evaluate: norm_diff {norm_diff} >= THRESH {} --> exit" ,
273+ Self :: THRESH
274+ ) ;
275+ Outcome :: Exit ( curr_cwnd)
276+ }
277+
231278 #[ cfg( test) ]
232279 pub const fn curr_idx ( & self ) -> Option < usize > {
233280 self . curr_idx
@@ -273,56 +320,9 @@ impl Search {
273320
274321 /// Re-exports the internal `evaluate` function for use in tests.
275322 #[ cfg( test) ]
276- pub fn evaluate_test ( & self , rtt : Duration , curr_idx : usize , curr_cwnd : usize ) -> Result {
323+ pub fn evaluate_test ( & self , rtt : Duration , curr_idx : usize , curr_cwnd : usize ) -> Outcome {
277324 self . evaluate ( rtt, curr_idx, curr_cwnd)
278325 }
279-
280- /// Evaluates whether SEARCH should exit slow start.
281- ///
282- /// Returns [`Result::Exit`] with the current cwnd if the normalized delivery-rate
283- /// difference exceeds [`Self::THRESH`], or a non-exit variant explaining why not.
284- fn evaluate ( & self , rtt : Duration , curr_idx : usize , curr_cwnd : usize ) -> Result {
285- // Compute how many bins fit in the last RTT. Integer division implicitly floors that value,
286- // so `prev_idx` might be too recent by a fraction of a bin. Said fraction is scaled to
287- // `0..[Self::SCALE]` for interpolation in `compute_sent`.
288- let ( prev_idx, fraction) = self . calc_prev_idx ( rtt, curr_idx) ;
289- qdebug ! ( "SEARCH: evaluate: prev_idx {prev_idx} curr_idx {curr_idx} fraction {fraction}" ) ;
290-
291- if prev_idx <= Self :: W {
292- qdebug ! ( "SEARCH: evaluate: not enough data for SEARCH evaluation (warming up)" ) ;
293- return Result :: WarmingUp ;
294- }
295- if curr_idx - prev_idx >= Self :: EXTRA_BINS {
296- qdebug ! ( "SEARCH: evaluate: not enough data for SEARCH evaluation (RTT inflated)" ) ;
297- return Result :: RttInflated ;
298- }
299-
300- let curr_delv = self . compute_delv ( curr_idx - Self :: W , curr_idx) ;
301- let prev_sent = self . compute_sent ( prev_idx - Self :: W , prev_idx, fraction) ;
302- qdebug ! ( "SEARCH: evaluate: curr_delv {curr_delv} prev_sent {prev_sent}" ) ;
303-
304- if prev_sent == 0 {
305- qdebug ! ( "SEARCH: evaluate: prev_sent is zero, can't evaluate" ) ;
306- return Result :: ZeroSent ;
307- }
308-
309- let diff = prev_sent. saturating_sub ( curr_delv) ;
310- let norm_diff =
311- usize:: try_from ( diff * Self :: SCALE as u64 / prev_sent) . unwrap_or ( usize:: MAX ) ;
312-
313- if norm_diff < Self :: THRESH {
314- qdebug ! (
315- "SEARCH: evaluate: norm_diff {norm_diff} < THRESH {} --> continue" ,
316- Self :: THRESH
317- ) ;
318- return Result :: Continue ;
319- }
320- qdebug ! (
321- "SEARCH: evaluate: norm_diff {norm_diff} >= THRESH {} --> exit" ,
322- Self :: THRESH
323- ) ;
324- Result :: Exit ( curr_cwnd)
325- }
326326}
327327
328328impl SlowStart for Search {
@@ -365,7 +365,7 @@ impl SlowStart for Search {
365365 //
366366 // <https://datatracker.ietf.org/doc/html/draft-chung-ccwg-search-09#section-3.2-17>
367367 match self . evaluate ( rtt, curr_idx, curr_cwnd) {
368- Result :: Exit ( cwnd) => Some ( cwnd) ,
368+ Outcome :: Exit ( cwnd) => Some ( cwnd) ,
369369 _ => None ,
370370 }
371371 }
0 commit comments