@@ -75,14 +75,14 @@ pub const CUBIC_FAST_CONVERGENCE: f64 = 0.85; // (1.0 + CUBIC_BETA) / 2.0;
75
75
/// this value reduces the magnitude of the resulting growth by a constant factor.
76
76
/// A value of 1.0 would mean a return to the rate used in slow start.
77
77
///
78
- /// UPDATE: Not found in RFC. What is the reason for this and should we reconsider ?
78
+ /// UPDATE: Not found in RFC. I don't exactly understand why we're doing this ?
79
79
const EXPONENTIAL_GROWTH_REDUCTION : f64 = 2.0 ;
80
80
81
81
/// Convert an integer congestion window value into a floating point value.
82
82
/// This has the effect of reducing larger values to `1<<53`.
83
83
/// If you have a congestion window that large, something is probably wrong.
84
84
///
85
- /// UPDATE: not found in RFC.
85
+ /// UPDATE: not part of RFC.
86
86
pub fn convert_to_f64 ( v : usize ) -> f64 {
87
87
let mut f_64 = f64:: from ( u32:: try_from ( v >> 21 ) . unwrap_or ( u32:: MAX ) ) ;
88
88
f_64 *= 2_097_152.0 ; // f_64 <<= 21
@@ -302,15 +302,17 @@ impl WindowAdjustment for Cubic {
302
302
// <https://datatracker.ietf.org/doc/html/rfc9438#section-4.2-11.1>
303
303
//
304
304
// And the `cwnd` increase of `target - cwnd / cwnd` only applies here,
305
- // not in the Reno-friendly region. So that needs to be adjusted. See wording:
305
+ // not in the Reno-friendly region. So that needs to be adjusted. Right now
306
+ // we are doing the `target - cwnd / cwnd` part for all regions I think.
307
+ // See wording:
306
308
//
307
- // > cwnd SHOULD be set to w_est
309
+ // > cwnd SHOULD be set to w_est ("set to")
308
310
//
309
311
// <https://datatracker.ietf.org/doc/html/rfc9438#section-4.3-8>
310
312
//
311
313
// vs.
312
314
//
313
- // > cwnd MUST be incremented by `target - cwnd / cwnd`
315
+ // > cwnd MUST be incremented by `target - cwnd / cwnd` ("incremented by")
314
316
//
315
317
// <https://datatracker.ietf.org/doc/html/rfc9438#name-convex-region>
316
318
let time_ca = self
@@ -331,9 +333,17 @@ impl WindowAdjustment for Cubic {
331
333
//
332
334
// <https://datatracker.ietf.org/doc/html/rfc9438#name-reno-friendly-region>
333
335
//
334
- // UPDATE: This is handled differently in the new RFC, but it's also
335
- // different from the original RFC. Still need to understand what exactly
336
- // is going on here.
336
+ // UPDATE/QUESTION: This is handled differently in the new RFC, but our implementation is also
337
+ // different from the original RFC. Still need to understand what exactly is going on here.
338
+ //
339
+ // Also note:
340
+ //
341
+ // > Once w_est has grown to reach the cwnd at the time of most recently setting
342
+ // > ssthresh -- that is, w_est >= cwnd_prior -- the sender SHOULD set CUBIC_ALPHA to
343
+ // > 1 to ensure that it can achieve the same congestion window increment rate
344
+ // > as Reno, which uses AIMD(1, 0.5).
345
+ //
346
+ // <https://datatracker.ietf.org/doc/html/rfc9438#section-4.3-11>
337
347
let max_datagram_size = convert_to_f64 ( max_datagram_size) ;
338
348
let tcp_cnt = self . estimated_tcp_cwnd / CUBIC_ALPHA ;
339
349
let incr = ( self . tcp_acked_bytes / tcp_cnt) . floor ( ) ;
@@ -345,7 +355,7 @@ impl WindowAdjustment for Cubic {
345
355
// Take the larger cwnd of Cubic concave or convex and Cubic Reno-friendly region.
346
356
//
347
357
// > When receiving a new ACK in congestion avoidance (where cwnd could be greater than
348
- // > or less than Wmax ), CUBIC checks whether Wcubic (t) is less than w_est. If so, CUBIC
358
+ // > or less than w_max ), CUBIC checks whether W_cubic (t) is less than w_est. If so, CUBIC
349
359
// > is in the Reno-friendly region and cwnd SHOULD be set to w_est at each reception of a new ACK.
350
360
//
351
361
// <https://datatracker.ietf.org/doc/html/rfc9438#section-4.3-8>
@@ -377,6 +387,41 @@ impl WindowAdjustment for Cubic {
377
387
acked_to_increase as usize
378
388
}
379
389
390
+ // UPDATE: CUBIC RFC 9438 changes the logic for multiplicative decrease and uses
391
+ // `bytes_in_flight` instead of `cwnd` for it's calculation:
392
+ //
393
+ // > ssthresh = bytes_in_flight * CUBIC_BETA
394
+ // > cwnd_prior = cwnd
395
+ // > if (reduction_on_loss) {
396
+ // > cwnd = max(ssthresh, 2*MSS)
397
+ // > } else if (reduction_on_ece) {
398
+ // > cwnd = max(ssthresh, 1*MSS)
399
+ // > }
400
+ // > ssthresh = max(ssthresh, 2*MSS)
401
+ //
402
+ // <https://datatracker.ietf.org/doc/html/rfc9438#figure-5>
403
+ //
404
+ // With the note:
405
+ //
406
+ // > A QUIC sender that uses a cwnd value to calculate new values for cwnd and ssthresh after
407
+ // > detecting a congestion event is REQUIRED to apply similar mechanisms [RFC9002].
408
+ //
409
+ // "similar mechanisms" refering to taking "measures to prevent cwnd from growing when the
410
+ // volume of bytes in flight is smaller than cwnd".
411
+ //
412
+ // QUESTION: Do we already do this somewhere?
413
+ //
414
+ // <https://datatracker.ietf.org/doc/html/rfc9438#section-4.6>
415
+ //
416
+ // We are setting ssthresh in `on_congestion_event()` in `classic_cc.rs` after having returned `cwnd`
417
+ // from `reduce_cwnd()` below. That is code that isn't CUBIC specific though, so we can add a condition
418
+ // there to check which cc algorithm we're using and do the `ssthresh|cwnd = max(...)` outlined above
419
+ // for CUBIC only.
420
+ //
421
+ // (or maybe don't do it at all because QUIC has a minimum congestion window of 2*MSS, as mentioned
422
+ // further below)
423
+ // QUESTION: Which takes precedence? The minimum congestion window of 2*MSS for QUIC or CUBIC wanting to set
424
+ // cwnd to 1*MSS on reduction by ECE?
380
425
fn reduce_cwnd (
381
426
& mut self ,
382
427
curr_cwnd : usize ,
@@ -386,12 +431,26 @@ impl WindowAdjustment for Cubic {
386
431
let curr_cwnd_f64 = convert_to_f64 ( curr_cwnd) ;
387
432
// Fast Convergence
388
433
//
389
- // If congestion event occurs before the maximum congestion window before the last
390
- // congestion event, we reduce the the maximum congestion window and thereby W_max.
391
- // check cwnd + MAX_DATAGRAM_SIZE instead of cwnd because with cwnd in bytes, cwnd may be
434
+ // > During a congestion event, if the current cwnd is less than w_max, this indicates
435
+ // > that the saturation point experienced by this flow is getting reduced because of
436
+ // > a change in available bandwidth. This flow can then release more bandwidth by
437
+ // > reducing w_max further. This action effectively lengthens the time for this flow
438
+ // > to increase its congestion window, because the reduced w_max forces the flow to
439
+ // > plateau earlier. This allows more time for the new flow to catch up to its
440
+ // > congestion window size.
441
+ //
442
+ // Check cwnd + MAX_DATAGRAM_SIZE instead of cwnd because with cwnd in bytes, cwnd may be
392
443
// slightly off.
393
444
//
394
- // <https://www.rfc-editor.org/rfc/rfc8312#section-4.6>
445
+ // UPDATE: New logic for fast convergence.
446
+ //
447
+ // if (cwnd < w_max AND fast_convergence_enabled) {
448
+ // w_max = cwnd * CUBIC_FAST_CONVERGENCE;
449
+ // } else {
450
+ // w_max = cwnd;
451
+ // }
452
+ //
453
+ // <https://datatracker.ietf.org/doc/html/rfc9438#name-fast-convergence>
395
454
self . last_max_cwnd =
396
455
if curr_cwnd_f64 + convert_to_f64 ( max_datagram_size) < self . last_max_cwnd {
397
456
curr_cwnd_f64 * CUBIC_FAST_CONVERGENCE
@@ -424,4 +483,32 @@ impl WindowAdjustment for Cubic {
424
483
425
484
// UPDATE: Things to remember that didn't make it anywhere else:
426
485
//
427
- // - what about `ssthresh`
486
+ // 1. Minimum congestion window:
487
+ //
488
+ // > Note that CUBIC MUST continue to reduce cwnd in response to congestion events
489
+ // > detected by ECN-Echo ACKs until it reaches a value of 1 SMSS. If congestion events
490
+ // > indicated by ECN-Echo ACKs persist, a sender with a cwnd of 1 SMSS MUST reduce its
491
+ // > sending rate even further. This can be achieved by using a retransmission timer
492
+ // > with exponential backoff, as described in [RFC3168].
493
+ //
494
+ // <https://datatracker.ietf.org/doc/html/rfc9438#section-4.6-7> VERSUS
495
+ //
496
+ // > QUIC therefore recommends that the minimum congestion window be two packets.
497
+ //
498
+ // <https://datatracker.ietf.org/doc/html/rfc9002#section-4.8>
499
+ //
500
+ // QUESTION: So this probably does not apply to CUBIC on QUIC and we never go below 2*MSS
501
+ // (as is currently implemented)?
502
+ //
503
+ // 2. Timeout: <https://datatracker.ietf.org/doc/html/rfc9438#section-4.8>
504
+ //
505
+ // > QUIC does not collapse the congestion window when the PTO expires, since a single
506
+ // > packet loss at the tail does not indicate persistent congestion.
507
+ //
508
+ // <https://datatracker.ietf.org/doc/html/rfc9002#section-4.7>
509
+ //
510
+ // QUESTION: Timeout (RTO/PTO) does not apply to us then?
511
+ //
512
+ // 3. Spurious fast retransmits: <https://datatracker.ietf.org/doc/html/rfc9438#section-4.9.2>
513
+ //
514
+ // QUESTION: Do we want to implement this? If yes then we may be able to do it in a follow up PR.
0 commit comments