When the algorithm flips a coin

A queue that drains at intervals

A blocked filter's threshold table, its lines sized for the mean number of closed blocks and each line's surplus carried into the next lines' free slots, is cheapest in bits and dear in lines, because at room for the mean the queue of carried values never drains. Capping the walk turned the cap into an overflow. Giving every eighth line a deviation of extra room was predicted to empty the queue there, bound every walk by eight lines, and cost about 3.15 bits a block for under 2.02 lines a lookup. It costs 3.123 bits for 2.103 lines. After a slack line the queue is empty a third of the time, walks reach 33 lines, and the reason is arithmetic: one line's deviation of room is not room for eight lines' wander, which grows as the square root of eight. The slack lines do buy something — a hundredth or two of a bit a block under spreading the same room evenly, at five of the six spacings — but they buy it at lines no other design here would accept.

A threshold filter splits its keys between two blocks and records, for each block, the threshold at which it closed to new keys; a lookup reads the block’s threshold before it reads the block. The thresholds live in a table packed into 512-bit lines, and most of the essays on this filter have been about the table. A bit for whether it closed stored one bit for every block and a four-bit value only for the blocks that closed, which is about half of them, so each line holds a run of blocks and room for the values of those that closed. The room has to be sized in advance, and the number of closed blocks in a line varies.

The room the lines lend sized every line for the mean and let a line’s surplus — the closed blocks it has no room for — carry forward, in order, into the next lines’ free slots. A lookup of a carried value reads its own line, then walks forward, reading each line’s free slots from its own count, until it reaches the line that holds the value. At room for the mean that table was the cheapest in bits measured, 3.113 bits a block, and dear in lines, 2.183 a lookup against the two of a table that never carries: a carried value joined a queue with no slack and walked 12.8 lines on average. A walk that stops at two lines capped the walk and sent anything that would go further to a shared overflow, and found that at room for the mean 53% of carried values went to overflow; the cap became an ungrouped overflow by another name.

That essay’s closing section diagnosed the walk. At room for the mean, free slots and surplus balance on average, so the queue’s length is a random walk with no drift — it wanders, and most of the time it is long. A queue that drains needs room somewhere, and the proposal was to put the room at intervals rather than everywhere: every kk-th line sized for a deviation more than the mean, so that the queue empties into it, and no carried value walks past the next slack line. The prediction was that a slack line every eighth line, with a deviation of extra room, would cost about 3.15 bits a block for under 2.02 lines a lookup, two hundredths of a bit under the reserve sized for the spill. The named risk was a queue that had grown since the last slack line longer than any deviation of room.

A line that holds fewer blocks

A line is 512 bits whatever it holds, so room is bought with blocks. An ordinary line here holds 165 blocks and room for 85 closed values, which is the mean number of closed blocks among 165 rounded up, plus the bits for each block’s closed flag and the count of values waiting. A slack line sized for one deviation more than the mean holds 156 or 157 blocks and room for 87 values; for two deviations, 148 or 149 blocks and room for 89. Every other line is ordinary, at room for the mean, and there is no cap on the walk: a value waits until some line has a free slot for it, and the few still waiting at the end of the table go to an overflow area.

The layout repeats every kk lines, so a block’s line is arithmetic and a lookup needs no index — the property positions confined to one line made the starting point of this whole filter, where a lookup’s positions were confined to one block so that it reads one line. Everything else is the earlier essays’: a split threshold filter of 524,288 keys in 64-bit blocks, sixteen bits a key, four-bit threshold values, two filters a seed and two seeds, a hundred thousand absent lookups a filter, every stored key checked to be found. Lines a lookup counts the threshold table’s lines read plus the block’s own line.

What a slack line holds

