Skip to content

Commit

Permalink
docs(transport/cubic): add references to RFC 8312 (#2344)
Browse files Browse the repository at this point in the history
* docs(transport/cubic): add references to RFC 8312

* Use htmlized

* Clippy

* Clippy
  • Loading branch information
mxinden authored Jan 14, 2025
1 parent 8bc926a commit 7bbf900
Showing 1 changed file with 82 additions and 8 deletions.
90 changes: 82 additions & 8 deletions neqo-transport/src/cc/cubic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! CUBIC congestion control
use std::{
fmt::{self, Display},
time::{Duration, Instant},
Expand All @@ -13,19 +15,38 @@ use neqo_common::qtrace;

use crate::cc::classic_cc::WindowAdjustment;

// CUBIC congestion control

// C is a constant fixed to determine the aggressiveness of window
// increase in high BDP networks.
/// > C is a constant fixed to determine the aggressiveness of window
/// > increase in high BDP networks.
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.1>
///
/// See discussion for rational for concrete value.
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-5.1>
pub const CUBIC_C: f64 = 0.4;
/// TCP-friendly region additive factor
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.2>
pub const CUBIC_ALPHA: f64 = 3.0 * (1.0 - 0.7) / (1.0 + 0.7);

// CUBIC_BETA = 0.7;
/// `CUBIC_BETA` = 0.7;
///
/// > Principle 4: To balance between the scalability and convergence speed,
/// > CUBIC sets the multiplicative window decrease factor to 0.7 while Standard
/// > TCP uses 0.5. While this improves the scalability of CUBIC, a side effect
/// > of this decision is slower convergence, especially under low statistical
/// > multiplexing environments.
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-3>
pub const CUBIC_BETA_USIZE_DIVIDEND: usize = 7;
pub const CUBIC_BETA_USIZE_DIVISOR: usize = 10;

/// The fast convergence ratio further reduces the congestion window when a congestion event
/// occurs before reaching the previous `W_max`.
/// The fast convergence ratio further reduces the congestion window when a
/// congestion event occurs before reaching the previous `W_max`.
///
/// See formula defined below.
///
/// <https://www.rfc-editor.org/rfc/rfc8312#section-4.6>
pub const CUBIC_FAST_CONVERGENCE: f64 = 0.85; // (1.0 + CUBIC_BETA) / 2.0;

/// The minimum number of multiples of the datagram size that need
Expand All @@ -47,11 +68,41 @@ pub fn convert_to_f64(v: usize) -> f64 {

#[derive(Debug)]
pub struct Cubic {
/// Maximum Window size two congestion events ago.
///
/// > With fast convergence, when a congestion event occurs, before the
/// > window reduction of the congestion window, a flow remembers the last
/// > value of W_max before it updates W_max for the current congestion
/// > event.
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.6>
last_max_cwnd: f64,
/// Estimate of Standard TCP congestion window for Cubic's TCP-friendly
/// Region.
///
/// > Standard TCP performs well in certain types of networks, for example,
/// > under short RTT and small bandwidth (or small BDP) networks. In
/// > these networks, we use the TCP-friendly region to ensure that CUBIC
/// > achieves at least the same throughput as Standard TCP.
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.2>
estimated_tcp_cwnd: f64,
/// > K is the time period that the above function takes to increase the
/// > current window size to W_max if there are no further congestion events
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.1>
k: f64,
/// > W_max is the window size just before the window is reduced in the last
/// > congestion event.
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.1>
w_max: f64,
/// > the elapsed time from the beginning of the current congestion
/// > avoidance
///
/// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.1>
ca_epoch_start: Option<Instant>,
/// Number of bytes acked since the last Standard TCP congestion window increase.
tcp_acked_bytes: f64,
}

Expand Down Expand Up @@ -91,12 +142,16 @@ impl Cubic {
///
/// From that equation we can calculate K as:
/// K = cubic_root((W_max - W_cubic) / C / MSS);
///
/// <https://www.rfc-editor.org/rfc/rfc8312#section-4.1>
fn calc_k(&self, curr_cwnd: f64, max_datagram_size: usize) -> f64 {
((self.w_max - curr_cwnd) / CUBIC_C / convert_to_f64(max_datagram_size)).cbrt()
}

/// W_cubic(t) = C*(t-K)^3 + W_max (Eq. 1)
/// t is relative to the start of the congestion avoidance phase and it is in seconds.
///
/// <https://www.rfc-editor.org/rfc/rfc8312#section-4.1>
fn w_cubic(&self, t: f64, max_datagram_size: usize) -> f64 {
(CUBIC_C * (t - self.k).powi(3)).mul_add(convert_to_f64(max_datagram_size), self.w_max)
}
Expand Down Expand Up @@ -144,6 +199,10 @@ impl WindowAdjustment for Cubic {
self.tcp_acked_bytes += new_acked_f64;
}

// Cubic concave or convex region
//
// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.3>
// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.4>
let time_ca = self
.ca_epoch_start
.map_or(min_rtt, |t| {
Expand All @@ -158,6 +217,9 @@ impl WindowAdjustment for Cubic {
.as_secs_f64();
let target_cubic = self.w_cubic(time_ca, max_datagram_size);

// Cubic TCP-friendly region
//
// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.2>
let max_datagram_size = convert_to_f64(max_datagram_size);
let tcp_cnt = self.estimated_tcp_cwnd / CUBIC_ALPHA;
let incr = (self.tcp_acked_bytes / tcp_cnt).floor();
Expand All @@ -166,10 +228,19 @@ impl WindowAdjustment for Cubic {
self.estimated_tcp_cwnd += incr * max_datagram_size;
}

// Take the larger cwnd of Cubic concave or convex and Cubic
// TCP-friendly region.
//
// > When receiving an ACK in congestion avoidance (cwnd could be
// > greater than or less than W_max), CUBIC checks whether W_cubic(t) is
// > less than W_est(t). If so, CUBIC is in the TCP-friendly region and
// > cwnd SHOULD be set to W_est(t) at each reception of an ACK.
//
// <https://datatracker.ietf.org/doc/html/rfc8312#section-4.2>
let target_cwnd = target_cubic.max(self.estimated_tcp_cwnd);

// Calculate the number of bytes that would need to be acknowledged for an increase
// of `MAX_DATAGRAM_SIZE` to match the increase of `target - cwnd / cwnd` as defined
// of `max_datagram_size` to match the increase of `target - cwnd / cwnd` as defined
// in the specification (Sections 4.4 and 4.5).
// The amount of data required therefore reduces asymptotically as the target increases.
// If the target is not significantly higher than the congestion window, require a very
Expand All @@ -191,10 +262,13 @@ impl WindowAdjustment for Cubic {
) -> (usize, usize) {
let curr_cwnd_f64 = convert_to_f64(curr_cwnd);
// Fast Convergence
//
// If congestion event occurs before the maximum congestion window before the last
// congestion event, we reduce the the maximum congestion window and thereby W_max.
// check cwnd + MAX_DATAGRAM_SIZE instead of cwnd because with cwnd in bytes, cwnd may be
// slightly off.
//
// <https://www.rfc-editor.org/rfc/rfc8312#section-4.6>
self.last_max_cwnd =
if curr_cwnd_f64 + convert_to_f64(max_datagram_size) < self.last_max_cwnd {
curr_cwnd_f64 * CUBIC_FAST_CONVERGENCE
Expand Down

0 comments on commit 7bbf900

Please sign in to comment.