What the machine does

A bit that remembers room

A cuckoo table that kicks each full bucket's oldest resident keeps its newest keys at home, but at long caps it fails because age is a stand-in for crowding: the oldest resident's other bucket has room 7% of the time. One bit beside each resident, saying whether its other bucket had room when last read, was proposed as a way to steer by room at no cost, and predicted to leave under 1% of keys in overflow at a cap of 32 for under five reads an insertion. The bit is not free. A blind chain reads only the bucket it kicks into, so the only bit it learns is that of the key it just moved — whose other bucket is the one it was just kicked out of, and full. A bit that says room has to be bought with a read of a fresh key's second bucket, about half a read an insertion. When the bit chooses, it is right three times in five; but it chooses for 40% of kicks at a cap of two and 4% at 32, where it leaves 5.2% of keys in overflow for 6.8 reads.

A bucketed cuckoo table gives each key two buckets of eight slots, and when a new key finds its first bucket full the table moves one of that bucket’s residents to the resident’s other bucket, which may be full in turn and send another on, up to a cap of kicks; a key the chain cannot place waits in a small overflow block. A kick that moves the oldest chose the resident by age. Moving the oldest rather than a random one sorted the table by age, kept the newest keys in their first buckets, and brought a lookup asked mostly of recent keys from 1.084 bucket reads to 1.040 at 95% load — under a cap of two kicks the cheapest design measured.

At longer caps it failed. With a cap of 32, random kicks left no keys in overflow and the oldest kick left 6.6%, and an old key is a crowded key found out why. It had blamed a loop — an old key kicked away and kicked straight back — and sparing recently moved keys did nothing. The reason was the destination. A random resident’s other bucket had room 22% of the time at that cap; the oldest resident’s, 7%. A key that has sat in a bucket longest is a key whose other bucket has been full the longest, so age, under the oldest kick, measures crowding, and a chain steered by age is steered into the crowded corners of the table.

That essay’s closing section proposed reading the crowding directly. Each bucket could keep, beside its residents, one bit per resident saying whether that resident’s other bucket had room the last time it was checked, refreshed whenever a chain passed through; the kick would move the oldest resident whose bit said room, and the oldest overall if none did. The proposal called this a kick that “reads nothing extra”. The prediction was that at a cap of 32 it would leave under 1% of keys in overflow for under five reads an insertion, keeping a lookup by recency under 1.045, and that at a cap of two it would match the plain oldest kick. The named risk was staleness: at 95% load a bucket with room fills within a few insertions.

What a chain can know

Before anything was measured, building the bit exposed a problem the proposal had passed over. A bit about a resident’s other bucket can be written only by reading that bucket, and a blind chain reads exactly one bucket a step: the one it kicks into. It learns whether that bucket has room, and the bucket’s residents stay where they are, carrying no information about where they would go. The one key whose bit the chain can set for nothing is the key it has just placed — and that key’s other bucket is the bucket it was just kicked out of, which the chain has just filled. Every bit a blind chain writes for free is 0.

So a bit that says room has to be bought. The version measured here buys it at the only moment a key’s other bucket is not known to be full: when a fresh key is placed directly in its first bucket, the table reads the key’s second bucket — one more bucket read, charged to the insertion — and records whether it had room. A fresh key that enters by kicking a resident out gets 0, since its second bucket is unread, and every key placed by a kick gets 0 for the reason above. The kick moves the oldest resident with a 1, and if no resident has one, the oldest of all. Everything else is the earlier essays’: 16,384 slots in buckets of eight, filled to 95% and churned eight times its capacity by deleting a random key and inserting a fresh one, with insertion reads and lookups charged as before — one bucket read for a key in its first bucket, two in its second, three in overflow.

A memory that is mostly right