A line is 512 bits whatever it holds, so a slack line buys its room with blocks: ordinary line 165 blocks and 85 slots, slack, 1 deviation 157 blocks and 87 slots, slack, 2 deviations 149 blocks and 89 slots — the extra room is 6 or 12 slots beyond the share an ordinary line gives the same blocksBlocks and threshold slots a 512-bit line holds: ordinary line: 165 blocks, 85 slots; slack, 1 deviation: 157 blocks, 87 slots; slack, 2 deviations: 149 blocks, 89 slots, with slack every eighth line.ordinary line165 blocks85 slotsslack, 1 deviation157 blocks87 slotsslack, 2 deviations149 blocks89 slotsblocks the line codesthreshold slots, its room for closed blockslines of 512 bitsblocks and slots
Fig. 1 Blocks and threshold slots a 512-bit line holds, with slack every eighth line: an ordinary line 165 blocks and 85 slots, a slack line at one deviation 157 blocks and 87 slots, at two deviations 149 blocks and 89 slots.

A line is 512 bits whatever it holds, so a slack line buys its room with blocks: an ordinary line holds 165 blocks and 85 slots, a slack line at one deviation 157 blocks and 87, at two deviations 149 blocks and 89 — the extra room is a handful of slots beyond the share an ordinary line gives the same blocks. The plate is the cost side of the proposal. A slack line at one deviation gives up eight blocks to buy two more slots, and since its 157 blocks fill about 80 of its 87 slots on average, it has about seven slots to spare for the queue. At two deviations it gives up sixteen blocks and has about thirteen to spare. Those spare slots are all the draining any slack line can do.

The other cost is the table’s length. A table with one line in eight holding eight fewer blocks needs about 0.6% more lines, which is the bits the proposal expected to spend. What it did not expect is how little draining eight slots buy.

The frontier

A slack line every eighth line, with a deviation of extra room, costs 3.123 bits a block for 2.1030 lines a lookup — cheaper in bits than the predicted 3.15 and five times further over two lines than the predicted under 2.02 — and 5 of the 6 slack spacings land 0.012 to 0.020 bits under the uncapped walk at the same linesBits a block against lines a lookup on the largest table. Slack lines every k-th line at room for the mean, extra room e deviations: k 4, e 1: 3.142 for 2.0537, k 4, e 2: 3.180 for 2.0316, k 8, e 1: 3.123 for 2.1030, k 8, e 2: 3.141 for 2.0625, k 16, e 1: 3.118 for 2.1319, k 16, e 2: 3.132 for 2.0986. Uncapped walk at room 0: 3.113 for 2.1832, 0.25: 3.161 for 2.0520, 0.5: 3.190 for 2.0187, 0.75: 3.215 for 2.0098, 1: 3.282 for 2.0047, 1.5: 3.346 for 2.0012. Walk capped at two lines: 3.180 for 2.0267. Spill-sized reserve: 3.191 for 2.0261, 3.192 for 2.0137, 3.243 for 2.0078, 3.313 for 2.0034. Ungrouped line: 3.182 for 2.0403, 3.182 for 2.0241, 3.215 for 2.0150, 3.265 for 2.0075, 3.337 for 2.0031, 3.400 for 2.0009, 3.478 for 2.0003.3.103.203.3022.052.102.15lines a lookupbits a block4·14·28·18·216·116·2spill reserveungrouped linecapped at twoslack lines (k·e)uncapped walk524,288 keys, two seedsdown and left is better
Fig. 2 Bits a block against lines a lookup on the largest table: slack lines every kk lines with ee deviations of extra room (labelled k⋅ek·e), the uncapped walk at rising room, the walk capped at two lines, the spill-sized reserve and the ungrouped overflow line. Slack 8·1: 3.123 bits for 2.103 lines. Slack 4·2: 3.180 for 2.032. Uncapped at room for the mean: 3.113 for 2.183. Capped at two: 3.180 for 2.027.

A slack line every eighth line, with a deviation of extra room, costs 3.123 bits a block for 2.103 lines a lookup — cheaper in bits than the predicted 3.15 and five times further over two lines than the predicted under 2.02 — and five of the six slack spacings land a hundredth to two hundredths of a bit under the uncapped walk at the same lines. The prediction fails in the direction that matters. The design is cheaper in bits than expected because a slack line’s extra room costs little, and much dearer in lines because that room does not do what it was for: a lookup that reads 2.103 lines is carrying a tenth of a line of walking on average, five times the two hundredths the proposal allowed.

