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
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.
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.