CS PAPERS DEEP-READ · PAPER 45
Van Jacobson & Michael Karels · SIGCOMM 1988
In 1988, Van Jacobson patched the internet's transport protocol, TCP, so the whole network wouldn't jam and seize up when traffic surged. Every time you stream a video, download a file, or open a page today, it is quietly working the gas and the brake for you — deciding how fast to send at each moment. Without it, the internet would have buckled more than thirty years ago.
In the fall of 1986, two machines a few hundred meters apart, just three hops away, saw their speed crash from about 32,000 bits per second to 40 bits per second — nearly a thousand times slower. Nobody pulled a cable, nobody attacked it; everyone just sent as hard as they could, and then nobody could get through. It's like a highway at rush hour: past a certain point, more cars don't mean slower — the whole road locks up and barely moves. Engineers named it congestion collapse.
There is no central traffic controller on the internet telling each machine how fast to send. Jacobson's key call was: since no one is in charge, let each sender feel out whether the road is jammed and voluntarily hold back. But the network won't announce "I'm congested" — so what does the sender feel with? A humble signal: dropped packets. On a decent link, packets almost never vanish from errors; they vanish because some junction's queue overflowed and threw them away. So "my packet didn't arrive" = "it's jammed ahead." That's the network talking to you.
Three small moves add up to self-discipline:
① Send at the rhythm of the echoes. Every packet you send, the other side answers with an "got it" (an ACK). Jacobson lets the sender release a new packet only when it hears an echo come back — like pouring water into a funnel only as fast as it drains, so the network never overflows. Those returning echoes become a metronome keeping time for you.
② Speed up slowly, cut back hard. While all is well, each round nudges the sending rate up just a hair (a cautious probe upward); the instant a packet is lost, halve the rate at once (slam the brakes). Gentle up, brutal down — that asymmetry is exactly what lets everyone settle into a fair share of the road instead of all piling in.
③ Start by testing, don't floor it. At the start you don't know how wide the road is, so begin with one packet and double each round until you first feel resistance, then switch to the slow nudging — like easing into an unfamiliar road rather than gunning it.
These moves went into the operating systems of the day and became TCP's standard behavior; the internet never suffered another large-scale congestion collapse, and only then could it carry its later explosive growth. To this day, every connection on your phone still runs this "nudge up, halve down" at its core. One honest cost: it treats every lost packet as "jammed" — but on wireless, packets are often lost to interference with no jam at all, and it will still needlessly cut the rate in half. That's its old sore spot in the Wi-Fi and cellular era.
With no central dispatcher, let each sender pace its packets to the returning echoes, nudge up while things go well, halve on any loss, and double gently at startup. Through this shared self-discipline of "nudge up, halve down," the internet stops jamming itself even though no one is in charge. It is the invisible driver behind every network connection today.
Want the collapse curve, the ACK-clock, and the mechanics of AIMD and timeout estimation? → Switch to the deep read
This paper diagnoses the cause of the internet's first congestion collapse (1986) and adds to TCP a set of algorithms for end-to-end self-regulation — slow-start, congestion avoidance (additive-increase / multiplicative-decrease, AIMD), and better round-trip-time and timeout estimation. The core idea: treat packet loss as the signal that "the network is jammed," so every sender voluntarily throttles up and down with no central dispatcher. Shipped in 4.3BSD, these algorithms became TCP's de-facto standard and are a key foundation for why the internet scaled instead of collapsing.
By Van Jacobson (Lawrence Berkeley National Laboratory) and Michael Karels (UC Berkeley), published at SIGCOMM 1988. It builds on 1970s TCP/IP and the "end-to-end argument" (put intelligence at the two ends, keep the network simple); it answers head-on the congestion collapses that began recurring in 1986; and it seeds the entire lineage of later congestion control — Tahoe, Reno, NewReno, on through CUBIC and BBR — all built on the framework it laid down.
In October 1986, Jacobson observed something absurd: on a link from LBL to the Berkeley campus — a few hundred meters, only three hops — throughput crashed from 32 Kbps to 40 bps, nearly three orders of magnitude. No hardware failure; traffic itself had strangled the network. This is congestion collapse.
The mechanism is a positive-feedback death spiral: once the network jams, packets pile up in router queues and RTT grows; but TCP of the day used a fixed timeout formula (timeout ≈ 2 × smoothed RTT) that badly underestimates under load, so the sender retransmits packets that were merely queued, not actually lost. The duplicate packets double the load on an already-congested network — more jam, longer RTT, more false timeouts, more retransmissions — until almost everything in flight is useless duplicates and useful throughput approaches zero.
The deeper problem: the internet has no central dispatcher — no one knows the global traffic or where the bottleneck is. So control must be distributed and end-to-end: each connection observes and decides for itself. The hard part: how can a crowd of selfish senders, each with only local information, spontaneously converge on a state that wastes no bandwidth, doesn't overrun the network, and is fair to one another?
Jacobson starts from a physical intuition — conservation of packets: a connection running "in equilibrium" should only inject a new packet once an old one has left the network, keeping the number in flight steady and filling the path exactly without overflowing it.
How to achieve this "one out, one in"? The answer is elegant to the point of being nearly free: use the returning ACKs as a clock (self-clocking). Each ACK that comes back means one packet has left the network and been received; only then is a new packet allowed in. The send rate is thus automatically locked to the bottleneck's service rate — however fast the bottleneck releases a packet is how fast the ACK returns, which is how fast a new packet is sent. You never need to know the bottleneck bandwidth; the network "tells" you through the timing of the ACKs.
Self-clocking only holds once you're already in equilibrium, but at connection start there isn't a single packet in the network yet, so the ACK clock hasn't started. If you dump a whole window of data at once based on the receiver's advertised window, you instantly flood the bottleneck queue. Slow-start instead sets the new congestion window cwnd to 1 packet and adds 1 to cwnd for each ACK. The effect: the window doubles every RTT — 1, 2, 4, 8… Despite the name "slow," it's exponential growth, but relative to "full window immediately" it's a very gentle probe. It ramps up until the first loss or a threshold, cleanly "lighting" the ACK clock.
Once probing gets near the network's capacity, you can no longer keep doubling, or one step overshoots. TCP keeps a threshold ssthresh: while cwnd is below it, use slow-start (exponential); above it, switch to congestion avoidance — grow the window by only 1 per RTT (increment cwnd by 1/cwnd per ACK), i.e. additive increase, inching up snugly against capacity. On a detected loss (read as congestion), it does multiplicative decrease: halve the threshold (ssthresh = cwnd/2), reset cwnd to 1, and slow-start back up to the new threshold before resuming additive increase. Why is increase "additive" but decrease "multiplicative"? Because once congestion sets in, queues build explosively, and only an exponential (multiplicative) cut can rein it in; while probing for spare bandwidth must stay conservative, so use the gentlest additive increase. This "slow rise, sharp drop" sawtooth is exactly what lets many connections spontaneously converge to fair, efficient sharing — a result with grounding in control theory (additive-increase / multiplicative-decrease converges to the fairness line).
The fuse of congestion collapse is needless retransmission from false timeouts, so the paper also rewrites timeout estimation. Old TCP tracked only the smoothed mean RTT with a fixed multiplier of 2; Jacobson points out that under load the variation in RTT is itself large and must be estimated too. The new formula tracks both the mean RTT and its mean deviation, taking timeout = smoothed RTT + 4 × deviation, so the timeout loosens adaptively with network jitter and stops mistaking un-lost packets for lost ones at the root. Combined with exponential backoff of the retransmit timer (doubling the wait on successive retransmissions) and Karn's algorithm (don't use a retransmitted packet to estimate RTT, avoiding ambiguity), it finally cuts that positive-feedback spiral.
The paper adds seven cooperating new algorithms to TCP (the self-clocking/slow-start, congestion avoidance, RTT-variance estimation, exponential backoff, and more above). The effect was direct and dramatic: on Jacobson's test links, with the same hardware and load, a severely congested connection whose throughput had cratered to near zero was pulled back to stable throughput close to the link bandwidth, and multiple connections could coexist fairly instead of dragging one another down. The code shipped with 4.3BSD (Tahoe) and was rapidly adopted across operating systems, becoming default TCP behavior across the internet. It has no glamorous theorem; its strength is that it is deployable, immediately effective, and requires no change to devices in the middle of the network — a pure end-software upgrade that cured a network-wide disease.
It established the whole paradigm of internet congestion control: end-to-end, loss as the signal, AIMD converging to fairness. Later work — Reno / NewReno (adding fast retransmit and fast recovery, so a loss halves the window and continues rather than resetting to 1), CUBIC (Linux's default, reshaping the growth curve for high-bandwidth long-distance links), and Google's BBR — all iterate on or argue with its framework. More profoundly, it demonstrated an idea: in a large-scale system with no central authority, consistent, selfish-but-self-disciplined local rules can make global stability and fairness emerge. This "TCP-friendly" social contract is the invisible constitution that let the internet grow into what it is without falling over.
① In one line: add end-to-end self-regulation to TCP, treat loss as the congestion signal, and keep the internet — which has no central dispatcher — from collapsing together.
② The pain: the 1986 congestion collapse dropped throughput from 32 Kbps to 40 bps; the culprit was a positive-feedback death spiral of false timeouts → needless retransmits → more jam.
③ Conservation + self-clocking: pace packets to the returning ACKs, one out and one in, locking the rate to the bottleneck's service rate without knowing the bandwidth.
④ Slow-start: cwnd starts at 1 and doubles per RTT, gently lighting the ACK clock into equilibrium.
⑤ Congestion avoidance (AIMD): above the threshold grow only +1 per RTT (additive increase); on loss halve the threshold (multiplicative decrease); the slow-rise, sharp-drop sawtooth converges to fair and efficient.
⑥ Defusing the fuse: estimate the timeout from RTT mean + deviation (SRTT + 4×dev), plus exponential backoff and Karn's algorithm, cutting the false-retransmit spiral.
⑦ Impact: shipped with 4.3BSD as TCP's standard; the internet stopped collapsing; Reno/CUBIC/BBR are its descendants, and it defined the end-to-end congestion-control paradigm.
⑧ Limits: loss ≠ always congestion (wireless suffers), bufferbloat, RTT unfairness, hard to fill fat-long pipes, depends on everyone playing fair.