What the machine does

An old key is a crowded key

A cuckoo table that makes room by kicking each full bucket's oldest resident keeps its newest keys at home and brings a lookup asked by recency to 1.04 reads, the cheapest design measured — but only at a cap of two kicks. At a cap of 32 it leaves 6.6% of its keys in overflow where random kicks leave none, and the explanation offered was a loop: an old key kicked to its other bucket is old there too, and the next chain moves it back. Sparing every key the chain has already moved changes nothing at caps of two and eight and makes cap 32 slightly worse, 7.1%; sparing the last sixteen insertions' moves gets it to 5.0%. There was no loop. A kicked resident's other bucket has room 22% of the time when the resident is chosen at random and 7% when it is the oldest: in a table that always moves the oldest, a key grows old because both of its buckets stay full. Kicking the older of four drawn at random keeps much of the gain at a long cap, 1.055 reads by recency and 0.5% overflow, and still costs more than the oldest kick at a cap of two.

A kick that moves the oldest changed one choice in a cuckoo table. When a new key finds its first bucket full, the table moves a resident of that bucket to the resident’s other bucket, which may be full and send another on, up to a cap of kicks; a key the chain cannot place waits in a small overflow block. Moving the oldest resident rather than one at random sorted the table by age. The newest three tenths of the keys almost never left their first buckets, a lookup asked mostly of recent keys fell from 1.084 bucket reads to 1.040 at 95% load, and even lookups fell too, from 1.420 to 1.331, since fewer keys in all sat away from home. Under a cap of two kicks it was the cheapest design at every mix of lookups to insertions measured.

At longer caps it went wrong. A random chain allowed 32 kicks almost always finds room, and leaves 0.01% of the keys in overflow; an oldest chain allowed 32 kicks left 6.6%, for 6.10 kicks an insertion against 3.23. That essay offered an explanation the counts allowed but did not show: the oldest kick keeps choosing keys it has just moved, since a key kicked into its other bucket is old, so it is likely to be the oldest there and to be moved back by the next chain through that bucket. Its closing section proposed sparing such keys — the oldest resident not moved within the last few insertions, or not moved by this chain — and predicted that at a cap of 32 the spared kick would leave under 1% of keys in overflow for under four kicks an insertion, keeping a lookup by recency near 1.04, with nothing changed at a cap of two. It named the way to fail: if the repeated moves were spread across chains far apart, a short memory would not see them, and the trouble would be a property of always choosing by age.

Three memories

The table, churn and charges are the earlier essays’: 16,384 keys in buckets of eight at 95% load, churned eight rounds so that what is measured is a steady state, a lookup charged one, two or three bucket reads for a key in its first bucket, its second or overflow, and asked by recency in proportion to 1/(r+1)1/(r + 1) or evenly. A key not stored is charged both buckets and its region’s overflow block, if the block holds anything.

Three memories were given the oldest kick. Under the first, a chain passes over any resident it has already moved and takes the oldest of the rest. Under the second, the kick passes over any resident moved by the previous insertion’s chain or this one. Under the third, any resident moved within the last sixteen insertions. If every resident of a bucket was spared, the kick would take the oldest regardless; that fallback was counted, and never happened. A second count was added for every chain: whether the key just kicked found room in its other bucket, which is the event that ends a chain.

No loop to break

