Frontend Labs
⋯

36. Throughput math: loss, latency, and the Mathis equation

36.1 The equations every network engineer should know

Window-limited throughput (no loss):

Throughput ~ Window / RTT

Loss-limited throughput of a single loss-based TCP flow (Mathis et al., 1997):

Throughput ~ (MSS / RTT) x (C / sqrt(p))

  p = packet loss probability
  C ~ 1.22  (for the standard AIMD sawtooth with periodic loss)

A more complete form (Padhye/PFTK, 1998) adds the RTO term for heavier loss:

Throughput ~ MSS /
   ( RTT*sqrt(2p/3)  +  RTO*min(1, 3*sqrt(3p/8))*p*(1 + 32p^2) )

36.2 What Mathis tells you (the intuition)

  • Throughput is inversely proportional to RTT — double the latency, halve the speed.
  • Throughput is inversely proportional to √loss — going from 0.01% to 1% loss (100×) cuts throughput 10×.
  • A loss-based flow cannot sustain a large window on a path with even modest loss, regardless of link capacity.

Worked example — 1 Gbit/s link, RTT 80 ms, MSS 1448:

Loss p Mathis throughput (1 flow) % of 1 Gbit/s
0 (window-limited by 8 MB) ~800 Mbit/s 80%
0.001% (1e-5) 1448/0.08 × 1.22/√1e-5 ≈ 69.8 Mbit/s 7%
0.01% (1e-4) ≈ 22 Mbit/s 2.2%
0.1% (1e-3) ≈ 7 Mbit/s 0.7%
1% (1e-2) ≈ 2.2 Mbit/s 0.2%

This is why a "1 Gbit/s" trans-continental link delivers single-digit Mbit/s per CUBIC stream once there's any real loss — and why the workarounds are: many parallel streams (N streams ≈ N× throughput until they collectively cause loss), BBR (models bandwidth, ignores that loss), FEC/erasure coding, or fixing the loss.

36.3 Parallel streams

N independent flows sharing a path get roughly N × a single flow's Mathis rate — until their combined load pushes p up. Tools: iperf3 -P, aria2c -x, GridFTP, rclone --multi-thread-streams, Aspera/UDT (UDP-based, bypasses the √p problem entirely). This is also why a browser's 6 connections and HTTP/2's multiplexing-over-one-connection behave differently under loss (HTTP/2's single TCP connection suffers head-of-line blocking; HTTP/3 fixes it — Ch. 39).

36.4 Goodput vs throughput

  • Throughput: bytes on the wire per second (includes headers, retransmits).
  • Goodput: useful application bytes per second delivered.
  • Overhead sources: 40–52 B headers per ~1448 B (~3.5%), retransmissions (2×p roughly), TLS record overhead (~0.5–1%), slow-start ramp (matters for short transfers), and connection setup RTTs.
  • For a short transfer (e.g. 50 KB HTTP response), you never leave slow start; throughput math is irrelevant and round trips dominate: time ≈ (handshake RTTs) + ceil(log2(response/IW)) × RTT. Optimize RTT count, not bandwidth.

36.5 War story

A genomics lab moved ~2 TB/day between universities over a shared 10 Gbit/s research link, RTT 55 ms, measured loss ~0.02%. Single scp ≈ 15 Mbit/s (Mathis predicts ~12–20). They blamed the link. Solution stack: (1) tcp_rmem/wmem max → 256 MB, (2) switch senders to BBR + fq, (3) use a parallel tool (bbcp/GridFTP, 16 streams). Result: ~8 Gbit/s aggregate, transfer window shrank from all day to ~45 min. Lesson: don't argue with MSS/(RTT·√p); engineer around it with buffers + BBR + parallelism.

Try it yourself

# Estimate p from counters over a known interval:
nstat -az | grep -E 'TcpRetransSegs|TcpOutSegs'   # p ~ RetransSegs / OutSegs (rough)
# Compare 1 vs N streams (if Nx faster, you're loss/CC-limited, not link-limited):
iperf3 -c <peer> -t30 ; iperf3 -c <peer> -t30 -P16
# Compare CUBIC vs BBR on the same lossy path:
iperf3 -c <peer> -t30 -C cubic ; iperf3 -c <peer> -t30 -C bbr

Generated from real-life-example.md · 8 Oct 2026 16:36