Sending small packets has a large overhead that can cause network congestion.
For example, sending 1 byte of data over the network results in a 41-byte packet (20 bytes for TCP and 20 bytes for IPv4 headers).
This is called the “small-packet problem” and for example happens in telnet sessions, where every single character is sent over the network as it’s typed.
Especially over a slow network, sending many small packets like that can lead to network congestion.
Solving the small-packet problem
Nagle’s algorithm solves the small-packet problem by essentially delaying (and buffering) the sending of packets to improve bandwidth efficiency and throughput.
The algorithm can be described as:
- If the sender has unacknowledged data, buffer new application data.
- Send it when the outstanding data is acknowledged, or when enough data is available for a full-sized segment.
This is sometimes called “nagling” and is usually enabled by default.
Note
Nagle’s algorithm is controlled via
TCP_NODELAY. EnablingTCP_NODELAYdisables Nagle’s algorithm.
Delayed ACKs
A different solution for the same problem is to use delayed acknowledgments (delayed ACKs).
Delayed ACKs combine several ACK responses into a single one, by waiting for a short period that varies by implementation (e.g. 200 ms).
This way it can either:
- Combine multiple ACKs.
- Include the ACK in data it needs to send anyway (this is called “piggybacking”).
Delayed ACKs are usually also enabled by default.
Nagle’s algorithm and delayed ACKs
Nagle’s algorithm can interact badly with delayed ACKs. When both are enabled, a small write can wait for an ACK or for enough data to fill a segment, while the ACK itself is delayed.
This becomes problematic for latency-sensitive applications.
… after I put in Nagle’s algorithm, Berkeley put in delayed ACKs. Delayed ACKs delay sending an empty ACK packet for a short, fixed period based on human typing speed, maybe 100ms. This was a hack Berkeley put in to handle large numbers of dumb terminals going in to time-sharing computers using terminal to Ethernet concentrators. Without delayed ACKs, each keystroke sent a datagram with one payload byte, and got a datagram back with no payload, just an ACK, followed shortly thereafter by a datagram with one echoed character. So they got a 30% load reduction for their TELNET application.
Disabling Nagle’s algorithm
For some modern latency-sensitive applications, Nagle’s algorithm should be disabled. But it depends on the write pattern and latency requirements if it should be disabled or not: batching can improve efficiency, while small interactive writes may benefit from sending data immediately.
So disabling Nagle can reduce latency, but can also increase the number of small packets.
For example, Go considers disabling Nagle’s algorithm to be a sane default: pkg.go.dev/net#TCPConn.SetNoDelay.