When the algorithm flips a coin

The day a filter cannot grow

A fingerprint table dies when its quotient reaches its shortest fingerprint, and a filter cannot lengthen a fingerprint because it does not keep its keys. The store it filters does. Rebuilt from that store whenever its own count says its rate has passed 1%, a table grown a thousandfold never exceeds 1.05% and ends holding exactly what a reserve of ten bits holds — 18.9 bits a key — having read the store 1.34 times over. The reserve paid its bits early: 34.8 a key at four times the forecast, where the rebuilt table held 18.4. Which one is cheaper depends on whether the growth arrives.

The bits given to the wrong keys ended with a table that could not store its next key. A fingerprint table keeps the top bits of each key’s fingerprint as a slot number and stores the rest. Every time it doubles in place, one more bit moves into the slot number and one fewer is stored. When the slot number has taken every bit of the shortest fingerprint, that key has nothing left to store, and the table can go no further. Lengthening the fingerprints of later arrivals does not help, because the day of death is set by the keys that arrived first. The table cannot give them longer fingerprints, because a filter does not keep its keys.

That essay’s closing section pointed out that the keys are not lost. They are in the store the filter is a filter for — the database, the log-structured store, the set of URLs already crawled — and the whole point of the filter is to avoid reading that store when the answer is no. On the day the table cannot grow, it can read the keys back, choose a longer fingerprint for the size it has reached, and build a new table. The section proposed pricing that rebuild against the two designs that never need one: a reserve large enough for the whole growth, and a stack of filters that adds a layer instead. It predicted that the rebuild would win on bits by a wide margin, and that the choice would come down to what a key costs to read back.

The measurement confirms the second half and refutes the first. At the rate the reserve holds, the rebuild does not save bits at the end of the growth. It saves them in the middle, and whether that matters depends on a quantity neither design can see.

Four ways through a thousandfold growth

Every table starts from a forecast of 2,000 keys at a target rate of 1%, which gives an 18-bit fingerprint and 4,096 slots. The growth runs to 2,000,000 keys, a thousand times the forecast, and at checkpoints six to a doubling each design is asked about 20,000 keys it does not hold. The table is the model of a filter that grows by moving a bit: a slot count doubled whenever the load passes three quarters, the rate exactly the chance that an absent key’s fingerprint equals a stored one, and bits counted as slots times stored bits, three of them metadata.

Four designs take the growth:

  • Ten bits reserved. One table with a 28-bit fingerprint from the start: 2102^{10} is the thousandfold, so its rate reaches the 1% target only at about 1,300 times the forecast. It never rebuilds.
  • Rebuilt from the store. One bit reserved, and a rebuild whenever the table’s own rate passes 1%. The table can compute that from its count and its fingerprint length, as n/2pn / 2^p, without asking anything. A rebuild reads every key back from the store, charges one read each, and makes a new table with a fingerprint sized for the keys held now at 1%, plus the reserved bit.
  • Rebuilt only when it cannot grow. No reserve and no rate ceiling: the table doubles in place until it dies, and only then reads the store. This is the literal reading of the earlier essay’s question.
  • A stack of Bloom filters. The design a filter past its design size measured: a layer for the forecast at half the target rate, each new layer twice the capacity and half the rate of the one before, added when the top one fills.

The rate each one holds

Over a thousandfold growth, a table rebuilt from the store whenever its own count says its rate has passed 1% never answers yes to more than 1.05% of absent keys; rebuilt only on the day it cannot grow, it reaches 28.7%The share of 20,000 absent keys a filter answers yes to, against keys held as a multiple of a forecast of 2,000, from 1 to 1,000. Ten bits reserved: no absent key answered yes until 8 times the forecast, then 0.005%, 0.68% at worst, 0.68% at a thousand times. Rebuilt from the store: 0.350% at the forecast, 1.05% at worst, 0.68% at a thousand times. Rebuilt only when it cannot grow: 0.690% at the forecast, 28.71% at worst, 11.24% at a thousand times. A stack of Bloom filters: 0.545% at the forecast, 1.06% at worst, 1.06% at a thousand times. Both axes are logarithmic, so a rate of zero is not drawn; the dashed line is the 1% target.11010010³0.00010.0010.010.1keys held, as a multiple of the forecast of 2,000share of absent keys answered yesthe 1% targetten bits reservedrebuilt from the storerebuilt only when it cannotgrowa stack of Bloom filtersforecast 2,000 at 1%, grown to 2,000,00020,000 absent keys a point
Fig. 1 The share of absent keys answered yes, against keys held as a multiple of the forecast, both axes logarithmic. Ten bits reserved: none of the 20,000 absent keys answered yes until the table held eight times the forecast, then 0.005%, rising to 0.68% at a thousand times; a rate of zero cannot be drawn on a logarithmic axis, so its line starts at eight. Rebuilt from the store: between 0.35% and 1.05% throughout, 0.68% at the end. Rebuilt only when it cannot grow: 0.69% at the forecast, 28.7% at its worst, 11.2% at the end. The stack: 0.55% rising to 1.06%. The dashed line is the 1% target.

