How the calculation works
Bandwidth-delay product (BDP) = link speed × round-trip time ÷ 8. It is the number of bytes that must be in flight, sent but not yet acknowledged, to keep the link busy. A TCP connection needs a window at least this large to run at the full link speed.
Throughput ceiling for a window = window × 8 ÷ RTT. One stream can send one window per round trip, then waits for acknowledgments. The calculator shows this for a 64 KiB window, the limit without window scaling (65,535 bytes), and for the window you enter. It also shows how many parallel streams of that window would fill the link.
Packet loss ceiling (Mathis approximation) = (MSS ÷ RTT) × C ÷ √p, with C = √(3/2) ≈ 1.22 and p the loss rate as a fraction. It comes from Mathis, Semke, Mahdavi and Ott, “The Macroscopic Behavior of the TCP Congestion Avoidance Algorithm” (ACM SIGCOMM CCR, 1997). It is an upper-bound approximation for Reno-style congestion avoidance with small, random loss. CUBIC (the default on most Linux distributions) and BBR behave differently, so read it as a guide to scale, not a forecast.
Single-stream estimate = the lowest of the link speed, the window ceiling and the loss ceiling.
Units. Window sizes use binary units: 1 KiB = 1,024 bytes and 1 MiB = 1,048,576 bytes. Link speeds are decimal: 1 Mbps = 10⁶ bits per second.
Operating system limits (assumptions). Each figure below is a ceiling. The window a connection actually reaches depends on both hosts and the path.
- Without window scaling: 65,535 bytes, the largest value of the 16-bit window field. Window scaling (RFC 7323) lifts it.
- Windows: receive window auto-tuning is on at the Normal level by default, which uses a scale factor of 8: up to 65,535 × 2⁸ bytes, about 16 MiB (Microsoft).
- Linux: auto-tuning grows the receive buffer up to
net.ipv4.tcp_rmemmax. The current kernel documentation gives the default as between 128 KiB and 32 MiB, depending on RAM; many kernels in service use up to 6 MiB (kernel.org). This is a buffer, and part of it covers overhead, so the advertised window is smaller than the buffer.
Worked example
1 Gbps between New York and Los Angeles at 70 ms RTT.
- BDP = 1,000,000,000 × 0.070 ÷ 8 = 8,750,000 bytes (8.34 MiB): the window needed to fill the link.
- With a 64 KiB window (no scaling): 65,535 × 8 ÷ 0.070 = 7.49 Mbps.
- With a 4 MiB window: 4,194,304 × 8 ÷ 0.070 = 479.3 Mbps, so it takes 3 streams to fill the link.
- With 0.01% loss and a 1,460-byte MSS: (1,460 × 8 ÷ 0.070) × 1.2247 ÷ √0.0001 = 20.44 Mbps, so it takes 49 streams to fill the link.
- Single-stream estimate: 20.44 Mbps, limited by packet loss.
The window fits inside the Windows Normal limit (16 MiB), and on Linux tcp_rmem max needs to be more than 8.34 MiB. Even then, loss is what holds one stream back here, which is why parallel streams or a cleaner path help more than bigger buffers.
10 Gbps at 100 ms. BDP = 10,000,000,000 × 0.1 ÷ 8 = 125,000,000 bytes (119.21 MiB). That is more than the Windows Normal limit and more than the default Linux maximum, so one stream can’t fill the link without tuning on both hosts. ESnet’s host-tuning guide uses the same example (“a 10Gbps flow across a 100ms network requires 120MB of buffering”).
Recommended settings
These are starting points for hosts that move bulk data over long paths. Test on one host first, change both ends, and measure with a tool such as iperf3 before and after.
Linux. Raise the auto-tuning maximums above your BDP, leaving room for overhead. ESnet’s host-tuning guide suggests these values for a 10 Gbps host on paths up to 100 ms, sized for parallel streams rather than one:
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.ipv4.tcp_rmem = 4096 87380 33554432
net.ipv4.tcp_wmem = 4096 65536 33554432
net.ipv4.tcp_rmemandnet.ipv4.tcp_wmemare three values: min, default and max, in bytes. The max is the ceiling for automatic tuning.net.core.rmem_maxandnet.core.wmem_maxcap buffers that applications set themselves withSO_RCVBUFandSO_SNDBUF. Setting those disables auto-tuning for that socket, so an application that sets a small buffer stays slow whatever the sysctls say.- Put the lines in a file under
/etc/sysctl.d/and load it withsysctl --system. Keep the default congestion control (CUBIC on most distributions) unless you have a reason to change it.
Windows. Check that receive window auto-tuning is at Normal with netsh interface tcp show global or Get-NetTCPSetting | Select SettingName,AutoTuningLevelLocal. If it shows disabled or restricted, set it back with netsh interface tcp set global autotuninglevel=normal. Microsoft notes the old TcpWindowSize registry value is ignored on current versions.
Parallel streams. When the BDP is above what one host can reach, or the path has loss, split the transfer: several streams each need only part of the window, and a loss event slows one of them, not the whole transfer. Many copy and backup tools support this; check the thread or stream setting.
The path. The window is only one limit. Loss, a congested hop, a firewall that disables window scaling, or a VPN with a smaller MTU can each cap a transfer. A dedicated wave or private connection with low, steady loss can do more for long-distance throughput than host tuning. To estimate RTT from distance, use the network latency calculator; to see how long a transfer takes at the speed you get, use the data transfer time calculator.