Sparing the keys an oldest kick has just moved changes almost nothing at caps of two and eight and little at 32: sparing this chain's moves leaves 7.1% of the keys in overflow against 6.6%, sparing the last sixteen insertions' 5.0% — the long chains were not loopingAt 95% load after churn, blind kicks that move the oldest resident, the oldest not moved by this chain, and the oldest not moved within the last sixteen insertions, at caps of 2, 8, 32: share of keys in overflow and kicks an insertion. cap 2, oldest: 10.9%, 0.61 kicks; cap 2, not moved this chain: 10.9%, 0.61 kicks; cap 2, not moved in sixteen: 10.8%, 0.61 kicks; cap 8, oldest: 7.8%, 1.93 kicks; cap 8, not moved this chain: 8.0%, 1.90 kicks; cap 8, not moved in sixteen: 7.5%, 1.92 kicks; cap 32, oldest: 6.6%, 6.10 kicks; cap 32, not moved this chain: 7.1%, 6.45 kicks; cap 32, not moved in sixteen: 5.0%, 6.51 kicks. A memory of the last insertion alone is the chain's at every cap.share in overflowkicks an insertioncap 2 · oldest10.9%0.61cap 2 · not moved this chain10.9%0.61cap 2 · not moved in sixteen10.8%0.61cap 8 · oldest7.8%1.93cap 8 · not moved this chain8.0%1.90cap 8 · not moved in sixteen7.5%1.92cap 32 · oldest6.6%6.10cap 32 · not moved this chain7.1%6.45cap 32 · not moved in sixteen5.0%6.5195% load, after churnthree memories
Fig. 1 At 95% load, blind kicks that move the oldest resident, the oldest not moved by this chain, and the oldest not moved within sixteen insertions, at caps of 2, 8 and 32: the share of keys in overflow and kicks an insertion. Cap 2: 10.9%, 10.9%, 10.8%. Cap 8: 7.8%, 8.0%, 7.5%. Cap 32: 6.6%, 7.1%, 5.0%, for 6.10, 6.45 and 6.51 kicks. A memory of the last insertion alone gives exactly what the chain’s memory gives.

Sparing the keys an oldest kick has just moved changes almost nothing at caps of two and eight and little at 32: sparing this chain’s moves leaves 7.1% of the keys in overflow against 6.6%, sparing the last sixteen insertions’ 5.0% — the long chains were not looping. At a cap of two the chain’s memory cannot matter, since a chain of two kicks visits three buckets and almost never returns to one of them, and the table is identical to the bit. At a cap of 32 a chain can return, and sparing its own moves makes it slightly worse, which says that the keys it would have moved back were, on balance, good choices. Remembering the previous insertion’s chain as well adds nothing: it is the same table as remembering this chain alone, at every cap, so chains do not meet each other’s recent moves either.

Sixteen insertions’ memory is the only one that moves the number, by a quarter, and it does so at a cost of more kicks and more keys in second buckets, 19% against 16%. It is not a loop broken; it is the kick made a little less faithful to age, which the next plates show is the whole of what helps. The prediction’s under 1% is not reached by any memory, and its premise, that the oldest chain was undoing its own work, is refuted by the first row of the plate.

Where the chains land

An old key's other bucket is a crowded one: at a cap of 32 a resident kicked at random finds room in its other bucket 22% of the time, the older of two 19%, of four 15%, and the bucket's oldest 7% — a key grows old in a table that kicks the oldest because both of its buckets stay fullAt 95% load after churn, the share of kicks whose moved key found room in its other bucket, for blind kicks moving the oldest of 1, 2, 3, 4 residents drawn at random or the bucket's oldest, at caps of 2, 8, 32: cap 2: 1 52.9%, 2 52.2%, 3 51.2%, 4 50.3%, 8 46.5%; cap 8: 1 32.3%, 2 30.9%, 3 29.7%, 4 28.2%, 8 21.0%; cap 32: 1 22.2%, 2 19.0%, 3 16.5%, 4 14.8%, 8 7.4%.00.2000.4000.600residents drawn, the oldest of them kicked (8: the bucket's oldest)kicks whose key found room12348cap of 2cap of 8cap of 3295% load, after churnshare of kicks
Fig. 2 At 95% load, the share of kicks whose moved key found room in its other bucket, for blind kicks that move the oldest of 1 (a random resident), 2, 3 or 4 residents drawn at random, or the bucket’s oldest, at caps of 2, 8 and 32. Cap 32: 22.2%, 19.0%, 16.5%, 14.8% and 7.4%. Cap 8: 32.3%, 30.9%, 29.7%, 28.2% and 21.0%. Cap 2: 52.9%, 52.2%, 51.2%, 50.3% and 46.5%.

An old key’s other bucket is a crowded one: at a cap of 32 a resident kicked at random finds room in its other bucket 22% of the time, the older of two 19%, of four 15%, and the bucket’s oldest 7% — a key grows old in a table that kicks the oldest because both of its buckets stay full. The chain ends at the first kicked key whose other bucket has room, so this share is what sets how long chains run and how often they reach the cap. A random resident’s other bucket is any bucket, and at 95% load a fifth of the buckets reached this way have room. The oldest resident’s other bucket is not any bucket.