When the bit chooses the key to move, that key's other bucket has room 66%, 61%, 59% of the time at caps of 2, 8, 32 — the memory is mostly right — but it chooses too rarely to matter: over all kicks the guided table finds room 54%, 26%, 9%, against random kicks' 53%, 32%, 22% and the oldest kick's 46%, 21%, 7%A cuckoo table of buckets of eight at 95% load under churn: the share of kicks after which the moved key's other bucket had room, for random kicks, the oldest kicked, and the oldest whose room bit is set (the oldest of all if none is), at caps of 2, 8, 32: cap 2: random 52.9%, oldest 46.5%, by the bit 54.0% (65.7% of the kicks the bit chose); cap 8: random 32.3%, oldest 21.0%, by the bit 25.8% (60.8% of the kicks the bit chose); cap 32: random 22.2%, oldest 7.4%, by the bit 9.3% (58.8% of the kicks the bit chose).cap of 253%46%54% · 66% when the bit chosecap of 832%21%26% · 61% when the bit chosecap of 3222%7%9% · 59% when the bit choserandom kickoldest kickedoldest with room by its bitshare of kicks whose moved key found room in its other bucket95% load, buckets of eightroom after a kick
Fig. 1 At 95% load, the share of kicks after which the moved key’s other bucket had room: random kicks, the oldest kicked, and the oldest with room by its bit. Cap 2: 53%, 46% and 54%. Cap 8: 32%, 21% and 26%. Cap 32: 22%, 7% and 9%. Of the kicks the bit itself chose, 66%, 61% and 59% found room.

When the bit chooses the key to move, that key’s other bucket has room 66%, 61% and 59% of the time at caps of 2, 8 and 32 — the memory is mostly right — but it chooses too rarely to matter: over all kicks the guided table finds room 54%, 26% and 9%, against random kicks’ 53%, 32% and 22% and the oldest kick’s 46%, 21% and 7%. The risk the proposal named turns out to be the smaller of the two problems. A bit written when a key was placed, and read some time later when a chain arrives at that key’s bucket, is right about three times in five; a bucket’s room does change in between, but not so fast that the memory is worthless. Three in five is several times what either the random or the oldest choice gets at the longer caps.

The trouble is the other two in five of the kicks at a cap of two, and nearly all of them at 32. Those are kicks into buckets where no resident carries a 1, and there the rule moves the oldest of all, as the plain oldest kick does — so at the longer caps the guided table finds room almost exactly as often as the oldest kick, 9% against 7%. The memory is accurate and mostly absent.

How many kicks a bit could guide

The bit guides 40%, 12%, 4% of kicks at caps of 2, 8, 32: a moved key's bit is always 0, since its other bucket is the one it left, so the longer the chains the fewer residents carry a 1 — while writing the bits costs 0.57, 0.48, 0.45 reads an insertionFor the room-bit kick at 95% load: the share of kicks where some resident's bit said room (the rest moved the oldest of all), and the reads an insertion spent reading fresh keys' second buckets to set the bits: cap 2: 39.9% guided, 0.567 reads; cap 8: 11.8% guided, 0.478 reads; cap 32: 3.6% guided, 0.448 reads.share of kicks a bit guidedreads an insertion spent on bitscap of 239.9%0.567cap of 811.8%0.478cap of 323.6%0.44895% loadguided kicks · reads for bits
Fig. 2 For the room-bit kick at 95% load: the share of kicks where some resident’s bit said room, and the reads an insertion spent setting the bits. Cap 2: 40% of kicks, 0.57 reads. Cap 8: 12%, 0.48. Cap 32: 4%, 0.45.

The bit guides 40%, 12% and 4% of kicks at caps of 2, 8 and 32: a moved key’s bit is always 0, since its other bucket is the one it left, so the longer the chains the fewer residents carry a 1 — while writing the bits costs 0.57, 0.48 and 0.45 reads an insertion. The decline is the arithmetic of the first section working itself out. Every kick replaces a resident with the key being placed, and that key’s bit is 0; every chain of length kk writes kk zeros into kk buckets. At a cap of two, chains are short and many buckets keep a fresh key or two with a 1. At a cap of 32, chains average more than five kicks an insertion, and the 1s are kicked out faster than fresh keys arrive to replace them.

The bits’ price falls slightly as the cap grows, for the same reason: a fresh key pays for its bit only when it lands in its first bucket directly, and at long caps a larger share of fresh keys land by kicking. At every cap the price is close to half a read an insertion, which at a cap of two is a third more than the whole of the plain oldest kick’s insertion cost, 1.61 reads. The proposal’s “reads nothing extra” was the claim that broke first, and it broke by a third.

Overflow at a long cap