Rebuilding on the day of death is the wrong day. A table with nothing reserved doubles its rate every time it doubles its keys, because its fingerprint length is fixed and the keys competing to match it double. By the time it cannot grow it has doubled six times and is answering yes to 28.7% of absent keys. That is one lookup in three and a half going to the store for nothing, which is exactly what the filter exists to prevent. After the rebuild it starts again at a longer fingerprint and climbs again, to 11.2% by the end of the growth.

A rebuild triggered by the rate instead keeps the table between 0.35% and 1.05% for the whole thousandfold. It never needs to wait for the table to die, because the table’s own count says when the rate has passed its target. That count is the one number a fingerprint table always knows about itself. The day a filter cannot grow is several doublings after the day it stopped being the filter its caller asked for, and the second day is the one to act on.

The reserve and the stack both stay under the target without rebuilding, and in opposite ways. The reserve starts far better than it needs to be: not one of 20,000 absent keys is answered yes until the table holds eight times its forecast, because its 28-bit fingerprint was chosen for two million keys and holds only two thousand. It then worsens steadily to 0.68%. The stack starts near its layer’s half-target and creeps up as layers are added, towards the sum of its layers’ rates, 1.06% at the end.

Where the bits go

The reserve and the rebuild end the growth at the same 18.9 bits a key; the reserve paid 34.8 at four times the forecast where the rebuild held 18.4Bits held per key — slots times stored bits, three of them metadata — against keys held as a multiple of the forecast. Ten bits reserved: 38.9 at the forecast, 34.8 at four times, 26.6 at sixty-four, 18.9 at a thousand. Rebuilt from the store: 20.5 at the forecast, 18.4 at four times, 18.4 at sixty-four, 18.9 at a thousand. A stack of Bloom filters: 11.0 at the forecast, 22.9 at four times, 36.4 at sixty-four, 23.1 at a thousand. Both axes are logarithmic.11010010³10keys held, as a multiple of the forecast of 2,000bits held per keyten bits reservedrebuilt from the storea stack of Bloom filtersforecast 2,000 at 1%slots × stored bits ÷ keys
Fig. 2 Bits held per key against keys held as a multiple of the forecast. Ten bits reserved: 38.9 at the forecast, 34.8 at four times, 26.6 at sixty-four, 18.9 at a thousand. Rebuilt from the store: 20.5 at the forecast, 18.4 at four times and at sixty-four, 18.9 at a thousand. The stack: 11.0 at the forecast, 22.9 at four times, 36.4 at sixty-four, 23.1 at a thousand. Both axes are logarithmic.

At the end of the growth, the reserve and the rebuilt table hold exactly the same bits: 18.9 a key. That is not a coincidence of the measurement. At two million keys the rebuilt table has a fingerprint sized for two million keys at 1% plus one reserved bit, which is 28 bits. The reserve’s fingerprint is the forecast’s 18 plus 10 reserved, also 28. Both have the same slot count, the same stored bits and the same rate, 0.68%. They are the same table, reached by two routes.

The routes cost different amounts along the way. The reserve carries its ten extra bits from the first key, and every doubling moves one of them into the slot number, so its bits per key fall from 38.9 at the forecast to 18.9 at the end — a reserve spent down as the growth arrives, as the earlier essay put it. The rebuilt table carries one extra bit at any time and holds between 18.4 and 20.5 bits a key throughout. At four times the forecast the reserve holds 34.8 bits a key to the rebuilt table’s 18.4, nearly twice; at sixty-four times, 26.6 to 18.4.

So the prediction’s “wide margin” exists, but in the middle of the growth rather than at the end. And it is a margin in bits per key, which is not the same as a margin in bits. The reserve’s excess is largest when the table is smallest. At four times the forecast, 8,000 keys, its excess is about 130,000 bits, 16 KB. At the end, where the table is a thousand times larger, its excess is zero. A reserve is expensive in exactly the period when it is cheap in absolute terms.

