NetCalcLab

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.

Bandwidth-delay product
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 MB

So 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:

Window-limited throughput
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 Mbps

This 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:

Window ceiling by scale factor over a 100 ms RTT
Scale factorMultiplierEffective windowThroughput ceiling
0 (none)×165,535 B≈ 5.24 Mbps
4×161,048,560 B≈ 83.9 Mbps
6×644,194,240 B≈ 335 Mbps
7×1288,388,480 B≈ 671 Mbps
8×25616,776,960 B≈ 1.34 Gbps
10×102467,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:

Mathis equation
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 Mbps

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

Mathis ceiling for MSS 1460 B over a 100 ms RTT
Loss rate√pC ÷ √pThroughput ceiling
0.001% (1e-5)0.00316386≈ 45.1 Mbps
0.01% (1e-4)0.01122≈ 14.3 Mbps
0.1% (1e-3)0.031638.6≈ 4.51 Mbps
1% (1e-2)0.112.2≈ 1.42 Mbps
5% (5e-2)0.2245.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.

Diagnosing a slow transfer
SymptomLikely limitWhat to check
Throughput × RTT ≈ 64 KBWindow, unscaledIs the window scale option present in the handshake on both sides?
Throughput × RTT ≈ a round number of MBWindow, sized below the BDPCompare the advertised window against the BDP; check tcp_rmem/tcp_wmem autotuning.
Throughput × RTT ≈ the BDP, but still slowLossLook for retransmissions and duplicate ACKs; measure loss on the path, not just at the endpoints.
Throughput well under the window and loss ceilingsThe application or diskThe sender may not have data ready to send, or the receiver may be writing slowly.
One flow slow, several flows fast in aggregatePer-flow limits, not the linkMultiple 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.

BDP in bytes by link rate and RTT
Link rate1 ms10 ms50 ms100 ms200 ms
10 Mbps1.25 KB12.5 KB62.5 KB125 KB250 KB
100 Mbps12.5 KB125 KB625 KB1.25 MB2.5 MB
1 Gbps125 KB1.25 MB6.25 MB12.5 MB25 MB
10 Gbps1.25 MB12.5 MB62.5 MB125 MB250 MB
100 Gbps12.5 MB125 MB625 MB1.25 GB2.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

Numbers that govern TCP throughput
QuantityValueWhy it matters
Maximum unscaled window65,535 bytes16-bit field limit; the ceiling without the scale option
Maximum window scale×2^14RFC 7323 caps the shift at 14, so the largest window is ~1 GB
Default Ethernet MSS1460 bytes1500 MTU − 20 IPv4 − 20 TCP
Mathis constant C≈ 1.22Empirical factor for a simple AIMD Reno sender
Minimum recommended MTU576 bytes (IPv4)Any path must forward this without fragmenting
IPv6 minimum MTU1280 bytesRFC 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.