When it does not fit

The small levels' share of a range filter

Prefix filters on a four-level store were proposed a prefix length each, on the reasoning that one length is a compromise between levels whose densities differ 43-fold. Each level does want its own length, 18 to 22 bits, and that recovers a quarter of the gap to a perfect range filter. The gap is mostly not about length. Three fifths of it is false positives. Moving filter bits from the largest level to the small ones — 12, 12, 8 and 3 bits a key instead of 5 each — recovers more than half of it at the same memory, and matches one shared length given twice the memory.

The runs a range query can skip built a log-structured store and gave each of its runs a filter a range query can use. A prefix filter is a Bloom filter over the leading bits of each key. It can be asked whether a run holds any key beginning with a given prefix, and a range that touches only a few prefixes can skip a run whose filter says no to all of them. The store — the arrangement the writes nobody counted found cheap to write and one dial between two structures placed at one end of a range of designs — held 2^20 uniform 32-bit keys in four levels of 16,384, 65,536, 262,144 and 704,512 keys, with five bits of filter for every key a level held. On a workload mixing ranges of a tenth of a key, one key, ten and a hundred in equal shares, the best single prefix length, 20 bits, brought the cost from 5.95 block transfers a query to 4.51. A perfect range filter, skipping exactly the levels with nothing in the range, would reach 3.74.

The page’s closing section blamed the remaining gap on the compromise in “single”. The levels’ densities differ by a factor of 43, and at 20 bits the smallest level was skipped 79% of the time and the largest 26%, because the largest has a key in half of all 20-bit prefixes. It proposed a prefix length for each level. It predicted that a level four times denser would want a prefix two bits longer, and that lengths chosen that way would close most of the gap. It named one way this could fail: the dense levels might have no good length at all.

Each level does have a good length and does want its own. Choosing it closes a quarter of the gap. The rest of this page finds where the other three quarters went, and what recovers most of them, which is not a length.

Each level’s best length

Nothing about a level’s filter affects another level’s read, so the cost of a query is a sum over levels, and each level’s best length can be found alone. The measurement builds every level’s filter at every length from 8 to 32 bits and charges each level its transfers on the same 4,000 ranges.

Each level's best prefix is its own: 18 bits for the level of 16,384 keys, 19 bits for the level of 65,536 keys, 21 bits for the level of 262,144 keys, 22 bits for the level of 704,512 keys; at the shared 20 the smallest level pays 0.746 transfers a query against 0.656 at its ownThe transfers each level costs a query on the mixed workload, against the prefix length of its filter, at five bits a key. Level of 16,384 keys: 12 bits 1.219, 14 bits 0.953, 16 bits 0.691, 17 bits 0.660, 18 bits 0.656, 19 bits 0.684, 20 bits 0.746, 21 bits 0.808, 22 bits 0.863, 23 bits 0.922, 24 bits 0.990, 26 bits 1.106, 28 bits 1.216; best 18 (0.656), with no filter 1.234, oracle 0.467. Level of 65,536 keys: 12 bits 1.381, 14 bits 1.368, 16 bits 1.148, 17 bits 1.027, 18 bits 0.948, 19 bits 0.926, 20 bits 0.937, 21 bits 0.979, 22 bits 1.020, 23 bits 1.078, 24 bits 1.140, 26 bits 1.260, 28 bits 1.361; best 19 (0.926), with no filter 1.381, oracle 0.763. Level of 262,144 keys: 12 bits 1.532, 14 bits 1.532, 16 bits 1.520, 17 bits 1.461, 18 bits 1.351, 19 bits 1.261, 20 bits 1.208, 21 bits 1.193, 22 bits 1.208, 23 bits 1.242, 24 bits 1.301, 26 bits 1.409, 28 bits 1.511; best 21 (1.193), with no filter 1.532, oracle 1.065. Level of 704,512 keys: 12 bits 1.800, 14 bits 1.800, 16 bits 1.800, 17 bits 1.797, 18 bits 1.767, 19 bits 1.693, 20 bits 1.624, 21 bits 1.571, 22 bits 1.543, 23 bits 1.564, 24 bits 1.604, 26 bits 1.686, 28 bits 1.780; best 22 (1.543), with no filter 1.800, oracle 1.443. Open circles mark each level's best length; the dashed line is the shared length 20.0.50011.502152025prefix length, bitstransfers a query on the level16,384 keys65,536 keys262,144 keys704,512 keysfive bits a key on every levelcircles: each level's best · dashed: shared
Fig. 1 Transfers a query that each level costs on the mixed workload, against its filter’s prefix length, at five bits a key. The level of 16,384 keys is cheapest at 18 bits (0.656); 65,536 keys at 19 (0.926); 262,144 at 21 (1.193); 704,512 at 22 (1.543). At the shared 20 bits the four cost 0.746, 0.937, 1.208 and 1.624.