The stack’s bits follow a different pattern. Each new layer is allocated at its full capacity, so bits per key jump when a layer is added and fall as it fills: 36.4 a key at sixty-four times, when a large new layer is nearly empty, and 23.1 at the end, when the tenth layer is mostly full. On average across the growth it holds more than either table. It also pays in another currency the tables do not: a lookup must consult every layer, ten of them by the end.

What the store is read for

Holding the rate under 1% costs 1.34 reads of the store per key held by the end; letting it reach 2% with a bit reserved costs 0.45Cumulative keys read back from the store during rebuilds, divided by the keys held, against keys held as a multiple of the forecast. Rebuilt from the store: 9 rebuilds, 1.34 reads a key at the end, at most 1.85 along the way. One bit reserved, rebuilt at 2%: 4 rebuilds, 0.45 reads a key at the end, at most 1.23 along the way. Two bits reserved, rebuilt at 2%: 3 rebuilds, 0.77 reads a key at the end, at most 1.06 along the way. The rule rebuilt from the store rebuilds when its rate passes 1% with one bit reserved. Both axes are logarithmic; points before the first rebuild are drawn at the floor.11010010³0.0010.010.11keys held, as a multiple of the forecast of 2,000keys read back from the store, per key heldrebuilt from the storeone bit reserved, rebuilt at2%two bits reserved, rebuiltat 2%forecast 2,000, target 1%cumulative reads ÷ keys held
Fig. 3 Cumulative keys read back from the store, divided by the keys held, against keys held as a multiple of the forecast. Rebuilt under 1% with one bit reserved: 9 rebuilds, at most 1.85 reads a key along the way and 1.34 at the end. One bit reserved and rebuilt at 2%: 4 rebuilds, 0.45 reads a key at the end. Two bits reserved and rebuilt at 2%: 3 rebuilds, 0.77 at the end. Both axes are logarithmic; points before the first rebuild are drawn at the floor.

The rebuilt table’s price is reads, and the plate shows how many. Holding the rate under 1% with one bit reserved means a rebuild at every doubling. The rebuilt table starts each doubling at half the target and reaches the target after one doubling. Each rebuild reads every key held, so the total is the geometric series n+n/2+n/4+…n + n/2 + n/4 + \dots, a little under two reads a key. It sits at 1.85 through most of the growth and 1.34 at the end, where the last rebuild happened well before the last key.

Loosening the ceiling to 2% halves the rebuilds and more. With one bit reserved and a 2% ceiling the table rebuilds every other doubling and ends at 0.45 reads a key, with a worst rate of 1.96% and 16.8 bits a key. The reserve and the ceiling are two dials on one trade: each reserved bit halves the starting rate and doubles the interval between rebuilds; each doubling of the ceiling does the same to the interval and doubles the worst rate. The formula everybody sizes filters with is the static version of this arithmetic; the growing version adds a third quantity, the reads, and the three trade against each other as 2−r2^{-r}.

Two reads a key over the life of a filter is a large or a small number depending entirely on what a read is. A filter in front of a disk-resident store rebuilds by scanning the store, sequentially, at the store’s full bandwidth. A filter in front of a remote service rebuilds by asking the service for every key it holds, which may not be possible at all. The filter’s own design sees neither case, and the caller always knows which one it is.

If the growth stops early

The reserve and the rebuild reach the same table at the end of a thousandfold growth, so the question between them is what happens if the growth does not arrive. A reserve is sized for the worst case a designer can imagine. The growth that actually happens is usually smaller.