The reason is in what makes a key old in this table. Under the oldest kick a key leaves a bucket only when it is the oldest resident there and an insertion finds the bucket full. A key in an uncrowded bucket is rarely in a full bucket at all, so it is rarely kicked, but it is also rarely the oldest of anything for long, since its neighbours come and go with the churn. A key that survives to be the oldest in its bucket has survived there, and the keys that are oldest in both of their buckets are the keys whose buckets are both persistently full — the crowded corners of the table. Kicking the oldest sends the chain to exactly those corners.

So the trouble at long caps was the one the proposal named as the alternative: a property of always choosing by age, not of a short loop. Choosing by age selects keys by their buckets’ fullness, and a chain that follows such keys walks from full bucket to full bucket.

The older of a few

Drawing a few residents and kicking the oldest of them trades the oldest kick's lookups against the random kick's chains: at a cap of 32 the older of four reads 1.055 a lookup by recency for 5.83 an insertion and leaves 0.5% in overflow; but the oldest kick at a cap of two reads 1.040 for 1.61, and no drawn rule at any cap reaches itAt 95% load after churn, for blind kicks moving the oldest of 1 (random), 2, 3, 4 residents drawn, or the bucket's oldest, at caps of 2, 8, 32: reads an insertion, reads a lookup by recency, and share in overflow. cap 2: 1 1.63, 1.084, 9.3%; 2 1.63, 1.062, 9.4%; 3 1.63, 1.052, 9.5%; 4 1.63, 1.049, 10.2%; 8 1.61, 1.040, 10.9%. cap 8: 1 2.80, 1.110, 2.7%; 2 2.86, 1.071, 3.1%; 3 2.89, 1.057, 3.7%; 4 2.93, 1.050, 4.1%; 8 2.93, 1.039, 7.8%. cap 32: 1 4.23, 1.124, 0.0%; 2 4.78, 1.087, 0.1%; 3 5.38, 1.064, 0.3%; 4 5.83, 1.055, 0.5%; 8 7.10, 1.037, 6.6%.1.021.051.071.101.13246bucket reads an insertionreads a lookup by recencyrandomrandomrandomcap of 2cap of 8cap of 32down and left is better95% load
Fig. 3 At 95% load, reads a lookup by recency against bucket reads an insertion, for blind kicks moving a random resident, the older of 2, 3 or 4 drawn at random, or the bucket’s oldest, at caps of 2, 8 and 32. At cap 32: 1.124 for 4.23 reads (random), 1.087 for 4.78, 1.064 for 5.38, 1.055 for 5.83, 1.037 for 7.10 (oldest). At cap 2 the oldest: 1.040 for 1.61.

Drawing a few residents and kicking the oldest of them trades the oldest kick’s lookups against the random kick’s chains: at a cap of 32 the older of four reads 1.055 a lookup by recency for 5.83 an insertion and leaves 0.5% in overflow; but the oldest kick at a cap of two reads 1.040 for 1.61, and no drawn rule at any cap reaches it. The draw interpolates as it should. Each resident added to the draw makes the kicked key a little older and its chain a little longer: at a cap of 32 the older of two takes 0.56 more reads an insertion than a random kick for 0.037 less a lookup, the older of four 1.60 more for 0.069 less. Overflow rises from nought to half a per cent, far under the oldest kick’s 6.6%.

None of that beats the short cap. The oldest kick at a cap of two gets the whole of the age-sorting at a chain of at most two kicks, because it never has to find room far away: a key it cannot place goes to overflow, and under recency overflow holds keys nobody asks for. Lengthening the cap is a way of keeping keys out of overflow, and the plates have been saying since overflow holds the old keys that under recency that is not worth paying for. The draw is a better long-cap design than the oldest kick, not a better design. The keys a capped chain leaves behind measured how much overflow each cap leaves under random kicks; under recency the right cap was always the short one, and choosing by age makes it shorter still.

Even lookups