At a cap of 32 the room bit leaves 5.2% of the keys in overflow for 6.82 reads an insertion — not the predicted under 1% for under five — against the plain oldest kick's 6.6% for 7.10 and random kicks' 0.0% for 4.23Keys in overflow and reads an insertion at 95% load, for random kicks, the oldest kicked and the oldest with room by its bit: cap 2: random kick 9.3%, 1.627 reads; oldest kicked 10.9%, 1.610 reads; oldest with room by its bit 8.5%, 2.198 reads. cap 8: random kick 2.7%, 2.803 reads; oldest kicked 7.8%, 2.935 reads; oldest with room by its bit 6.1%, 3.254 reads. cap 32: random kick 0.0%, 4.226 reads; oldest kicked 6.6%, 7.098 reads; oldest with room by its bit 5.2%, 6.821 reads.cap of 29.3% · 1.63 reads10.9% · 1.61 reads8.5% · 2.20 readscap of 82.7% · 2.80 reads7.8% · 2.93 reads6.1% · 3.25 readscap of 320.0% · 4.23 reads6.6% · 7.10 reads5.2% · 6.82 readsrandom kickoldest kickedoldest with room by its bitshare of keys in overflow · reads an insertion95% load, buckets of eightoverflow · insertion reads
Fig. 3 Keys in overflow and reads an insertion at 95% load. Cap 2: random 9.3% for 1.63 reads, oldest 10.9% for 1.61, by the bit 8.5% for 2.20. Cap 8: 2.7% for 2.80, 7.8% for 2.94, 6.1% for 3.25. Cap 32: 0.0% for 4.23, 6.6% for 7.10, 5.2% for 6.82.

At a cap of 32 the room bit leaves 5.2% of the keys in overflow for 6.82 reads an insertion — not the predicted under 1% for under five — against the plain oldest kick’s 6.6% for 7.10 and random kicks’ none for 4.23. The prediction fails on both counts and by large margins. The bit moves the oldest kick a little way toward random kicks: fewer keys in overflow, slightly fewer reads, because the 4% of kicks it guides find room three times as often as the oldest kick’s. It moves it a fifth of the way. Random kicks, which choose nothing, still do best at this cap by every measure of insertion.

At a cap of two the bit does what no other kick here does: it leaves fewer keys in overflow than random kicks, 8.5% against 9.3%. That is the one place where the bit guides enough kicks to change the table, and where the rule’s prediction — that it would match the oldest kick — is wrong in the other direction: the oldest kick at a cap of two leaves 10.9%. But the insertion reads include the bits’ price, 2.20 against 1.61, and for a cap-two table the bits cost more than the overflow they save.

What steering by room costs the lookups

A lookup by recency reads 1.072, 1.068, 1.064 under the room bit at caps of 2, 8, 32, between random kicks and the plain oldest kick's 1.040, 1.039, 1.037: a kick steered by room is a kick no longer steered by age, and newer keys are moved in its placeReads a lookup at 95% load — one for a key in its first bucket, two in its second, three in overflow — weighted by recency (the newest key asked in proportion to 1/(r + 1)) and evenly: cap 2: random kick 1.084 and 1.420; oldest kicked 1.040 and 1.331; oldest with room by its bit 1.072 and 1.402. cap 8: random kick 1.110 and 1.436; oldest kicked 1.039 and 1.313; oldest with room by its bit 1.068 and 1.366. cap 32: random kick 1.124 and 1.426; oldest kicked 1.037 and 1.295; oldest with room by its bit 1.064 and 1.352.cap of 21.084 by recency · 1.420 evenly1.040 by recency · 1.331 evenly1.072 by recency · 1.402 evenlycap of 81.110 by recency · 1.436 evenly1.039 by recency · 1.313 evenly1.068 by recency · 1.366 evenlycap of 321.124 by recency · 1.426 evenly1.037 by recency · 1.295 evenly1.064 by recency · 1.352 evenlyrandom kickoldest kickedoldest with room by its bitreads a lookup beyond the first, by recency95% load, buckets of eightbars start at one read
Fig. 4 Reads a lookup at 95% load, by recency (the newest key asked in proportion to 1/(r + 1)) and evenly, per cap. By recency: random 1.084, 1.110 and 1.124; oldest 1.040, 1.039 and 1.037; by the bit 1.072, 1.068 and 1.064.