The six slack designs make a curve of their own, and it is a real improvement on one thing. The uncapped walk with its room raised evenly across every line — a quarter of a deviation, then a half — runs from 3.113 bits for 2.183 lines to 3.190 for 2.019. Five of the six slack designs sit under that curve, by between 0.012 and 0.020 bits a block; the sixth, a slack line every fourth line at two deviations, sits on it. Room placed at intervals is cheaper than the same room spread out, as the proposal argued. But the slack curve stops where the others start. The closest it comes to two lines is a slack line every fourth line at two deviations, 3.180 bits for 2.032 lines, and there it lies on top of the cap-two walk and the ungrouped line, 3.180 and 3.182. Under 2.03 lines, where the reserve and the ungrouped line live, no slack spacing measured reaches at all.

A queue that does not drain

The queue does not drain: after a slack line every eighth line with a deviation of extra room, the queue is empty 32% of the time; two deviations raise it to 62%, and a slack line every fourth to 74%The largest table, two seeds: the share of slack lines after which no carried value is still waiting, and the carried values each slack line took, for slack every 4, 8, 16 lines with 1 or 2 deviations of extra room: k 4, e 1: drained 52%, took 3.80; k 4, e 2: drained 74%, took 5.01; k 8, e 1: drained 32%, took 4.90; k 8, e 2: drained 62%, took 7.20; k 16, e 1: drained 28%, took 5.20; k 16, e 2: drained 44%, took 8.15.every 4, 1 deviation52% · took 3.80every 4, 2 deviations74% · took 5.01every 8, 1 deviation32% · took 4.90every 8, 2 deviations62% · took 7.20every 16, 1 deviation28% · took 5.20every 16, 2 deviations44% · took 8.15share of slack lines after which the queue is empty · values each took524,288 keysslack lines
Fig. 3 The share of slack lines after which no carried value is still waiting, and the carried values each slack line took. Every 4 lines: 52% at one deviation, 74% at two. Every 8: 32% and 62%. Every 16: 28% and 44%. Each slack line took between 3.8 and 8.1 waiting values.

The queue does not drain: after a slack line every eighth line with a deviation of extra room, the queue is empty 32% of the time; two deviations raise it to 62%, and a slack line every fourth to 74%. The named risk is the common case. Two times in three, a queue that reaches a slack line is longer than the slack line’s spare slots, and the values left over walk on to the next one. The slack line still takes what it can — about five values at that spacing — so it shortens the queue, but it does not reset it.

The pattern across the plate is the one the risk implied. More room per slack line drains more often. Closer slack lines drain more often, because the queue has had less time to grow. A slack line every sixteenth line at one deviation drains barely more than a quarter of the time, so most of the time it is just a slightly roomier line in a queue that runs past it.

How far the values walk

No walk stops at the next slack line: with one every eighth line and a deviation of extra room a carried value walks 7.34 lines on average and up to 33, past four slack lines; at every spacing measured the longest walk is 2.3 to 5.3 spacings longLines walked by carried values, mean and longest over both seeds, for slack every 4, 8, 16 lines with 1 or 2 deviations of extra room: k 4, e 1: mean 4.40, longest 21; k 4, e 2: mean 2.59, longest 15; k 8, e 1: mean 7.34, longest 33; k 8, e 2: mean 4.54, longest 27; k 16, e 1: mean 9.58, longest 43; k 16, e 2: mean 7.37, longest 37. The uncapped walk at room for the mean: mean 12.80.every 4, 1 deviation4.40 · 21every 4, 2 deviations2.59 · 15every 8, 1 deviation7.34 · 33every 8, 2 deviations4.54 · 27every 16, 1 deviation9.58 · 43every 16, 2 deviations7.37 · 37dark: mean lines walked · light: longest · tick: the spacing524,288 keyslines walked
Fig. 4 Lines walked by carried values, mean and longest, with each spacing marked. Every 8 lines at one deviation: 7.34 on average, 33 at most. Every 4 at two deviations: 2.59 and 15. Every 16 at one deviation: 9.58 and 43. The uncapped walk at room for the mean: 12.80 on average.