Under even lookups every move toward the oldest helps, at every cap — the random kick at a cap of 32 reads 1.426 a lookup with no overflow, the older of four 1.323 with 0.5%, the oldest 1.295 with 6.6% — because keys moved by age leave fewer keys in second buckets, and overflow is the priceAt 95% load after churn: reads an even lookup against the share in overflow, for blind kicks moving the oldest of 1, 2, 3, 4 drawn or the bucket's oldest, at caps of 2, 8, 32: cap 2: 1 1.420 at 9.3% (second buckets 23.5%); 2 1.391 at 9.4% (second buckets 20.2%); 3 1.366 at 9.5% (second buckets 17.5%); 4 1.365 at 10.2% (second buckets 16.2%); 8 1.331 at 10.9% (second buckets 11.3%). cap 8: 1 1.436 at 2.7% (second buckets 38.3%); 2 1.380 at 3.1% (second buckets 31.8%); 3 1.350 at 3.7% (second buckets 27.5%); 4 1.337 at 4.1% (second buckets 25.5%); 8 1.313 at 7.8% (second buckets 15.6%). cap 32: 1 1.426 at 0.0% (second buckets 42.6%); 2 1.386 at 0.1% (second buckets 38.4%); 3 1.344 at 0.3% (second buckets 33.9%); 4 1.323 at 0.5% (second buckets 31.2%); 8 1.295 at 6.6% (second buckets 16.3%).1.301.351.401.4500.0250.0500.0750.100share of keys in overflowreads an even lookupcap of 2cap of 8cap of 32from random (left) to oldest95% load
Fig. 4 At 95% load, reads an even lookup against the share in overflow, for the same drawn rules at three caps. At cap 32: random 1.426 with no overflow, the older of two 1.386, of three 1.344, of four 1.323 with 0.5%, the oldest 1.295 with 6.6%. At cap 2: random 1.420 with 9.3%, the oldest 1.331 with 10.9%.

Under even lookups every move toward the oldest helps, at every cap — the random kick at a cap of 32 reads 1.426 a lookup with no overflow, the older of four 1.323 with 0.5%, the oldest 1.295 with 6.6% — because keys moved by age leave fewer keys in second buckets, and overflow is the price. At a cap of 32 a random kick leaves 43% of the keys in their second buckets; the older of four 31%; the oldest 16%. A table that concentrates its displacements on a few old keys has fewer keys displaced, and an even workload pays a second read for each one.

The even lookups make the draw look better than the recency plate did. The older of four at a cap of 32 reads 1.323 an even lookup with half a per cent in overflow; the oldest kick at a cap of two reads 1.331 with 10.9%. For a workload that asks for all keys alike and cares about overflow — because its absent-key lookups read the overflow block — the drawn rule at a long cap is the better design, and the next plate prices exactly that.

Keys that are not there

Overflow is paid for by lookups of keys not stored: at 10 lookups an insertion the oldest kick at a cap of two is cheapest while every lookup is for a stored key, 1.092 reads an operation, but its regions all have overflow, so an absent key costs three reads, and from 50% of lookups absent the two-level look, which leaves none, is cheaperAt 95% load, 10 lookups an insertion, a share of them for keys not stored and the rest by recency, bucket reads an operation: random, cap of 2: 0% 1.134, 10% 1.308, 25% 1.569, 50% 2.004, 75% 2.440; oldest, cap of 2: 0% 1.092, 10% 1.270, 25% 1.538, 50% 1.983, 75% 2.428; older of four, cap of 32: 0% 1.489, 10% 1.619, 25% 1.815, 50% 2.142, 75% 2.469; look, first with room, cap 2: 0% 1.378, 10% 1.485, 25% 1.647, 50% 1.916, 75% 2.185; two-level look: 0% 1.379, 10% 1.462, 25% 1.586, 50% 1.794, 75% 2.002. An absent key reads both buckets and its region's overflow block whenever that block holds anything.11.5022.5000.1000.2500.5000.750share of lookups for keys not storedbucket reads an operationolder of four, cap of 32random, cap of 2oldest, cap of 2look, first with room, cap 2two-level look10 lookups an insertion95% load
Fig. 5 At 95% load and ten lookups an insertion, a share of the lookups for keys not stored and the rest by recency: bucket reads an operation. With no absent lookups: the oldest kick at a cap of two 1.092, random 1.134, the older of four at cap 32 1.489, the first-found look 1.378, the two-level look 1.379. With half absent: 1.983, 2.004, 2.142, 1.916 and 1.794.

Overflow is paid for by lookups of keys not stored: at ten lookups an insertion the oldest kick at a cap of two is cheapest while every lookup is for a stored key, 1.092 reads an operation, but its regions all have overflow, so an absent key costs three reads, and from half the lookups absent the two-level look, which leaves none, is cheaper. Every design that leaves overflow anywhere in a region charges an absent lookup in that region three reads, and at 9% to 11% of keys in overflow every one of the table’s regions has some. The two-level look leaves none and charges two. At a quarter of lookups absent the oldest kick still wins, 1.538 against 1.586; at a half the look wins, 1.794 against 1.983.

