Skip to content
About
Main content

TCP, kernel bypass and the latency tiers

What does TCP do under load, and what does each tier of the network cost per round trip?

TCP: slow start, congestion control, incast

Slow start

A new connection knows nothing about available capacity, and it cannot ask. So begins with a small congestion window (: how much unacknowledged data it may have in flight) of about 10 segments ≈ 14.6 KB, and doubles it every .

Example: from 14.6 KB to 125 MB takes log₂(125,000,000 / 14,600) ≈ 13–14 doublings ≈ 13–14 RTTs ≈ 1.4 seconds at 100 ms RTT. Short transfers never leave slow start, so their effective speed depends on RTT and transfer size, not link speed. This is why connection reuse and HTTP/2 multiplexing matter much more than they seem to.

a small congestion window of about 10 segments ≈ 14.6 KB
bytes in flight needed to fill the link (the bandwidth-delay product)
time for a message to reach the other side and the answer to come back
RTT = 10 msRTT = 100 ms
Data table
Time to reach a window, from 14.6 KB
Window WseriesTime to reach it
19.5 KBRTT = 10 ms4.54 ms
22.4 KBRTT = 10 ms6.49 ms
25.6 KBRTT = 10 ms8.44 ms
29.3 KBRTT = 10 ms10.4 ms
33.5 KBRTT = 10 ms12.3 ms
38.4 KBRTT = 10 ms14.3 ms
44 KBRTT = 10 ms16.2 ms
50.3 KBRTT = 10 ms18.2 ms
57.6 KBRTT = 10 ms20.1 ms
66 KBRTT = 10 ms22.1 ms
75.5 KBRTT = 10 ms24.1 ms
86.5 KBRTT = 10 ms26 ms
99 KBRTT = 10 ms28 ms
113 KBRTT = 10 ms29.9 ms
130 KBRTT = 10 ms31.9 ms
149 KBRTT = 10 ms33.8 ms
170 KBRTT = 10 ms35.8 ms
195 KBRTT = 10 ms37.7 ms
223 KBRTT = 10 ms39.7 ms
255 KBRTT = 10 ms41.6 ms
292 KBRTT = 10 ms43.6 ms
334 KBRTT = 10 ms45.5 ms
383 KBRTT = 10 ms47.5 ms
438 KBRTT = 10 ms49.4 ms
502 KBRTT = 10 ms51.4 ms
574 KBRTT = 10 ms53.3 ms
657 KBRTT = 10 ms55.3 ms
753 KBRTT = 10 ms57.2 ms
862 KBRTT = 10 ms59.2 ms
987 KBRTT = 10 ms61.1 ms
1.1 MBRTT = 10 ms63.1 ms
1.26 MBRTT = 10 ms65 ms
1.45 MBRTT = 10 ms67 ms
1.65 MBRTT = 10 ms68.9 ms
1.89 MBRTT = 10 ms70.9 ms
2.17 MBRTT = 10 ms72.8 ms
2.48 MBRTT = 10 ms74.8 ms
2.84 MBRTT = 10 ms76.7 ms
3.25 MBRTT = 10 ms78.7 ms
3.73 MBRTT = 10 ms80.6 ms
4.26 MBRTT = 10 ms82.6 ms
4.88 MBRTT = 10 ms84.5 ms
5.59 MBRTT = 10 ms86.5 ms
6.4 MBRTT = 10 ms88.4 ms
7.33 MBRTT = 10 ms90.4 ms
8.39 MBRTT = 10 ms92.3 ms
9.6 MBRTT = 10 ms94.3 ms
11 MBRTT = 10 ms96.2 ms
12.6 MBRTT = 10 ms98.2 ms
14.4 MBRTT = 10 ms100 ms
16.5 MBRTT = 10 ms102 ms
18.9 MBRTT = 10 ms104 ms
21.6 MBRTT = 10 ms106 ms
24.7 MBRTT = 10 ms108 ms
28.3 MBRTT = 10 ms110 ms
32.4 MBRTT = 10 ms112 ms
37.1 MBRTT = 10 ms114 ms
42.5 MBRTT = 10 ms116 ms
48.7 MBRTT = 10 ms118 ms
55.7 MBRTT = 10 ms120 ms
63.8 MBRTT = 10 ms122 ms
73 MBRTT = 10 ms124 ms
83.6 MBRTT = 10 ms126 ms
95.7 MBRTT = 10 ms127 ms
110 MBRTT = 10 ms129 ms
125 MBRTT = 10 ms131 ms
144 MBRTT = 10 ms133 ms
164 MBRTT = 10 ms135 ms
188 MBRTT = 10 ms137 ms
215 MBRTT = 10 ms139 ms
247 MBRTT = 10 ms141 ms
282 MBRTT = 10 ms143 ms
323 MBRTT = 10 ms145 ms
370 MBRTT = 10 ms147 ms
424 MBRTT = 10 ms149 ms
485 MBRTT = 10 ms151 ms
555 MBRTT = 10 ms153 ms
636 MBRTT = 10 ms155 ms
728 MBRTT = 10 ms157 ms
833 MBRTT = 10 ms159 ms
954 MBRTT = 10 ms161 ms
19.5 KBRTT = 100 ms45.4 ms
22.4 KBRTT = 100 ms64.9 ms
25.6 KBRTT = 100 ms84.4 ms
29.3 KBRTT = 100 ms104 ms
33.5 KBRTT = 100 ms123 ms
38.4 KBRTT = 100 ms143 ms
44 KBRTT = 100 ms162 ms
50.3 KBRTT = 100 ms182 ms
57.6 KBRTT = 100 ms201 ms
66 KBRTT = 100 ms221 ms
75.5 KBRTT = 100 ms241 ms
86.5 KBRTT = 100 ms260 ms
99 KBRTT = 100 ms280 ms
113 KBRTT = 100 ms299 ms
130 KBRTT = 100 ms319 ms
149 KBRTT = 100 ms338 ms
170 KBRTT = 100 ms358 ms
195 KBRTT = 100 ms377 ms
223 KBRTT = 100 ms397 ms
255 KBRTT = 100 ms416 ms
292 KBRTT = 100 ms436 ms
334 KBRTT = 100 ms455 ms
383 KBRTT = 100 ms475 ms
438 KBRTT = 100 ms494 ms
502 KBRTT = 100 ms514 ms
574 KBRTT = 100 ms533 ms
657 KBRTT = 100 ms553 ms
753 KBRTT = 100 ms572 ms
862 KBRTT = 100 ms592 ms
987 KBRTT = 100 ms611 ms
1.1 MBRTT = 100 ms631 ms
1.26 MBRTT = 100 ms650 ms
1.45 MBRTT = 100 ms670 ms
1.65 MBRTT = 100 ms689 ms
1.89 MBRTT = 100 ms709 ms
2.17 MBRTT = 100 ms728 ms
2.48 MBRTT = 100 ms748 ms
2.84 MBRTT = 100 ms767 ms
3.25 MBRTT = 100 ms787 ms
3.73 MBRTT = 100 ms806 ms
4.26 MBRTT = 100 ms826 ms
4.88 MBRTT = 100 ms845 ms
5.59 MBRTT = 100 ms865 ms
6.4 MBRTT = 100 ms884 ms
7.33 MBRTT = 100 ms904 ms
8.39 MBRTT = 100 ms923 ms
9.6 MBRTT = 100 ms943 ms
11 MBRTT = 100 ms962 ms
12.6 MBRTT = 100 ms982 ms
14.4 MBRTT = 100 ms1 s
16.5 MBRTT = 100 ms1.02 s
18.9 MBRTT = 100 ms1.04 s
21.6 MBRTT = 100 ms1.06 s
24.7 MBRTT = 100 ms1.08 s
28.3 MBRTT = 100 ms1.1 s
32.4 MBRTT = 100 ms1.12 s
37.1 MBRTT = 100 ms1.14 s
42.5 MBRTT = 100 ms1.16 s
48.7 MBRTT = 100 ms1.18 s
55.7 MBRTT = 100 ms1.2 s
63.8 MBRTT = 100 ms1.22 s
73 MBRTT = 100 ms1.24 s
83.6 MBRTT = 100 ms1.26 s
95.7 MBRTT = 100 ms1.27 s
110 MBRTT = 100 ms1.29 s
125 MBRTT = 100 ms1.31 s
144 MBRTT = 100 ms1.33 s
164 MBRTT = 100 ms1.35 s
188 MBRTT = 100 ms1.37 s
215 MBRTT = 100 ms1.39 s
247 MBRTT = 100 ms1.41 s
282 MBRTT = 100 ms1.43 s
323 MBRTT = 100 ms1.45 s
370 MBRTT = 100 ms1.47 s
424 MBRTT = 100 ms1.49 s
485 MBRTT = 100 ms1.51 s
555 MBRTT = 100 ms1.53 s
636 MBRTT = 100 ms1.55 s
728 MBRTT = 100 ms1.57 s
833 MBRTT = 100 ms1.59 s
954 MBRTT = 100 ms1.61 s