No walk stops at the next slack line: with one every eighth line and a deviation of extra room a carried value walks 7.34 lines on average and up to 33, past four slack lines; at every spacing measured the longest walk is between 2.3 and 5.3 spacings long. The proposal’s bound — no carried value walks more than kk lines — would hold if every slack line drained, and the previous plate says most do not. The mean walk falls from the uncapped table’s 12.8 to between 2.6 and 9.6, which is the gain, and the longest walks stretch across several slack lines.

A capped walk had the opposite profile. The cap bounded every walk at two lines by construction and paid for it with an overflow that held half the carried values, three lines a lookup each. Slack lines keep every carried value in the table, and pay with walks that are shorter on average than the uncapped table’s and unbounded in the tail. Neither shape reaches two lines a lookup at room for the mean, for the same reason, which the next plate makes into arithmetic.

Room for one line, wander over eight

Why the queue does not drain: an ordinary line of 165 blocks spills with a standard deviation of about 6.42 values, so over k lines the queue wanders by about 6.42·√k — 12.84, 18.16, 25.68 at k = 4, 8, 16 — while a slack line's spare slots are 7.11 or 12.69, 6.60 or 12.69, 6.60 or 13.21The largest table: ordinary lines hold 165 blocks with room 85 (for the mean), slack lines 157–149 blocks with room 87–89. With a closed share of about 0.512 the spill of one line has a standard deviation of √(C·p·(1 − p)) ≈ 6.42 values; the queue's wander over k lines is about that times √k. k 4: wander 12.84; spare slots on a slack line 1 deviation 7.11, 2 deviations 12.69; k 8: wander 18.16; spare slots on a slack line 1 deviation 6.60, 2 deviations 12.69; k 16: wander 25.68; spare slots on a slack line 1 deviation 6.60, 2 deviations 13.21.every 4 lineswander 12.841 dev: 7.11 spare2 dev: 12.69 spareevery 8 lineswander 18.161 dev: 6.60 spare2 dev: 12.69 spareevery 16 lineswander 25.681 dev: 6.60 spare2 dev: 13.21 sparevalues: the queue's wander between slack lines · a slack line's spare slotsa model from the measured line sizesvalues
Fig. 5 An ordinary line of 165 blocks spills with a standard deviation of about 6.4 values, so the queue wanders over kk lines by about 6.4k6.4\sqrt{k}: 12.8 at four lines, 18.1 at eight, 25.6 at sixteen. A slack line’s spare slots: about 7 at one deviation and 13 at two.

Why the queue does not drain: an ordinary line of 165 blocks spills with a standard deviation of about 6.4 values, so over kk lines the queue wanders by about 6.4k6.4\sqrt{k} — 12.8, 18.1 and 25.6 at kk = 4, 8 and 16 — while a slack line’s spare slots are about seven at one deviation and thirteen at two. The proposal sized the slack line’s extra room in the deviations of one line, and the quantity it had to absorb was the deviation of kk lines’ surplus summed. Sums of independent deviations add in square, so eight lines’ surplus spreads 8\sqrt{8}, nearly three times, as wide as one line’s. A slack line with one line’s deviation of extra room is outwalked by the queue it was built for whenever that queue has had a moderately unlucky eight lines.

The arithmetic also says what spacing and room would drain. To absorb the queue’s wander most of the time, a slack line needs room for a couple of deviations of kk lines’ surplus, about 2×6.4k2 \times 6.4\sqrt{k} slots. At k=4k = 4 that is about 26 spare slots, nearly twice what two deviations of one line give; at k=8k = 8 about 36. And the cost of that room scales the same way: spare slots are bought with blocks, at about four blocks for every three slots. Slack lines trade a bit-cost that grows as k\sqrt{k} against a walk that shrinks as kk — there is no spacing at which both are small. The worst of more lines met the same arithmetic from the other side: a table that sizes every line for its own deviation pays for the worst line of many, and one that pools lines pays for the spread of their sum, which grows more slowly than the sum of their spreads. Here the square root works against the design, because the surplus a slack line must absorb is a sum it was sized as if it were one term.