A lookup by recency reads 1.072, 1.068 and 1.064 under the room bit at caps of 2, 8 and 32, between random kicks and the plain oldest kick’s 1.040, 1.039 and 1.037: a kick steered by room is a kick no longer steered by age, and newer keys are moved in its place. The third prediction, a lookup by recency under 1.045, fails at every cap, and the reason is built into the rule. The residents with a 1 are fresh keys that landed in their first buckets and have never been moved — the very keys the oldest kick protected. Choosing the oldest of them is choosing a key younger than the bucket’s oldest, and a key moved to its second bucket costs a lookup two reads instead of one. Under the bit the table holds 23% of its keys in their second buckets at a cap of two, where the oldest kick holds 11%.

The even lookup tells the same story without the weighting: 1.402 at a cap of two, against the oldest kick’s 1.331 and random kicks’ 1.420. The rule is closer to random than to the design it was meant to improve, because it spends its choice on a different property. Age and room pull the kick in different directions, and a rule that takes room when it can and age when it cannot gets some of each benefit and all of neither.

Every design, priced together

Charged for an insertion and its lookups by recency together, the room-bit kick is the cheapest design at no mix from 0.1 to 100 lookups an insertion; its best setting trails the best design by 0.038 to 0.537 reads an operationReads an insertion plus mix lookups by recency at 95% load, for random kicks, the oldest kicked and the room-bit kick at caps of 2, 8, 32: at each mix, the cheapest design and the best room-bit design. 0.1: best oldest kicked, cap 2 1.714, best bit oldest with room by its bit, cap 2 2.305; 0.3: best oldest kicked, cap 2 1.923, best bit oldest with room by its bit, cap 2 2.519; 1: best oldest kicked, cap 2 2.651, best bit oldest with room by its bit, cap 2 3.270; 3: best oldest kicked, cap 2 4.731, best bit oldest with room by its bit, cap 2 5.415; 10: best oldest kicked, cap 2 12.014, best bit oldest with room by its bit, cap 2 12.922; 30: best oldest kicked, cap 2 32.820, best bit oldest with room by its bit, cap 2 34.369; 100: best oldest kicked, cap 2 105.644, best bit oldest with room by its bit, cap 2 109.437.cheapest designbest room-bit design, reads an operation behind0.1 lookupsoldest kicked, cap 20.537 · cap 20.3 lookupsoldest kicked, cap 20.459 · cap 21 lookupsoldest kicked, cap 20.310 · cap 23 lookupsoldest kicked, cap 20.171 · cap 210 lookupsoldest kicked, cap 20.083 · cap 230 lookupsoldest kicked, cap 20.050 · cap 2100 lookupsoldest kicked, cap 20.038 · cap 295% load, lookups by recencyper operation
Fig. 5 Reads an insertion plus its lookups by recency at 95% load, at mixes of 0.1 to 100 lookups an insertion: the cheapest of the nine designs (random, oldest and room-bit kicks at caps of 2, 8 and 32) at each mix, and how far the best room-bit design trails it in reads an operation. The oldest kick at a cap of two is cheapest at every mix; the room-bit kick trails by 0.04 to 0.54.

Charged for an insertion and its lookups by recency together, the room-bit kick is the cheapest design at no mix from 0.1 to 100 lookups an insertion; its best setting trails the best design by between 0.04 and 0.54 reads an operation. At every mix the cheapest design is still the plain oldest kick under a cap of two, which overflow holds the old keys had found cheapest of the random designs’ rivals, and a kick that moves the oldest made cheaper still. The room bit’s best setting is also at a cap of two, and it loses most where insertions dominate — half a read an operation at one lookup an insertion or fewer, which is the bits’ price almost exactly — and least where lookups dominate, since its lookups are only three hundredths dearer.

So the bit loses twice: once at insertion, for the read that writes it, and again at lookup, for the newer keys it moves away from home. Neither loss is large. Together they make it the wrong design at every workload measured.

Worth most where it is rarest