Loss-based control must overshoot

Reno / CUBIC (the classic algorithms) use packet loss as the capacity signal. The surprising result: the algorithm must overshoot to learn. It fills the router's until it overflows, then slows down. So it normally runs with full buffers, which causes : 100 ms to 1 s+ of extra from queueing alone, on a link with no packet loss. Larger buffers made this worse, not better.

BBR (Google's algorithm) estimates the bottleneck and the minimum RTT directly, sends at that rate, and does not fill queues. It is much better on long fat networks and lossy links. It is not free: BBRv1 was aggressive towards CUBIC flows. It marks the shift from loss as signal to model as signal.

Incast: the failure that matters most

Incast has exactly the shape of every distributed query: N workers reply at the same time to one coordinator (a synchronized many-to-one fan-in). This is Meera's problem.

  1. The switch port facing the coordinator has a small buffer (often a few hundred KB). N simultaneous replies overflow it and packets are dropped.
  2. Normally TCP recovers with fast retransmit (the receiver sees a gap because later packets still arrive). But replies are small, so there are no later packets to reveal the gap.
  3. So senders wait for the (retransmission timeout), historically 200 ms – 1 s, when the datacenter RTT is ~100 µs.

One lost packet costs a 1,000× delay. does not fall slowly; it collapses by about 10× once the number of senders passes a threshold.

Mitigations:

  • microsecond-level RTO timers;
  • ECN / DCTCP (switches mark packets before dropping them, and senders react in proportion);
  • staggered / jittered requests (small random delays, so replies do not arrive together);
  • limit the fan-out width.

— the same idea as the variance-reduction methods in the CPU part.


Kernel bypass

  • Same arithmetic as the storage finding. The kernel network path costs ~5–15 µs: thousands of cycles of interrupt, , copies and cache pollution. When the wire RTT was 500 µs, that was noise. At 100 Gb/s and ~2 µs or kernel-bypass RTT, software is the main cost. Cause 3 moved up again.
  • DPDK (Data Plane Development Kit): user-space drivers that poll the card, with no interrupts, and pinned cores. ~5 µs → under 1 µs, with near line-rate small-packet processing. The price is real and often decisive: one core spins at 100% forever, you lose the whole kernel stack (TCP, firewall, tools), and the card is dedicated.
  • RDMA (Remote Direct Memory Access) is more interesting: it reads and writes the memory of another machine directly, without using that machine's CPU. ~1–2 µs end to end. Remote memory then costs about the same as a remote access in CPU and memory, which made disaggregated memory and CXL-over-network realistic.
  • Rule for when it matters: only when the work per message is similar to the ~5–15 µs stack cost. Analytics moving 8 MB Parquet chunks? Irrelevant — you are bandwidth-bound. Distributed transactions, consensus, of tiny messages? Decisive. For most teams the middle path is + TSO/GRO + interrupt tuning, which gives much of the benefit and keeps the kernel.

The latency tiers

This table is the network's "numbers every programmer should know". You will use it often.

TierRTTBandwidthReason for the minimum
Same host, loopback10–30 µstens of GB/ssoftware path only
Same host, shared memory~0.1 µs~memory bandwidthno protocol
Same rack (ToR = top-of-rack switch)50–200 µs10–100 Gb/s2 switch hops + stack
Same / datacenter200–500 µs10–100 Gb/s4–6 hops, ~1 km
Cross-AZ (same region)0.5–2 ms10–100 Gb/s10–100 km of fiber + hops
Cross-region, same continent10–40 msvaries~1,000 km+
Cross-region, intercontinental70–250 msvariesspeed of light, hard minimum
Object storage GET (same region)20–100 ms~50–90 MB/s per connectionnot distance: service depth
  • Each tier is about 10× the previous one, a useful mental ruler: same host ~10 µs, same rack ~100 µs, same AZ ~500 µs, cross-AZ ~1 ms, cross-region ~100 ms.
  • The last row deserves attention. Cross-AZ RTT is ~1 ms, but an S3 GET in the same region takes 20–100 ms. The extra 20–100× is not distance. It is load balancers, TLS handshake, authentication, metadata lookup and internal replication. The minimum is service depth, not travel time. Two different mechanisms with the same units. Mixing them up leads people to "fix" S3 latency by moving servers closer, which does not help.
  • ~1–2 ms per round trip. This is the numeric basis of the multi-AZ trade-off, and why systems (which need a majority of replicas to agree) keep replicas inside one region and replicate asynchronously between regions.
  • Synchronous consensus across regions is impossible for interactive work. Mumbai ↔ Virginia is ~200 ms RTT, and Paxos/Raft (consensus protocols) need 1–2 round trips per commit. This is geometry, not tuning. It is why Google Spanner uses atomic clocks: TrueTime lets it skip one coordination round trip, and pay a commit-wait limited by clock uncertainty rather than by distance.

The shape of the communication

The shape of the communication matters more than its volume. All-to-all shuffle is costly because it breaks the oversubscription bet. Synchronized fan-in causes because it is synchronized. Chatty chains are slow because RTT cannot be amortized. The same bytes can cost very different amounts depending only on the pattern.