What the walk costs a lookup

A carried value’s walk is paid only by lookups that need it, and most lookups do not. A lookup of an absent key reads its block’s threshold first, and the threshold is in the block’s own line unless the block closed and its value was carried; at room for the mean between 2.4% and 2.7% of the lookups that land on a closed block find its value carried, and closed blocks are about half of all blocks. So the tenth of a line that the slack design adds over two is the walk of about one lookup in seventy-five, each walking several lines — 7.3 on average at a slack line every eighth line — and a lookup that is not carried reads exactly two.

That is why a few long walks move the mean so little and why the mean is a poor summary of the design. The share carried is set by how much room the ordinary lines have, which is the mean, and no spacing of slack lines changes it; the slack lines change only how far the carried values go. A design that wanted to bring the mean lines down further would have to carry fewer values, which means more room on every line, which is the uncapped walk at higher room — the curve the slack lines sit a hundredth under.

Room at intervals, room everywhere

The plates leave the proposal’s central intuition standing in a reduced form. Room placed where the queue drains is cheaper than room placed everywhere: at equal lines a lookup, every slack design is a hundredth or two cheaper in bits than raising every line’s room. That is the same economy an offset that only has to reach its neighbours found for the overflow’s addressing — spend where the variance is absorbed, not everywhere it might arise.

What the intuition missed is that “where the queue drains” is not a place the table can choose in advance. The queue’s length at any line depends on every line since it last emptied, and a fixed spacing of slack lines meets the queue at whatever length chance has given it. A reserve sized for the spill sized a shared reserve for the expected spill of a group of lines, which is the same arithmetic done in advance: a group’s spill has a mean and a spread, and a reserve sized for them absorbs it whatever its order. The reserve reaches 2.014 lines at 3.192 bits; the slack lines’ best near that, 3.180 for 2.032, reads two hundredths of a line more.

The probe nobody waits for is the reminder about the tail: there, Robin Hood hashing left the mean probe count exactly where it was and moved the worst case from 114 slots to 19. A lookup’s cost here is a mean too, and slack lines leave a longer tail than any design with a hard bound on its walk; a system that cared about its slowest lookups would weigh the 33-line walks differently from this plate.

What the measurement leaves out

One table size. 524,288 keys, about 8,200 blocks a filter and fifty lines. The overflow at the end of the table holds the queue left over after the last line; on a longer table it would be the same few values over more lines.

Fixed spacing. Slack lines every kk lines exactly. A table that placed its slack where its own queue was long at build time — which it knows, since it builds the queue — would be a different design, measured next.

Mean lines. Lines a lookup is the mean over absent lookups; the plates on walks show how wide the tail is but do not price it.

Still open: slack placed where the queue is long

The slack lines fail because their room is fixed in advance and the queue’s length is not. But the table is built, not grown: when its lines are laid out, every line’s surplus is known, and so is the queue’s length at every line. A table could place its slack where the queue is long instead of at fixed intervals. Lay the lines out at room for the mean, compute the queue line by line, and whenever it passes a length QQ, make the next line a slack line with room for the queue’s excess; record, in a few bits a line, which lines are slack, so a lookup can still find its block’s line.

The measurement that follows sweeps QQ from four to thirty-two values on the same filters, charging the slack lines’ blocks and the bits that mark them. The prediction is that at QQ of about eight it reaches 3.15 bits a block for 2.03 lines a lookup — a hundredth of a line under the fixed spacing at the same bits — and bounds every walk at about eight lines, since no queue is allowed to grow past what one slack line drains. It could fail on the marking: a lookup now has to know how many slack lines precede its block, which is a rank over the marks, and a rank that costs a second line read would cost more than every walk it shortens. The question is whether room can be put where the variance lands once the table knows where that is, or whether any design that keeps every value in its own region must pay the square root of the region’s length.

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.

Bit vectorBlocked filterCache lineDesign parameterLinear probingPredictionThresholdVariance