Stopped at 2.8 times the forecast, the reserve holds 11.6 bits a key more than the rebuilt table, which has read back 0.93 keys a key — 12.5 bits held for every key read; run the full thousandfold and the reserve's excess is goneFor a growth that stops at a stated multiple of the forecast, the bits a key the ten-bit reserve holds beyond the rebuilt table, divided by the keys the rebuilt table has read back per key held by then. At 2.8 times: 11.6 extra bits a key, 0.93 reads a key, 12.5 bits per read; At 5.7 times: 10.1 extra bits a key, 1.39 reads a key, 7.3 bits per read; At 11.3 times: 8.7 extra bits a key, 1.62 reads a key, 5.4 bits per read; At 22.6 times: 7.2 extra bits a key, 1.74 reads a key, 4.2 bits per read; At 45.3 times: 5.8 extra bits a key, 1.80 reads a key, 3.2 bits per read; At 90.5 times: 4.3 extra bits a key, 1.82 reads a key, 2.4 bits per read; At 181.0 times: 2.9 extra bits a key, 1.84 reads a key, 1.6 bits per read; At 362.0 times: 1.4 extra bits a key, 1.85 reads a key, 0.8 bits per read; At 724.1 times: 0.0 extra bits a key, 1.85 reads a key, 0.0 bits per read; At 1000.0 times: 0.0 extra bits a key, 1.34 reads a key, 0.0 bits per read. Both axes are logarithmic; values at or below zero are drawn at the floor.1010010³0.010.1110the multiple of the forecast at which the growth stopsthe reserve's extra bits a key, per key the rebuild read backten bits reserved, against rebuilt under 1%bits a key per read a key
Fig. 4 For a growth that stops at a stated multiple of the forecast: the bits a key the ten-bit reserve holds beyond the rebuilt table, divided by the keys the rebuilt table has read back per key held by then. At 2.8 times: 11.6 extra bits a key against 0.93 reads, 12.5 bits held per key read. At 22.6 times: 7.2 bits against 1.74 reads, 4.2. At 181 times: 2.9 against 1.84, 1.6. At 724 times and beyond: none. Both axes are logarithmic.

The plate is the exchange rate the earlier essay asked for, in the only units the two designs share. If the growth stops at three times the forecast, the reserve is holding 11.6 bits a key more than a rebuilt table would, and the rebuilt table has read the store 0.93 times over to get there: 12.5 bits held, for every key read. If it stops at twenty-two times, the exchange is 4.2 bits per key read; at a hundred and eighty times, 1.6. Past about seven hundred times the reserve has spent its excess entirely and the rebuilt table holds the same bits for its reads.

So the choice is between two numbers the designer knows and the filter does not: how sure the growth is, and what a read costs. A caller that expects the full thousandfold and cannot read its store cheaply should reserve. A caller whose forecast is a guess — which is the reason a filter past its design size is a page at all — pays for the reserve up front in proportion to how badly the guess might miss. At the start that is twice the bits of a table that can rebuild. A caller that can read its store sequentially at little cost should rebuild, and should do it on the rate rather than on the day the table dies.

The ledger at two million keys

At two million keys: the reserve and the rebuild both hold 18.9 bits a key, the rebuild having read the store 1.34 times over; the stack holds 23.1 and asks 10 filters on every lookupFor each design at the end of a thousandfold growth: bits held a key, keys read back from the store per key held, the rate at the end, the worst rate at any checkpoint, and filters consulted per lookup. Ten bits reserved: bits a key 18.9, reads a key 0.00, rate at end 0.68%, worst rate 0.68%, probes 1. Rebuilt from the store: bits a key 18.9, reads a key 1.34, rate at end 0.68%, worst rate 1.05%, probes 1. One bit reserved, rebuilt at 2%: bits a key 16.8, reads a key 0.45, rate at end 1.44%, worst rate 1.96%, probes 1. Rebuilt only when it cannot grow: bits a key 10.5, reads a key 0.05, rate at end 11.24%, worst rate 28.71%, probes 1. A stack of Bloom filters: bits a key 23.1, reads a key 0.00, rate at end 1.06%, worst rate 1.06%, probes 10.bits a keyreads a keyrate at endworst rateprobesten bits reserved18.90.000.68%0.68%1rebuilt from the store18.91.340.68%1.05%1one bit reserved, rebuilt at 2%16.80.451.44%1.96%1rebuilt only when it cannot grow10.50.0511.24%28.71%1a stack of Bloom filters23.10.001.06%1.06%102,000,000 keys, forecast 2,000 at 1%probes: filters asked per lookup
Fig. 5 At the end of the growth, for each design: bits a key, reads a key, rate at the end, worst rate, filters consulted per lookup. Ten bits reserved: 18.9, 0.00, 0.68%, 0.68%, 1. Rebuilt under 1%: 18.9, 1.34, 0.68%, 1.05%, 1. One bit reserved, rebuilt at 2%: 16.8, 0.45, 1.44%, 1.96%, 1. Rebuilt only when it cannot grow: 10.5, 0.05, 11.24%, 28.71%, 1. The stack: 23.1, 0.00, 1.06%, 1.06%, 10.