The best lengths are 18, 19, 21 and 22 bits, from the smallest level to the largest. Each curve has the shape the earlier page found for the store as a whole. Too short a prefix is occupied by keys outside the range, so the filter answers yes truthfully and the level is read. Too long a prefix cuts the range into many prefixes, and each is another chance for the filter to answer yes falsely. The bottom of each curve is broad: every level is within 5% of its best across three or four lengths.

With each level at its own length the mixed workload costs 4.319 transfers a query, against 4.514 at the shared 20 bits. That is 0.195 transfers of the 0.776 between the shared length and the perfect filter: a quarter of the gap. The earlier page predicted most of it.

The largest level does have a good length. At 22 bits it recovers 72% of what an oracle saves on it, and the failure the earlier page feared did not happen. Nor is 22 bits a poor compromise for that level, since three of its four range lengths leave little to skip. At ten keys a range, a level of 704,512 keys is never empty, and at a hundred keys no level larger than 16,384 ever is — the regime where the index not worth reading found that nothing short of a scan pays. Much of each level’s cost cannot be touched by any filter, and the gap that remains is in the part that can.

How the best length moves with density

The earlier page’s rule — two more bits for every factor of four in density — can be tested one range length at a time.

The best length rises with the level's density, but by about 1.47 bits for every factor of four rather than the two the earlier page predicted — from 19 to 23 bits across a 43-fold range of density for ranges of a tenth of a keyFor each level and each range length alone, the prefix length that minimises that level's transfers. Ranges of 0.1 keys: 16,384 keys 19 bits (recovers 90% of the oracle's skips), 65,536 keys 21 bits (recovers 87% of the oracle's skips), 262,144 keys 23 bits (recovers 83% of the oracle's skips), 704,512 keys 23 bits (recovers 80% of the oracle's skips). Ranges of 1 key: 16,384 keys 18 bits (recovers 85% of the oracle's skips), 65,536 keys 19 bits (recovers 78% of the oracle's skips), 262,144 keys 20 bits (recovers 70% of the oracle's skips), 704,512 keys 22 bits (recovers 61% of the oracle's skips). Ranges of 10 keys: 16,384 keys 17 bits (recovers 75% of the oracle's skips), 65,536 keys 18 bits (recovers 62% of the oracle's skips), 262,144 keys 19 bits (recovers 52% of the oracle's skips), 704,512 keys nothing to skip. On the mixed workload each level's single best: 18, 19, 21, 22 bits. The dashed line rises two bits for every factor of four, the earlier page's rule. The horizontal axis is logarithmic.100,000keys in the levelbest prefix length, bits1618202224ranges of 0.1 keysranges of 1 keyranges of 10 keysa level with nothing to skip is left offdashed: two bits a factor of four
Fig. 2 The best prefix length for each level, for each range length alone, against the level’s size. Ranges of a tenth of a key: 19, 21, 23 and 23 bits, recovering 90%, 87%, 83% and 80% of the oracle’s skips. One key: 18, 19, 20 and 22. Ten keys: 17, 18 and 19, the largest level having nothing to skip. The dashed line rises two bits for every factor of four.

The direction is right and the rate is lower. For ranges of a tenth of a key the best length rises from 19 bits on the smallest level to 23 on the largest, four bits across a 43-fold range of density. That is about 1.5 bits for every factor of four. For ranges of one key it rises by four bits too, and for ten keys by two across the three levels that ever have anything to skip.

The rule assumed the best prefix holds a fixed number of the level’s keys, so that each doubling of density would need one more bit. It does not, because false positives change the trade. On a denser level a longer prefix cuts the range into more pieces, and each piece is another probe that can come back falsely yes. At five bits a key a Bloom filter answers falsely about 9% of the time, so every extra probe costs. The best length is pulled back from where density alone would put it, and pulled back further on the dense levels, which need more probes.

That suggests the gap is not mainly about lengths at all.

Where the gap goes

The gap between one shared length and the perfect filter can be taken apart by computing a sequence of costs, each a lower bound on the one before.

Of the 0.78 transfers a query between one shared length and the oracle, lengths per level recover 25% and lengths per range another 12%; false positives are 61%, and prefixes too coarse for the range 2%Transfers a query on the mixed workload at five bits a key, each row a lower bound on the one above. No filter: 5.947; One length, 20 bits: 4.514; A length for each level: 4.319; … and each range length: 4.227; … with no false positives: 3.753; The oracle: 3.738. "Each range length" gives every level a different length for each of the four range lengths, which one filter cannot do. "No false positives" skips a level whenever none of its keys lies in a prefix the range touches. The axis starts at 3.3456no filter5.947one length, 20 bits4.514a length for each level4.319… and each range length4.227… with no false positives3.753the oracle3.738transfers a querymixed workload, five bits a keyeach row bounds the one above
Fig. 3 Transfers a query on the mixed workload at five bits a key. No filter: 5.947. One length, 20 bits: 4.514. A length for each level: 4.319. A length for each level and each range length — more than one filter can offer: 4.227. The same, with no false positives: 3.753. The oracle: 3.738.

False positives are 61% of the gap. Length is 37% of it, and only a quarter can be reached by one filter a level. A length for every level and every range length is a bound no real filter reaches, since a level’s filter is built at one length and a query cannot choose another. It costs 4.227. Removing every false positive from that ideal, so a level is skipped whenever none of its keys lies in any prefix the range touches, brings the cost to 3.753 — within 0.015 of the oracle. What remains is coarseness: a range touching a prefix that holds a key of the level outside the range. At the best lengths that almost never happens.

The arithmetic of the multiplication is short. A range holding one key on average spans 4,096 values of key space, so at the largest level’s 22-bit prefixes, each 1,024 values wide, it touches four or five of them. A range of ten keys on the level of 262,144, at 19 bits, touches five or six. A level whose keys are absent from all of them is skipped only if the filter says no to every one. With a false-positive rate of 9% a probe, the chance that six probes all come back truthfully is 0.91 to the sixth power, about 0.57. So the filter throws away more than two fifths of the skips it was entitled to, on exactly the ranges where a skip was available. Shortening the prefix to cut the number of probes is the only remedy a length offers, and it is the remedy each curve’s left-hand side shows failing, because a shorter prefix is occupied by keys outside the range.

So a prefix filter’s real limitation on this store is not the prefix. It is the Bloom filter under the prefix. A range consults several prefixes a level, each consultation can be wrong, and at five bits a key the wrong answers cost more than the choice of length ever could. The lever that addresses false positives is the number of bits a filter has, and the store has a fixed number of those to divide.

What a bit a key buys on each level

The filter each run carries divided a fixed filter memory among levels for lookups of absent keys. It found that the small levels should get more bits a key and the large level fewer. A bit a key on a small level is cheap in total memory, and a false positive on any level costs the same one read. A range filter is the same arrangement with a sharper incentive, since a range asks each level several questions rather than one.

A bit a key buys most where a key is cheap: going from 5 to 12 bits saves the level of 16,384 keys 0.154 transfers a query for 14 KB, and going from 5 to 3 costs the level of 704,512 keys 0.079 while freeing 172 KBFor each level, the transfers a query it costs on the mixed workload at its own best prefix length, against the bits a key its filter holds. Level of 16,384 keys: 1 bits 1.033 (length 16), 2 bits 0.891 (length 16), 3 bits 0.782 (length 17), 4 bits 0.705 (length 17), 5 bits 0.656 (length 18), 6 bits 0.614 (length 18), 8 bits 0.560 (length 18), 10 bits 0.521 (length 19), 12 bits 0.502 (length 19), 16 bits 0.492 (length 19), 20 bits 0.490 (length 19). Level of 65,536 keys: 1 bits 1.237 (length 18), 2 bits 1.125 (length 18), 3 bits 1.036 (length 18), 4 bits 0.970 (length 19), 5 bits 0.926 (length 19), 6 bits 0.887 (length 19), 8 bits 0.840 (length 20), 10 bits 0.810 (length 21), 12 bits 0.792 (length 21), 16 bits 0.777 (length 22), 20 bits 0.775 (length 22). Level of 262,144 keys: 1 bits 1.418 (length 19), 2 bits 1.341 (length 20), 3 bits 1.278 (length 20), 4 bits 1.236 (length 21), 5 bits 1.193 (length 21), 6 bits 1.168 (length 21), 8 bits 1.127 (length 22), 10 bits 1.112 (length 22), 12 bits 1.101 (length 22), 16 bits 1.096 (length 22), 20 bits 1.095 (length 22). Level of 704,512 keys: 1 bits 1.731 (length 20), 2 bits 1.667 (length 21), 3 bits 1.622 (length 22), 4 bits 1.579 (length 22), 5 bits 1.543 (length 22), 6 bits 1.527 (length 22), 8 bits 1.493 (length 23), 10 bits 1.475 (length 24), 12 bits 1.462 (length 24), 16 bits 1.458 (length 24), 20 bits 1.456 (length 24). Open circles mark the allocation that minimises the total at an average of five bits a key: 12 bits at 19 on the 16,384-key level, 12 bits at 21 on the 65,536-key level, 8 bits at 22 on the 262,144-key level, 3 bits at 22 on the 704,512-key level. The horizontal axis is logarithmic.123581220filter bits a key on the leveltransfers a query on the level0.400.801.201.6016,384 keys65,536 keys262,144 keys704,512 keyscircled: the best split of 640 KBdashed: five bits each
Fig. 4 Transfers a query each level costs at its own best length, against its filter’s bits a key. The level of 16,384 keys: 1.033 at one bit, 0.656 at five, 0.502 at twelve, 0.490 at twenty. The level of 704,512: 1.731, 1.543, 1.462, 1.456. The best length rises with the bits: the smallest level wants 16 bits at one bit a key and 19 at ten or more; the largest 20 and 24. Circled: the best division of the store’s memory at an average of five bits a key.

The curves say what a bit a key is worth on each level, and the price of a bit a key is the level’s size. Going from five bits to twelve on the smallest level saves 0.154 transfers a query and costs 14 KB. Going from five to three on the largest costs 0.079 transfers a query and frees 172 KB, enough for that raise twelve times over. With every combination of eleven settings on four levels tried under the same memory, the best division is 12, 12, 8 and 3 bits a key, from the smallest level to the largest. Each level then takes its own best length at its own bits, 19, 21, 22 and 22, and the mixed workload costs 4.043 transfers a query.

The division has the shape the point-lookup page found, and for a reason that can be stated without the curves. A read saved on any level is worth one transfer, whichever level it is. A bit a key costs memory in proportion to the level’s size, and the largest level here is 43 times the smallest. So a bit a key on the smallest level is 43 times cheaper than on the largest, and it buys at least as much, since the small levels are empty for more of the ranges and their filters get asked the questions that can actually be answered no. An allocation that spends the memory where it saves the most transfers per bit therefore strips the largest level down to the few bits that still keep its false positives from dominating, and hands the rest to the small ones until their curves flatten.

The flattening is what stops it. Past twelve bits a key the smallest level’s curve is nearly level — 0.502 transfers at twelve, 0.490 at twenty — because by then its false positives are rare and what remains is ranges long enough that the level really does hold a key. Past that point a bit is better spent on the third level, which is still steep at eight.

The best length depends on the bits, which is why the two decisions are one. A filter with more bits a key can afford more probes, so its best prefix is longer: the smallest level’s best rises from 16 bits at one bit a key to 19 at ten. A length chosen at five bits a key and kept when the bits change is chosen for the wrong filter.

The memory the reallocation is worth

Moving filter bits to the small levels is worth more than choosing their lengths: at five bits a key, one length costs 4.514 transfers a query, a length per level 4.319, and a length and a share of the bits per level 4.043 — about what one length reaches with ten (4.039)Transfers a query on the mixed workload against the filter memory, in average bits for every key in the store. One length, even bits: 2 bits 5.170, 3 bits 4.886, 5 bits 4.514, 8 bits 4.156, 10 bits 4.039. A length each, even bits: 2 bits 5.024, 3 bits 4.719, 5 bits 4.319, 8 bits 4.021, 10 bits 3.918. A length and bits each: 2 bits 4.384, 3 bits 4.207, 5 bits 4.043, 8 bits 3.907, 10 bits 3.860. The oracle: 3.738. Allocations: at 2, 12/10/2/1 bits a key from the smallest level to the largest; at 3, 20/12/5/1 bits a key from the smallest level to the largest; at 5, 12/12/8/3 bits a key from the smallest level to the largest; at 8, 20/16/10/6 bits a key from the smallest level to the largest; at 10, 20/20/12/8 bits a key from the smallest level to the largest. Charged on ranges drawn with another seed, the five-bit allocation costs 4.067 against 4.348 and 4.512. The horizontal axis is logarithmic.235810filter bits a key, averaged over the storetransfers a query3.804.204.605.00one length, even bitsa length each, even bitsa length and bits eachthe oraclemixed workload, 2^20 keys, four levelsdotted: the oracle
Fig. 5 Transfers a query on the mixed workload against the filter memory, in average bits a key over the whole store. One length, even bits: 5.170 at two bits, 4.886 at three, 4.514 at five, 4.156 at eight, 4.039 at ten. A length for each level, even bits: 5.024, 4.719, 4.319, 4.021, 3.918. A length and a share of the bits for each level: 4.384, 4.207, 4.043, 3.907, 3.860. The oracle (dotted): 3.738.

At five bits a key, moving the bits recovers 61% of the gap to the perfect filter, against a quarter for moving only the lengths. With both chosen per level the store reaches 4.043 transfers a query; one shared length needs ten bits a key to reach 4.039. Dividing the memory well is worth doubling it. Divided well, three bits a key (4.207) beats five spread evenly with a length for each level (4.319).

Choosing the lengths per level still matters once the bits are moved: with the bits divided and one length shared by all four levels, the best any length reaches is 4.100, at 22 bits. The larger share of the gain is the bits, and the rest is the lengths following them.

The allocation was chosen on the same 4,000 ranges it is measured on, from 11^4 combinations, and that could flatter it. Charged on 4,000 ranges drawn with a different seed, the same allocation costs 4.067 transfers a query, against 4.348 for lengths alone and 4.512 for one length. The ranking holds and the margins barely move. The allocation is a property of the store’s shape, not of the queries it was fitted to.

What the division does to a lookup for an absent key

The earlier page found the range filter’s price elsewhere: a store whose filters are built on 20-bit prefixes pays for them on every lookup of a key it does not hold. An absent key’s own prefix is present in the largest level half the time, because some other key shares it, and each such level is read. At five bits a key and 20 bits it measured 1.03 levels read for each absent lookup, against 0.37 for an ordinary filter over whole keys.

The division of bits changes that too, and for the better. Measured on 20,000 absent keys, the reallocated filters read 0.485 levels for each lookup: 0.034 on each of the two small levels, 0.084 on the third and 0.333 on the largest. Lengths chosen per level at five bits each read 0.729. The longer prefixes on the large levels are shared by fewer keys, and the small levels’ extra bits almost silence them. The price of serving ranges is now 1.3 times an ordinary point filter’s reads rather than 2.8 times, and all of the excess comes from the largest level, where three bits a key buys little.

That largest level is where the two workloads pull hardest against each other. A store whose traffic is mostly absent-key lookups would give that level more bits, and the division above would move. The division is a function of the mix, and the method that found it finds that one too.

What was measured and what was not

Uniform keys. Every key is drawn uniformly, so every level’s prefixes are occupied evenly. Real keys cluster — a table’s rows under one tenant’s prefix, a time series under one day’s — and clustering changes which prefixes are occupied and how many a range touches. The read a filter has no key for and its successor both stated this, and it remains the largest unknown in this whole line of measurement.

One store shape. Four levels with a size ratio of four. More levels would give the reallocation more to divide and, if the point-lookup result is a guide, more to gain.

Allocations on a grid. Bits a key were chosen from eleven settings a level. A continuous allocation might do slightly better and would not change the ranking.

Transfers, not probes. Every cost is a block transfer, the unit one access, eight kilobytes introduced. A range that touches many prefixes asks the filter many times, and each probe is a few cache misses. At the best lengths a range probes one to a few dozen prefixes a level, and that cost of CPU time is not charged against the transfers saved.

Still open: a filter that answers the range rather than its prefixes

Three fifths of the gap was false positives, and they arise because a range asks a Bloom filter one question for each prefix it touches, and every question can be wrong. The cost of a range grows with the number of questions, which is what pulls every level’s best length short. A filter that answered one question for the whole range would not pay that multiplication.

Such filters exist in principle. A structure over the level’s keys at several prefix lengths at once can answer whether any key lies in an interval by asking at the coarsest length that fits the interval inside one or two prefixes, then descending only where the answer is yes. It is the shape of a B-tree’s own descent, which a tree with nodes the size of a block priced: a question asked first at the coarsest level, and refined only below the branches that could hold an answer.

The measurement that follows builds such a filter on each level. Prefix filters at every length from 16 to 24 bits share one level’s memory, and a range is answered by asking at the longest length that covers it with at most two prefixes, then descending. It compares that with the divided single-length filters here at the same total memory. The prediction is that on short ranges it matches the single best length, since it asks the same one or two questions. On ranges of ten keys it should recover skips the single length loses, because it can ask at a coarser length without cutting the range into pieces. Whether it pays depends on the memory it takes from each length, since nine filters share what one filter had. The earlier page found that two filters sharing a budget lost to one, and the question is whether asking at the right resolution recovers more than the division costs.

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.

Block transferBloom filterDesign parameterFalse-positiveFalse-positive rateLsm treePer levelRange queryRead amplificationSorted runSpace time trade