The plates contain a crossing that neither the proposal nor its risk anticipated. The bit’s value as information is how much better it does than the choice it replaces — the oldest kick, which is what the rule falls back to — and that rises with the cap: at a cap of two the key the bit chooses finds room 66% of the time against the oldest key’s 46%, twenty points; at a cap of eight, 61% against 21%, forty points; at a cap of 32, 59% against 7%, fifty-one points. Long chains are the ones that wander into crowded buckets, and there a memory of where room was is worth the most.

Its coverage falls along exactly the same axis — 40%, 12%, 4% of kicks — because long chains are also the ones that write zeros fastest. The product of the two, the extra kicks that find room because a bit was there, is small at every cap: about eight kicks in a hundred at a cap of two, five at eight, two at 32 — which is, to the point, the guided table’s lead over the oldest kick in the first plate. That is the whole of what the bit adds, and it is why the guided table’s room-after-kick sits so close to its unguided neighbour’s.

Staleness, the named risk, is visible in the gap between the bit’s three in five and certainty. A bit written when a fresh key arrived is read when a later chain reaches the same bucket, and at 95% load, in two cases in five, some other chain has by then taken that bucket’s last free slot. A fresher memory would close part of that gap and leave the coverage untouched, so freshness is not where the bit’s shortfall lies.

Where the information was

The plates leave the proposal’s idea standing and its premise refuted. The idea was that a table could steer its kicks by where its room is rather than by a proxy for it, and the bit, when it has something to say, is right three times as often as age is at the longer caps. The premise was that the information comes free with the chains, and it does not, because a blind chain is blind in exactly the direction the bit needs: it sees where a key is going, never where the keys it leaves could go.

There is one operation in the table that reads every resident’s other bucket, and it is the look. A kick that looks before it moves introduced it: before kicking anyone, read each resident’s other bucket and move one that has room, if any does. The look is expensive — the look that moved the oldest resident with room cost 7.70 reads an insertion — and its answers are thrown away when the insertion ends. A look that spreads the room moved whichever resident left the most room behind, and threw those answers away too. A look is the one moment the bits proposed here could be written correctly for a whole bucket at once.

Room the chains could not reach found, under a cap of two at 95% load, 13.8% of the table’s entries standing empty while 9.2% of its keys waited outside their buckets. That is the same fact seen from the other end: room exists, and the operations that move keys do not learn where it is. The keys a capped chain leaves behind priced the search that does not know, finding five sixths of the bytes an insertion moves at that load in its kick chain, and most of them in the chain’s tail.

What the replay does not measure

One way to buy the bit. The bit is set by one read when a fresh key lands in its first bucket. A table could instead refresh every resident’s bit whenever its bucket is read during a chain, at the cost of reading eight other buckets, which is a look by another name.

One table. 16,384 slots, buckets of eight, 95% load, one churn — the layout the bucket that fits a line chose so that a lookup never touches more than two cache lines and the table builds past 95%. At lower loads, room lasts longer, the bits would be right more often, and more fresh keys would land directly and buy one; at higher loads the reverse.

Reads, not time. A bit read and a bucket read are charged as the replay charges them, and the bit itself is not charged as space: one bit a slot, a sixty-fourth of a slot of eight-byte keys, which no plate here counts.

Still open: a look whose answers are kept

The bit fails because the chains cannot write it, and the look writes nothing it learns. Join them. Let the table look only when a bucket’s stored answers are missing, and keep what it learns: when an insertion meets a full bucket whose residents have no bits, it reads their other buckets once, as the look does, records one bit per resident, and uses them; when a later chain arrives at the same bucket, it reads the bits instead of the buckets, and moves the oldest resident with a 1. A kick into a bucket clears the moved-in key’s bit, as before, so a bucket’s answers wear out as chains pass and are bought again when they run out.

The measurement that follows runs that kept look at caps of two, eight and 32, charging every look’s reads to the insertion that made it. The prediction is that at a cap of 32 it leaves under 2% of keys in overflow for under six reads an insertion, because most chains arrive at a bucket whose answers are still fresh, and that its lookups by recency stay near 1.07, as the bit’s did. It could fail on how fast answers wear out: at 95% load, a bucket’s room may change after a handful of insertions, and if the kept answers are mostly stale when used, the table pays for looks it cannot use. The question is whether a cuckoo table can remember where its room is at any price below a look per insertion, or whether at this load the room moves too fast for any memory to keep up.

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