The drawn rule never wins here. The older of four at a cap of 32 leaves overflow in half the regions, so its absent lookups cost two and a half reads, but its insertions cost 5.83, and at ten lookups an insertion that is too much to recover. Room the chains could not reach found that a cuckoo table’s empty slots end where its chains cannot find them; the oldest kick’s overflow ends in every region, because its chains reach the crowded corners in all of them.

What the counts establish

The closing question was whether the oldest kick’s trouble at long caps is a loop inside a chain, or a property of always choosing by age. The first plate rules out the loop at every memory tested: sparing a chain’s own moves makes the table worse, and sparing the previous chain adds nothing. The second shows the property directly: the oldest resident’s other bucket has room a third as often as a random resident’s. Age, in a table that kicks by age, is a measurement of crowding, and a rule that chooses the oldest chooses the key whose way out is most likely blocked.

That is also why the oldest kick works so well at a cap of two. A short cap does not ask the oldest key to find a way out; it asks it to step aside, once or twice, and then accepts overflow. The rule’s strength and its weakness are the same fact seen at two cap lengths.

Reading room against reading age

The cuckoo designs measured in these essays have split, from the start, into those that read before they move and those that do not. A kick that looks before it moves read every resident’s other bucket and moved the first with room; a look that spreads the room moved the one whose other bucket was emptiest. Both pay a read for every resident, which at buckets of eight is the reason their insertions cost four to seven reads where a blind kick costs under two. The bucket that fits a line made the resident’s own bucket one read; the residents’ other buckets are eight more.

The oldest kick reads nothing extra and chooses by a field already inside the bucket. What these plates show is that the field it reads carries information about the very thing the looks pay to read: an old resident is, more often than not, a resident whose other bucket is full. At a cap of two that is harmless, since the chain gives up quickly. At a cap of 32 it is the worst possible information to act on, and a blind kick that ignores it does better. So the choice of whom to kick is not free of the question of where there is room; it answers it badly, by proxy.

That is the pattern two probes are two misses found on a different axis: a table’s cost is set by where its accesses land, and a rule chosen for one property of the keys quietly decides another.

What the table does not show

One size, one bucket, one load. The plates are at 95% load in buckets of eight. The crowding that makes old keys hard to place is a property of a full table, and at 90% the long-cap oldest kick may behave differently; it was not run here.

Ages for free. Ages are kept beside each key at no charge, and so are the spared-move memories. A memory of sixteen insertions’ moves is a small hash set; a real table would pay for it, and it buys a quarter of the overflow at a long cap.

Draws from one generator. The drawn residents come from the same random stream as the random kick, so a draw of four makes three more draws than a random kick and shifts the stream of later choices. The comparison between draw sizes is therefore between different random tables, not one table with a parameter turned.

Still open: room counted, not age

If the oldest kick fails at long caps because age is a proxy for crowding, a kick could read the crowding directly. The first-found look already reads every resident’s other bucket and moves the first with room, and the look that moved the oldest with room was dearer than the blind oldest kick, 7.70 reads an insertion. Between them is a kick that reads nothing extra: each bucket can keep, beside its residents’ ages, one bit per resident saying whether that resident’s other bucket had room the last time it was checked, refreshed whenever a chain passes through. A kick could move the oldest resident whose bit says room, and the oldest overall if none does.

The measurement that follows keeps those bits, refreshed by every chain and look that reads a bucket, and charges the same workloads at caps of two, eight and 32. The prediction is that at a cap of 32 the bit-guided oldest kick leaves under 1% of keys in overflow for under five reads an insertion, keeping a lookup by recency under 1.045, because it sends chains away from the crowded corners while still choosing by age, and that at a cap of two it matches the plain oldest kick. It could fail on staleness: a bit set when a chain last passed may be wrong by the time it is read, since a bucket with room fills within a few insertions at 95% load. The question is whether a table can remember where its room is cheaply enough to steer by it, or whether room at this load moves faster than any memory of it.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

The objects this essay names

Each one links to every other essay that touches it.

Cuckoo hashingDesign parameterHash tableLoad factorMeasured countWorkload