The ledger puts every cost side by side, and no design dominates. The reserve spends no reads and has the best rate at the end, and past about 1,300 times the forecast its rate would pass the target with nothing it could do about it — it can keep doubling in place for sixteen doublings, but only with a fingerprint chosen at the forecast. The rebuilt table spends reads to hold a steady rate and would survive any growth. Its rate is at the target at worst rather than well under it, which is what it was asked to do. Rebuilding only at death is cheapest in bits and reads by far, because it is barely a filter: at its worst it answers yes to more than a quarter of the keys it does not hold, and every one of those sends a lookup to the store for nothing. The stack needs no forecast, no reads and no rebuild, and pays for that in bits and in ten lookups where every table pays one.

That last column is the one the earlier essays kept returning to. A stack is the design that keeps its layers independent, so tightening one layer never touches another. It pays with a lookup per layer. Positions confined to one line priced a single Bloom lookup in cache lines, and a stack multiplies that price by its depth. A rebuilt table keeps a single lookup and a single addressing scheme, and pays with reads of a store instead.

Three arrangements, each paying in its own currency

The earlier essay closed the design space in one sentence: a filter can be independent and pay per layer, shared and bounded by its shortest fingerprint, or rebuilt and pay for the rebuild. The ledger puts a price on the third and shows that the three currencies do not convert into each other without a number from outside.

A layer is paid in lookups, every lookup, forever. A reserve is paid in bits, most heavily at the start, and the bits are wasted exactly to the extent the forecast of growth was pessimistic. A rebuild is paid in reads of a store, once per doubling or less, and it is possible only when the store exists and can be read. None of the three can be declared best by the filter, because the exchange rates between lookups, bits and reads are properties of the system the filter sits in.

That is the same shape as the choice a filter allowed to be wrong began with. A filter’s rate is a number its caller chooses and the filter merely delivers. Here the caller chooses a second number, how the delivery is paid for, and the filter can only report what each option would cost. The most useful thing a growing filter can do is make that report possible: count its keys, know its own rate, and say how many reads a rebuild would take before it takes them. A filter that dies without warning — the failure an insertion that can fail describes from the caller’s side — has withheld the one piece of information that would have let its caller choose.

What was measured and what was not

The table’s rate is exact and its layout is not simulated. A key is a false positive exactly when its fingerprint equals a stored one, so the rates are measured, not modelled. The runs and shifts a real quotient filter performs on insertion cost time, not bits, and are not simulated; the rebuild’s time is charged only as reads of the store.

A read is a key, not a block. A rebuild from a store that is itself sorted and blocked reads blocks, and a key’s share of a block can be a small fraction of a read. That would make every rebuild cheaper than the reads plate says, by as much as the keys a block holds. The plate counts keys because that is the unit the filter’s caller can count without knowing the store.

The store is assumed to hold exactly the filter’s keys. A filter over a store that has had keys deleted would rebuild over the store’s current keys and lose the deleted ones, which a filter usually cannot delete by itself — a real advantage of rebuilding that is not measured here. The evidence a filter cannot remove is the page on what a filter does with a key it should forget.

One growth, one seed. The keys arrive at an even pace through a thousandfold growth. A growth that stops, restarts or reverses would move the rebuilds, and the rate plate’s 1.05% is a worst case over checkpoints six to a doubling, not over every key.

The stack is the earlier essay’s. Its first layer holds the forecast at half the target and each layer doubles. A stack whose layers grew faster would need fewer lookups and more bits, and the design space of stacks was not swept.

Still open: a rebuild that does not stop the table

Every rebuild here happens all at once. The table reads the whole store, builds the new table, and switches. While it reads, the old table goes on answering — at a rate that has just passed its target — and the store is being read at full speed alongside whatever else it serves. For a store of two million keys that is a pause. For a store of two billion it is an operation that takes hours, during which the old table keeps growing.

The measurement that follows spreads the rebuild across insertions: after each new key, it also reads cc old keys from the store into a new table under construction, and switches when the new table is complete. It asks how large cc must be for the new table to finish before the old one’s rate passes its ceiling, what the rate is during the overlap, and what the two tables together cost in bits while both exist. The prediction from the arithmetic here is that cc of about two suffices when the ceiling is twice the starting rate, because a doubling’s worth of new keys must arrive while the old ones are read back. The cost is holding both tables for a whole doubling, which would bring the rebuilt design’s bits a key up to roughly the reserve’s in the middle of the growth. If that holds, the incremental rebuild removes the pause and gives back most of the saving that made rebuilding worth measuring.

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.

AmortisationApproximate membershipBloom filterDesign parameterFalse-positive rateFingerprintForecastHonest limitQuotient filterResizingSpace time tradeThreshold