TCP Congestion Control Comparison

function mathisBps(mss, rttS, lossFrac) { return lossFrac > 0 ? (mss / rttS) / Math.sqrt(lossFrac) : Infinity; } function calcReno(rttMs, lossFrac, bwBps) { const mss = 1460; const rttS = rttMs / 1000; const bps = Math.min(mathisBps(mss, rttS, lossFrac), bwBps); return { bps, mbps: bps / 1e6, utilPct: Math.min(bps / bwBps * 100, 100) }; } function calcCubic(rttMs, lossFrac, bwBps) { const mss = 1460; const rttS = rttMs / 1000; // CUBIC throughput (Ha et al. 2008): // T_cubic = sqrt(3β/(2-β)) × C^(-1/4) × MSS / (RTT × sqrt(loss)) // β=0.7, C=0.4 → factor relative to simplified Mathis ≈ 1.60 // sqrt(3×0.7/(2-0.7)) / 0.4^0.25 = sqrt(1.615) / 0.795 ≈ 1.271/0.795 ≈ 1.60 const cubicFactor = Math.sqrt(3 * 0.7 / (2 - 0.7)) / Math.pow(0.4, 0.25); // ≈ 1.60 const bps = lossFrac > 0 ? Math.min(cubicFactor * mathisBps(mss, rttS, lossFrac), bwBps) : bwBps; return { bps, mbps: bps / 1e6, utilPct: Math.min(bps / bwBps * 100, 100) }; } function calcBbr(rttMs, lossFrac, bwBps) { // BBR does not reduce cwnd on loss (unlike CUBIC/Reno). // However, retransmissions consume bandwidth, reducing goodput. // Empirical model (Hock et al. 2017): goodput ≈ BW × (1 - 1.2 × loss) // The 1.2× factor accounts for each lost packet requiring ~1.2 transmit attempts on average. // Collapse floor at 5% minimum to avoid negative values at extreme loss. const goodput = Math.max(1.0 - 1.2 * lossFrac, 0.05); const bps = bwBps * goodput; return { bps, mbps: bps / 1e6, utilPct: Math.min(bps / bwBps * 100, 100) }; } function compare() { const rttMs = parseFloat(document.getElementById('rttMs').value); const lossPct = parseFloat(document.getElementById('lossPct').value); const bwMbps = parseFloat(document.getElementById('bwMbps').value); const div = document.getElementById('compareResult'); div.style.display = 'block'; if (isNaN(rttMs) || isNaN(lossPct) || isNaN(bwMbps) || rttMs <= 0 || bwMbps <= 0 || lossPct < 0) { div.innerHTML = 'Invalid input.'; return; } const lossFrac = lossPct / 100; const bwBps = bwMbps * 1e6; const reno = calcReno(rttMs, lossFrac, bwBps); const cubic = calcCubic(rttMs, lossFrac, bwBps); const bbr = calcBbr(rttMs, lossFrac, bwBps); const maxMbps = Math.max(reno.mbps, cubic.mbps, bbr.mbps, 0.001); const fmtMbps = v => v >= 1000 ? `${(v/1000).toFixed(1)} Gbps` : `${v.toFixed(2)} Mbps`; const winner = bbr.mbps >= cubic.mbps && bbr.mbps >= reno.mbps ? 'BBR' : cubic.mbps >= bbr.mbps && cubic.mbps >= reno.mbps ? 'CUBIC' : 'Reno'; div.innerHTML = `
Algorithm Comparison

CUBIC

Throughput${fmtMbps(cubic.mbps)}
Utilization${cubic.utilPct.toFixed(1)}%
Loss responsecwnd halve

BBR

Throughput${fmtMbps(bbr.mbps)}
Utilization${bbr.utilPct.toFixed(1)}%
Loss responseBW model

Reno

Throughput${fmtMbps(reno.mbps)}
Utilization${reno.utilPct.toFixed(1)}%
Loss responsecwnd halve
CUBIC
${fmtMbps(cubic.mbps)}
BBR
${fmtMbps(bbr.mbps)}
Reno
${fmtMbps(reno.mbps)}
Winner at this operating point: ${winner}
CUBIC uses cubic-polynomial model (Ha et al. 2008, β=0.7, C=0.4, ~1.6× Reno). BBR uses bandwidth estimation — retransmit overhead reduces goodput linearly (~1.2× loss rate). Reno uses simplified Mathis equation. These are analytical approximations — actual behavior depends on queue depth, congestion algorithm version, and competing flows.
`; }