TCP throughput and the BDP
Why a fast link does not automatically give you fast transfers: the bandwidth-delay product, the window ceiling, window scaling, and the surprisingly large cost of a little packet loss.
Why a fast link can still be slow
A 1 Gbps link between two offices 100 ms apart should move a file in a fraction of a second. In practice a single TCP connection often reaches a tenth of that. The link is not the problem — the protocol is waiting. TCP requires data to be acknowledged before it can be replaced in flight, and over a long path each acknowledgement takes a full round trip to arrive. If the sender runs out of permitted unacknowledged data before the first acknowledgement returns, the link sits idle for part of every RTT.
The quantity that determines whether this happens is the bandwidth-delay product: how much data has to be in flight at once to keep the pipe full. It is just bandwidth multiplied by round-trip time.
BDP (bits) = bandwidth (bits/s) × RTT (s)
BDP (bytes) = BDP (bits) ÷ 8
1 Gbps, 100 ms RTT:
BDP = 1,000,000,000 × 0.1 = 100,000,000 bits
= 100,000,000 ÷ 8 = 12,500,000 bytes
= 12.5 MBSo this link needs 12.5 MB of data outstanding at all times to stay busy. That number is the target that every other tuning decision is trying to reach.
The window ceiling
A TCP sender cannot have more unacknowledged data outstanding than the receiver's advertised window allows. That gives an upper bound on throughput that has nothing to do with the link rate:
Throughput (B/s) = window (bytes) ÷ RTT (s)
65,535-byte window, 100 ms RTT:
Throughput = 65,535 ÷ 0.1 = 655,350 B/s
= 655,350 × 8 = 5,242,800 bps ≈ 5.24 MbpsThis is the single most important number in TCP performance. A stock 65,535-byte window on a 100 ms path caps a single flow at about 5.24 Mbps — on a link of any speed. The same window over a 10 ms RTT reaches 52.4 Mbps, which is why the problem is invisible on a LAN and severe across an ocean.
The 16-bit problem
The original TCP header field for the window is 16 bits wide. That is not an implementation choice, it is the format: the maximum value expressible is 2^16 − 1 = 65,535. When TCP was designed this was far larger than any path's bandwidth-delay product, so it cost nothing. Once links crossed a few megabits per second over satellite or intercontinental distances, it became the binding constraint.
Window scaling: how TCP escapes the limit
RFC 7323 (originally RFC 1323) adds the window scale option. During the handshake each side advertises a shift count, and every window value sent afterwards is interpreted as value × 2^scale. A scale factor of 7 multiplies the ceiling by 128:
| Scale factor | Multiplier | Effective window | Throughput ceiling |
|---|---|---|---|
| 0 (none) | ×1 | 65,535 B | ≈ 5.24 Mbps |
| 4 | ×16 | 1,048,560 B | ≈ 83.9 Mbps |
| 6 | ×64 | 4,194,240 B | ≈ 335 Mbps |
| 7 | ×128 | 8,388,480 B | ≈ 671 Mbps |
| 8 | ×256 | 16,776,960 B | ≈ 1.34 Gbps |
| 10 | ×1024 | 67,107,840 B | ≈ 5.37 Gbps |
Because the shift count is negotiated, both endpoints must support it — a modern Linux or macOS host talking to an old appliance may still get an unscaled window. Note that the scale is applied to the window field but not to the value in a window update during the handshake itself, which is a classic source of off-by-a-factor-of-128 confusion when reading captures.
Packet loss: the other ceiling
Even with a large window, loss imposes its own limit. Congestion control treats a dropped packet as evidence of congestion and reduces the sending rate; recovery is gradual. The Mathis equation gives an order-of-magnitude ceiling for a single Reno-style AIMD flow:
Throughput ≈ (MSS ÷ RTT) × (C ÷ √p)
MSS = maximum segment size in bytes
RTT = round-trip time in seconds
p = packet loss probability
C ≈ 1.22 for a simple AIMD (Reno) sender
MSS 1460 B, RTT 100 ms, p = 1%:
1460 ÷ 0.1 = 14,600
1.22 ÷ √0.01 = 1.22 ÷ 0.1 = 12.2
Throughput = 14,600 × 12.2 = 178,120 B/s
≈ 1.42 MbpsThe loss term sits under a square root, which makes it unusually punishing. Going from 0.01% loss to 1% loss — a hundredfold increase — costs a factor of ten in throughput, not a hundred. It also means that a path with a small amount of persistent loss has a hard ceiling that no amount of window tuning can raise.
| Loss rate | √p | C ÷ √p | Throughput ceiling |
|---|---|---|---|
| 0.001% (1e-5) | 0.00316 | 386 | ≈ 45.1 Mbps |
| 0.01% (1e-4) | 0.01 | 122 | ≈ 14.3 Mbps |
| 0.1% (1e-3) | 0.0316 | 38.6 | ≈ 4.51 Mbps |
| 1% (1e-2) | 0.1 | 12.2 | ≈ 1.42 Mbps |
| 5% (5e-2) | 0.224 | 5.45 | ≈ 636 kbps |
Which limit is actually in play?
When a transfer is slower than expected, the useful question is which of the three limits is binding. Each has a different fix, and applying the wrong one wastes time.
| Symptom | Likely limit | What to check |
|---|---|---|
| Throughput × RTT ≈ 64 KB | Window, unscaled | Is the window scale option present in the handshake on both sides? |
| Throughput × RTT ≈ a round number of MB | Window, sized below the BDP | Compare the advertised window against the BDP; check tcp_rmem/tcp_wmem autotuning. |
| Throughput × RTT ≈ the BDP, but still slow | Loss | Look for retransmissions and duplicate ACKs; measure loss on the path, not just at the endpoints. |
| Throughput well under the window and loss ceilings | The application or disk | The sender may not have data ready to send, or the receiver may be writing slowly. |
| One flow slow, several flows fast in aggregate | Per-flow limits, not the link | Multiple flows each get their own window; a single flow is often the wrong test. |
The quick sanity check: multiply the observed throughput by the RTT. If the result is suspiciously close to 64 KB, or to a round number of megabytes, the window is the limit. If it is close to the BDP but well below the link rate, look at loss.
What this model does not cover
The figures above are upper bounds for a single idealised flow. Real transfers are also limited by TCP slow start — a connection begins with a small congestion window and doubles it each RTT until it reaches the path capacity, so short transfers never get near the ceiling. Delayed acknowledgements, receive-window autotuning, competing traffic, queuing delay that increases the effective RTT under load, and CUBIC's different response to loss all move real behaviour away from the model.
Use these numbers to work out which limit applies and how much headroom tuning could buy, not to predict a specific transfer's duration. The bandwidth calculator handles the more basic question of how long a file takes at a given rate, and the MTU and MSS calculator covers the encapsulation overhead that reduces the MSS these formulas depend on.
Bandwidth-delay product reference
How much data must be in flight to fill common links. This is the window size a single TCP flow needs to reach the full rate — compare it against the 65,535-byte unscaled maximum to see when window scaling becomes necessary.
| Link rate | 1 ms | 10 ms | 50 ms | 100 ms | 200 ms |
|---|---|---|---|---|---|
| 10 Mbps | 1.25 KB | 12.5 KB | 62.5 KB | 125 KB | 250 KB |
| 100 Mbps | 12.5 KB | 125 KB | 625 KB | 1.25 MB | 2.5 MB |
| 1 Gbps | 125 KB | 1.25 MB | 6.25 MB | 12.5 MB | 25 MB |
| 10 Gbps | 1.25 MB | 12.5 MB | 62.5 MB | 125 MB | 250 MB |
| 100 Gbps | 12.5 MB | 125 MB | 625 MB | 1.25 GB | 2.5 GB |
Everything above 65,535 bytes cannot be reached by a single TCP flow without the window scale option. That is nearly every cell in this table beyond the first column, which is why the option is effectively mandatory on the modern internet.
Key constants to remember
| Quantity | Value | Why it matters |
|---|---|---|
| Maximum unscaled window | 65,535 bytes | 16-bit field limit; the ceiling without the scale option |
| Maximum window scale | ×2^14 | RFC 7323 caps the shift at 14, so the largest window is ~1 GB |
| Default Ethernet MSS | 1460 bytes | 1500 MTU − 20 IPv4 − 20 TCP |
| Mathis constant C | ≈ 1.22 | Empirical factor for a simple AIMD Reno sender |
| Minimum recommended MTU | 576 bytes (IPv4) | Any path must forward this without fragmenting |
| IPv6 minimum MTU | 1280 bytes | RFC 8200 raises the floor; routers never fragment |
Related: TCP throughput calculator, bandwidth calculator, MTU / MSS calculator, MTU and MSS guide.
Frequently asked questions
What is the bandwidth-delay product?
The bandwidth-delay product (BDP) is the amount of data that must be in flight — sent but not yet acknowledged — to keep a link completely full. It is simply bandwidth multiplied by round-trip time. A 1 Gbps link with a 100 ms RTT has a BDP of 1e9 × 0.1 = 1e8 bits = 12.5 MB. Until 12.5 MB of unacknowledged data is on the wire, the link is idle part of the time.
Why is throughput capped by the TCP window?
A sender may not have more unacknowledged data outstanding than the receive window allows. If the window is W bytes and the round-trip time is R seconds, the most data that can cross the link per RTT is W bytes, so throughput is capped at W ÷ R. With a 65,535-byte window over a 100 ms RTT, that ceiling is 655,350 B/s ≈ 5.24 Mbps — regardless of how fast the underlying link is.
What is TCP window scaling and why is it needed?
The original TCP header has only 16 bits for the window field, so without an option the largest window is 65,535 bytes. On a long or fast path that ceiling throttles throughput badly. RFC 7323 adds the window scale option, which shifts the advertised value left by a negotiated number of bits — a scale factor of 7 turns 65,535 into 8,388,480 bytes. Both endpoints must offer the option or it is not used.
Why does packet loss hurt throughput so much?
Loss is interpreted by congestion control as a signal to halve the sending rate, and the recovery is gradual. The Mathis equation models the resulting ceiling as roughly (MSS ÷ RTT) × (1.22 ÷ √p), where p is the loss probability. Because the loss term sits under a square root, a 1% loss rate on a 100 ms RTT path limits a single 1460-byte-MSS flow to roughly 2.8 Mbps — even on a gigabit link.
What is a long fat network?
A long fat network (LFN, or 'long fat pipe') is a path with a high bandwidth-delay product — typically a transcontinental or intercontinental link where the product of bandwidth and RTT is large. These are the paths where window scaling, selective acknowledgement and larger buffers matter most, and where an untuned host performs far below the link's capacity.
Does a bigger window always make things faster?
No. The window must be backed by real buffers on both endpoints and by the kernel's autotuning limits, and it is only useful if the path can actually carry the data. On a short or lossy path, a larger window mostly increases the amount of data that has to be retransmitted after a loss. Size the window to the bandwidth-delay product, not larger.
Why do these calculators give an upper bound rather than a prediction?
They model an idealised single flow with no competing traffic, no queuing and no loss. Real throughput is lower because of slow start, congestion control ramping, delayed acknowledgements, receive-window autotuning, competing flows and application-level bottlenecks. Treat the numbers as the ceiling that tuning could approach, and as a way to identify which limit — window, loss or path — is actually in play.