CUBIC Congestion Control
CUBIC is a loss-based congestion-control algorithm that replaces the linear additive-increase of Reno with a cubic function of the wall-clock time elapsed since the last congestion event. Its window climbs fast when far below the last-known safe window, slows to a near-flat plateau as it approaches that remembered ceiling, pauses there, and then accelerates outward to probe for newly-available capacity. This shape makes CUBIC scale to high bandwidth-delay-product (BDP) links — where Reno’s
+1 segment/RTTclimb would take minutes or hours to refill a window — while remaining fair to Reno on ordinary paths and, crucially, keeping the window-growth rate independent of round-trip time (RTT), which greatly reduces the RTT-unfairness that biases Reno toward short-RTT flows. CUBIC’s current specification is RFC 9438 (“CUBIC for Fast and Long-Distance Networks,” Standards Track, August 2023), which obsoletes the earlier Informational RFC 8312 (February 2018). It originated in the 2008 paper by Ha, Rhee, and Xu (Ha et al. 2008) and has been the default TCP congestion control in Linux since kernel 2.6.19 (2006), and is also the default in Windows and Apple stacks (RFC 9438 §1). This note is the algorithm/RFC view; the Linux kernel implementation (tcp_cubic.c, the integer cube-root, HyStart) lives in the sibling TCP Congestion Control, and the AIMD foundation CUBIC builds on is Congestion Control Fundamentals and AIMD.
The Problem CUBIC Solves — Reno on Long Fat Pipes
The failing that motivated CUBIC is concrete and dramatic. Consider a path with a bandwidth of 10 Gbit/s and an RTT of 100 ms, carrying 1250-byte packets. The bandwidth-delay product — the amount of in-flight data needed to keep such a pipe completely full — is about 100,000 packets (Ha et al. 2008 §1). Now recall that AIMD-Reno, after a loss, halves its window to roughly 50,000 packets and then climbs back at one segment per RTT (the additive increase analysed in Congestion Control Fundamentals and AIMD). To crawl from 50,000 back to 100,000 packets takes 50,000 RTTs — about 5,000 seconds, or 1.4 hours (Ha et al. 2008 §1). Any transfer shorter than 1.4 hours — which is almost all of them — never fills the pipe, so the link runs “severely under-utilized.” The bigger and longer the link, the worse the penalty: Reno’s fixed +1/RTT increment simply does not scale with the window size it has to rebuild.
The obvious fix is to increase more aggressively when the window is large, and a family of “high-speed” TCPs (HSTCP, STCP, HTCP, FAST, Westwood, and BIC-TCP) did exactly that. CUBIC’s direct ancestor is BIC-TCP (Binary Increase Congestion control), which Linux adopted as its default in kernel 2.6.8 (2004) (Wikipedia: BIC TCP). BIC treats window growth as a binary search: after a loss at window W_max, the safe window lies somewhere between the reduced window W_min and W_max, so BIC jumps toward the midpoint, and if no loss occurs there it makes that the new minimum and searches again — a logarithmic, concave approach that spends a long time near W_max (good for stability) and probes exponentially (“max probing”) once it climbs past W_max to find a new ceiling (Ha et al. 2008 §3.1). BIC worked, but its growth function stitched together several distinct phases (binary search, max probing, with S_max/S_min bounds) that made it too aggressive on short-RTT or low-speed paths and hard to analyse. CUBIC’s insight was that a single odd-degree polynomial — a cubic — naturally has exactly the concave-then-convex shape BIC assembled by hand, so one clean function could replace the whole machine (Ha et al. 2008 §3.2).
Mental Model — A Cubic Curve Anchored at the Last Loss
Think of CUBIC as remembering the window size W_max at which the last congestion event happened, and then drawing a cubic curve through that point as a function of real time. The curve is deliberately flat right at W_max (a cubic has zero slope at its inflection point) and steep on either side:
flowchart LR LOSS["Congestion event<br/>record W_max<br/>cut: cwnd = 0.7 * W_max<br/>epoch time t = 0"] --> CONCAVE CONCAVE["CONCAVE region<br/>cwnd < W_max, t < K<br/>fast growth, decelerating<br/>W_cubic = C(t-K)^3 + W_max"] CONCAVE -->|"t reaches K"| PLATEAU["PLATEAU / inflection<br/>t = K, cwnd approx W_max<br/>near-flat: cautious around<br/>the last-known safe point"] PLATEAU -->|"t > K"| CONVEX["CONVEX region<br/>cwnd > W_max, t > K<br/>accelerating outward<br/>'max probing' for new capacity"] CONVEX -->|"new loss"| LOSS
The life of a CUBIC congestion epoch. What it shows: a loss records W_max and cuts the window to 0.7·W_max; the cubic then climbs steeply but decelerating through the concave region, flattens into a plateau exactly at the remembered W_max (reached at time t = K), and — if no loss occurs there — accelerates through the convex region, “max probing” for capacity the network may have gained. The insight to take: all the growth is a function of elapsed time t, not of how many ACKs or RTTs have passed. The plateau is where CUBIC is most cautious — it lingers near the last safe window rather than blasting past it — which is what gives it stability; the convex acceleration is what gives it the speed to fill a big pipe once it is confident the ceiling has risen.
The Cubic Window-Growth Function
CUBIC’s core equation, computed on each ACK in congestion avoidance (RFC 9438 §4.2; Ha et al. 2008 eq. 1):
W_cubic(t) = C * (t - K)^3 + W_max
Symbol by symbol:
W_cubic(t)— the target congestion window (in segments) at timet.t— seconds elapsed since the start of the current congestion-avoidance epoch (i.e. since the last window reduction). This is real elapsed time, read from a wall clock, not a count of RTTs — the single most important property of CUBIC.W_max— the window size in segments just before the last congestion event (RFC 9438 also calls the recorded valuecwnd_prior). It is the “saturation point” the curve is anchored to.C— the CUBIC scaling constant, fixed atC = 0.4(units of segments per second³), governing overall aggressiveness (RFC 9438 §4.1).K— the time it takes the cubic to climb from the post-reduction window back up toW_max(assuming no further loss):
K = cubic_root( W_max * (1 - beta_cubic) / C )
where beta_cubic = 0.7 is the multiplicative-decrease factor (below). At t = K the cube term is zero and W_cubic = W_max — the plateau. For t < K the term (t − K)³ is negative, so W_cubic < W_max (the concave region, rising toward the ceiling); for t > K it is positive, so W_cubic > W_max (the convex region, the “maximum probing phase” searching for new capacity) (RFC 9438 §4.2).
In practice CUBIC does not set cwnd to W_cubic(t) for the current instant; it looks one RTT ahead, computing target = W_cubic(t + RTT), and increments cwnd toward that target over the coming RTT (RFC 9438 §4.2). This is how a continuous curve is realised by discrete per-ACK increments. Because the kernel data path has no floating-point unit, the cube and cube-root are done in integer arithmetic; Linux originally used bisection and later switched to Newton-Raphson, cutting the cube-root cost roughly tenfold — an implementation detail owned by TCP Congestion Control (Ha et al. 2008 §4.1).
Multiplicative Decrease — 0.7, Not 0.5
On detecting a loss, CUBIC performs its multiplicative decrease (RFC 9438 §4.6):
ssthresh = cwnd * beta_cubic
cwnd = cwnd * beta_cubic (with beta_cubic = 0.7)
The window drops to 70 % of its value, a gentler cut than Reno’s halving to 50 %. A milder decrease means CUBIC gives up less throughput per loss and therefore needs to climb less afterward, which suits high-BDP paths where re-climbing is expensive. The trade-off is that a gentler backoff is slightly slower to relinquish bandwidth to a newly-arriving competitor — which is exactly what the fast-convergence mechanism (below) exists to counteract.
The decrease factor evolved from 0.8 to 0.7
The original 2008 CUBIC paper set its decrease parameter to
β = 0.2, whereβdenoted the fraction removed, giving a surviving factor of1 − 0.2 = 0.8— i.e. the window dropped to 80 % (Ha et al. 2008, Algorithm 1). The modern specification (RFC 8312 in 2018 and RFC 9438 in 2023) and current Linux instead usebeta_cubic = 0.7as the surviving factor directly — the window drops to 70 %. Two things changed: the notation (the RFC’sbeta_cubicis the survivor, the paper’sβwas the amount cut) and the value (the deployed multiplicative factor moved from 0.8 to 0.7). Modern Linux implements this as717/1024 ≈ 0.7. When comparing the paper and the RFC, keep the notation straight or the constants appear to contradict.
The Reno-Friendly (TCP-Friendly) Region
A pure cubic would be too slow on the very paths where Reno does fine — short-RTT, low-BDP links — because near the plateau the cubic’s growth can fall below what Reno would have managed. CUBIC guards against this by continuously computing, in parallel, an estimate of the window Reno would have and never letting itself grow slower than that. This is the Reno-friendly region (called the “TCP-friendly region” in the 2008 paper, before “TCP” was disambiguated from “Reno”) (RFC 9438 §4.3).
The Reno estimate is a standard AIMD accumulator:
W_est = W_est + alpha_cubic * (segments_acked / cwnd)
with the additive factor
alpha_cubic = 3 * (1 - beta_cubic) / (1 + beta_cubic)
Plugging in beta_cubic = 0.7 gives alpha_cubic = 3 × 0.3 / 1.7 ≈ 0.53 (RFC 9438 §4.3). This particular expression is not arbitrary: it is the additive-increase rate that makes a flow with a 0.7 multiplicative decrease achieve the same average throughput as standard Reno (whose ½ decrease pairs with a +1 increase) — it is the AIMD-equivalence condition derived in the paper’s response-function analysis (Ha et al. 2008 §3.3). On each new ACK CUBIC compares W_cubic(t) against W_est: if W_cubic(t) < W_est, the flow is in the Reno-friendly region and cwnd is set to W_est — CUBIC behaves exactly like Reno. Only when the cubic curve overtakes the Reno estimate does CUBIC’s high-speed behaviour actually take over. This is the mechanism that lets one algorithm be safe and fair on a home DSL line and fast on a transcontinental 10-gigabit link, without a mode switch the operator has to configure.
Fast Convergence — Yielding to Newcomers
Because CUBIC’s gentle 0.7 decrease makes an established flow reluctant to shrink, a new flow joining a shared bottleneck could take a long time to claim its fair share. CUBIC adds a heuristic called fast convergence (RFC 9438 §4.7; Ha et al. 2008 §3.7). On a loss, CUBIC compares the current window against the previous W_max. If the current window is smaller than last time’s W_max, that indicates available bandwidth has shrunk (a new competitor has appeared), so CUBIC deliberately lowers its remembered ceiling further:
W_max = cwnd * (1 + beta_cubic) / 2 (= 0.85 * cwnd when beta_cubic = 0.7)
Reducing W_max shortens K (the plateau arrives earlier at a lower window), so the established flow’s cubic flattens sooner and climbs less aggressively, ceding room for the newcomer to grow into. In steady state with many flows this “releases bandwidth more quickly when new flows join” and speeds convergence to a fair split (RFC 9438 §4.7).
RTT-Fairness — CUBIC’s Signature Property
The reason CUBIC keys its growth on real time rather than RTTs is not just to fill big pipes; it is to fix a structural unfairness in Reno. Under Reno the window grows by +1 segment per RTT, so two flows sharing a bottleneck grow at rates inversely proportional to their RTTs — a flow with a 20 ms RTT ramps five times faster than one with a 100 ms RTT and grabs a correspondingly larger share. This is RTT-unfairness.
CUBIC’s window-increase rate is C·(t−K)³, a function of a wall clock that ticks identically for both flows regardless of their RTTs. So two competing CUBIC flows with very different RTTs “have similar congestion window sizes under steady state” and thus divide the bottleneck far more evenly (RFC 9438 §4; Ha et al. 2008 §3.2). The paper’s steady-state analysis gives CUBIC’s average window (under a deterministic loss model where 1/p packets pass between losses) as
E{W_cubic} = fourth_root( C(4 - beta) / (4*beta) * (RTT/p)^3 )
which with the paper’s constants reduces to E{W_cubic} = 1.17 · fourth_root( (RTT/p)^3 ) (Ha et al. 2008 eq. 5-6). Compare Reno, whose average window is about 1.2/√p, essentially RTT-independent; CUBIC’s window instead grows with RTT (as RTT^{3/4}), so longer-RTT flows are handed proportionally larger windows — which is precisely what compensates for the throughput being window ÷ RTT and pulls the two flows’ throughputs together.
Uncertain
Verify: the precise quantitative degree of CUBIC’s RTT-fairness. RFC 9438 makes the qualitative claim (window growth “independent of RTTs outside the Reno-friendly region,” so different-RTT flows reach “similar congestion window sizes”). The throughput scaling — that CUBIC reduces the RTT-bias from Reno’s roughly
throughput ∝ 1/RTTto roughlythroughput ∝ RTT^{-1/4}— is a corollary I derived from the paper’s response functionE{W} ∝ (RTT/p)^{3/4}divided by RTT, not a figure the RFC states directly. Reason: the RFC frames RTT-fairness as window equality, the paper frames it via the response function, and the two are not identical statements. To resolve: check RFC 9438’s RTT-fairness discussion and the paper’s §6 experimental results for the measured throughput-vs-RTT ratios.#uncertain
Scalability and TCP-Friendliness Together
CUBIC’s design brief was to be both scalable on high-BDP links and friendly to Reno where Reno already works, and the paper quantifies the balance. Standard Reno “performs well” on two kinds of network: those with a small BDP, and those with a short RTT (even if the BDP is large) (Ha et al. 2008 §5.1). The choice of C = 0.4 is what tunes the size of the Reno-friendly region to “encompass most of the environments where Standard TCP performs well while preserving the scalability of the window growth function.” A worked example: on a path with RTT = 10 ms and loss rate p = 10⁻⁶ (1500-byte packets), Reno achieves an average window of ~1200 packets and ~1.44 Gbit/s — and in this regime CUBIC achieves exactly the same rate as Reno, whereas HSTCP would be about ten times more aggressive (Ha et al. 2008 §5.1). That restraint is the whole point: CUBIC unlocks the high-BDP regime without stealing bandwidth from ordinary Reno flows on ordinary paths.
Failure Modes and Common Misunderstandings
- CUBIC is still loss-based. Its clever curve changes how the window grows, but the congestion signal is still a dropped packet, so CUBIC inherits every pathology of loss-as-signal: it fills bottleneck queues to find them (contributing to bufferbloat), and it cannot distinguish congestive loss from random wireless loss. This is exactly what BBR set out to escape.
tis wall-clock time, not RTT count. The most common conceptual error is to readW_cubic(t)as a per-RTT recurrence. It is a continuous function of elapsed seconds; that is the source of both the high-BDP speed and the RTT-fairness.- RTT-fairness is greatly reduced, not eliminated. CUBIC equalises windows across RTTs; residual throughput differences remain because throughput is still
window ÷ RTT. Do not sell CUBIC as “RTT-fair” without the qualification (see the uncertainty flag above). - CUBIC vs BBR fairness on a shared link. When CUBIC (which retreats on loss) shares a bottleneck with BBR (which largely ignores loss), the loss-based flow can be pushed toward starvation, or conversely a deep buffer can let CUBIC dominate — the interaction depends on buffer sizing and BBR version. Benchmark, do not assume.
- The plateau can look like a stall. A CUBIC flow sitting near
W_maxwith the window barely moving is working as designed (cautious probing around the last safe point), not stuck. The convex acceleration follows if the ceiling has genuinely risen. - Slow start still comes first. CUBIC only governs congestion avoidance; a fresh connection still ramps through exponential slow start (Linux augments this with HyStart to exit slow start before overshooting — a kernel detail in TCP Congestion Control).
Alternatives and When to Choose Them
- Reno / NewReno — the AIMD baseline of Congestion Control Fundamentals and AIMD. Simpler and well-understood, but its
+1/RTTclimb makes it unusably slow to fill high-BDP links. CUBIC dominates it on fast/long paths and matches it on short/slow ones, which is why CUBIC displaced it as the default. - BBR — model-based rather than loss-based; estimates bottleneck bandwidth and minimum RTT directly and paces to them, avoiding both bufferbloat and needless cuts on random loss. Prefer BBR on deeply-buffered or lossy paths (CDN edges, wireless, transcontinental bulk); mind its fairness caveats against CUBIC.
- HSTCP / HTCP / Scalable TCP — earlier high-speed variants CUBIC’s design absorbed and improved upon; you will rarely choose them today.
- DCTCP — a data-centre algorithm reacting to the fraction of ECN marks; lower latency than CUBIC but requires ECN on every hop and is unsuitable for the open Internet.
Production Notes
CUBIC’s ubiquity makes it the reference congestion behaviour of today’s Internet: it is the Linux default from kernel 2.6.19 (2006) onward and the default in Windows and Apple stacks, so the overwhelming majority of TCP flows on the wire are CUBIC (RFC 9438 §1; Wikipedia: CUBIC TCP). Its long road from a 2006 Linux patch to a 2023 IETF Standards-Track RFC (RFC 9438, obsoleting the 2018 Informational RFC 8312) is itself instructive: the algorithm was deployed at planetary scale for seventeen years before the IETF blessed the deployed behaviour as a standard, a reminder that in congestion control, running code leads the specification rather than the other way round. Because congestion control is a purely sender-side choice, switching a fleet to or from CUBIC (via net.ipv4.tcp_congestion_control, covered in TCP Congestion Control) requires no cooperation from peers — which is how a change this large happened incrementally and invisibly. The practical guidance remains: CUBIC is the sensible default; reach for BBR only when you have measured that loss-based backoff is leaving bandwidth on the table or that bufferbloat is inflating your latency.
See Also
- Congestion Control Fundamentals and AIMD — the slow-start / congestion-avoidance / AIMD foundation CUBIC extends; read it first
- BBR Congestion Control — the model-based alternative that abandons loss-as-signal, and CUBIC’s main modern rival
- TCP Congestion Control — the Linux kernel implementation of CUBIC (
tcp_cubic.c, integer cube-root, HyStart, the pluggable framework); mechanism there, algorithm here - TCP Flow Control and the Sliding Window — the receiver-imposed
rwndthat bounds the sender alongsidecwnd - Latency Bandwidth and the Bandwidth-Delay Product — the BDP that CUBIC exists to fill, and the arithmetic of why Reno cannot
- Bufferbloat and Active Queue Management — the queue-filling pathology CUBIC (as a loss-based algorithm) contributes to
- UP: Networking and Protocols MOC — §3 (TCP as a Protocol)