When the algorithm flips a coin

Two blocks and the chances they add

Send each key to the emptier of two 512-bit blocks and the busiest block of a filter at sixteen bits a key holds 38 keys instead of 55. The false-positive rate does not move: 0.100% against 0.095%. At eight bits a key it doubles. A lookup cannot tell which block a key went to, so it has to ask both, and asking twice is two chances to be wrong. The repair that tames a hash table's worst bucket buys a filter nothing that a block twice as wide does not.

Positions confined to one line found that a blocked Bloom filter’s penalty is not about hashing at all. A block of 512 bits holds however many keys happened to choose it, the false-positive rate is convex in that load, and the overloaded blocks cost more than the underloaded ones save. At eight bits a key the penalty was 7%. At sixteen it was 91%, because a tight filter has the most to lose.

That is a balls-in-bins problem, and the standard repair for one has already been measured. The second choice offered each key two buckets and sent it to the emptier, and the maximum load fell from a logarithm to the logarithm of a logarithm. So the natural question is whether a filter can do the same thing: offer each key two blocks, put its bits in the emptier, and flatten the load that the block penalty comes from.

It can do the insertion. The trouble is the lookup.

The load, flattened

The repair works exactly as it does for a hash table.

Keys a block holds at 16 bits a key: one choice spreads them with a standard deviation of 5.72 and a busiest block of 55; two choices, 1.75 and 38, around the same mean of 32How many of 2,048 keys landed in each 512-bit block, over 24 filters of 16 bits a key, with one block choice (pale) and with each key sent to the emptier of two (dark). The mean is 32 keys a block either way. One choice: standard deviation 5.72, busiest block 55. Two choices: 1.75 and 38, and 68% of keys went to their first choice.00.1000.20020304050keys in a 512-bit blockshare of blocksmean 3216 bits a key · 2,048 keys · 24 filterspale: one choice · dark: two
Fig. 1 How many of 2,048 keys landed in each 512-bit block over 24 filters at sixteen bits a key, where the mean is 32. With one block choice the counts have a standard deviation of 5.72 and the busiest block holds 55. With each key sent to the emptier of two blocks the standard deviation is 1.75 and the busiest block holds 38. 68% of keys went to their first choice.

The spread falls by more than a factor of three and the busiest block goes from 72% over the mean to 19% over. That is the balls-in-bins result again. A second choice leaves each block’s load close to the mean, because a key never adds to the busier of its two candidates.

If the false-positive rate depended only on load, this would fix the block penalty. It depends on something else as well.

Two blocks to ask

A lookup for a key has to answer whether the key might be stored. With one block choice there is one block to check. With two, the lookup does not know which block the key was sent to. The emptier block at insertion time depended on every key inserted before it, and the filter does not record which one won. So the lookup must check both blocks and answer yes if either holds all of the key’s positions.

For a stored key that costs nothing in correctness: its bits are in one of the two, so one of them matches. For an absent key it is two tries at a false positive. Each block’s rate is lower because the blocks are flatter. But the lookup succeeds if either block produces a false match, so the rate it reports is close to the sum of the two blocks’ rates.

A second block choice doubles a filter's false positives at 8 bits a key and cuts them by 66% at 24, and a single block twice as wide does as well at every densityFalse-positive rates of 2,048 keys in filters of 8, 12, 16, 20, 24 bits a key, with the optimum number of positions at each, over 24 filters and 16,000, 24,000, 32,000, 40,000, 48,000 absent queries a filter. The whole filter: 2.105%, 0.318%, 0.0440%, 0.0058%, 0.0012%. One block of 512 bits: 2.338%, 0.417%, 0.0954%, 0.0231%, 0.0085%. The emptier of two 512-bit blocks: 4.335%, 0.640%, 0.100%, 0.0159%, 0.0029%. One block of 1,024 bits: 2.260%, 0.351%, 0.0651%, 0.0145%, 0.0026%. At the two tightest densities the rates rest on a few dozen to a few hundred false positives each, so neighbouring lines there are not distinguishable. Both axes are logarithmic.8121620240.000010.00010.0010.01bits a keyfalse-positive ratethe whole filterone block of 512 bitsthe emptier of two 512-bit blocksone block of 1,024 bits2,048 keys · 24 filters a pointdashed: no blocks
Fig. 2 False-positive rates of 2,048 keys at 8 to 24 bits a key, with the optimum number of positions. The whole filter: 2.105%, 0.318%, 0.0440%, 0.0058%, 0.0012%. One block of 512 bits: 2.338%, 0.417%, 0.0954%, 0.0231%, 0.0085%. The emptier of two 512-bit blocks: 4.335%, 0.640%, 0.100%, 0.0159%, 0.0029%. One block of 1,024 bits: 2.260%, 0.351%, 0.0651%, 0.0145%, 0.0026%. At 20 and 24 bits a key the rates rest on a few dozen to a few hundred false positives each.

The result has two regimes and a crossing.

At low density the second choice nearly doubles the rate. At eight bits a key one block measures 2.338% and two choices 4.335%. The block penalty there was only 11% over the whole filter, so flattening the load could save at most that. Asking two blocks costs nearly 100%.

At high density it wins. At 24 bits a key one block measures 0.0085% and two choices 0.0029%. The block penalty there is seven times the whole filter’s rate, because the rate is so steeply convex in load that an overloaded block dominates. Flattening removes most of that, and doubling what is left still comes out ahead.

The two cross at about sixteen bits a key, where one block measures 0.0954% and two choices 0.100%. Below that density the second choice is a loss. Above it the second choice gains, and the gain grows with density.

As a multiple of the whole filter's rate: one 512-bit block goes from 1.11× to 7.0×, two choices from 2.06× to 2.4×False-positive rates of 2,048 keys in filters of 8, 12, 16, 20, 24 bits a key, with the optimum number of positions at each, over 24 filters and 16,000, 24,000, 32,000, 40,000, 48,000 absent queries a filter. The whole filter: 2.105%, 0.318%, 0.0440%, 0.0058%, 0.0012%. One block of 512 bits: 2.338%, 0.417%, 0.0954%, 0.0231%, 0.0085%, or 1.11, 1.31, 2.17, 3.96, 7.00 times the whole filter. The emptier of two 512-bit blocks: 4.335%, 0.640%, 0.100%, 0.0159%, 0.0029%, or 2.06, 2.01, 2.28, 2.73, 2.36 times the whole filter. One block of 1,024 bits: 2.260%, 0.351%, 0.0651%, 0.0145%, 0.0026%, or 1.07, 1.10, 1.48, 2.48, 2.14 times the whole filter. Two choices, one block asked: 2.227%, 0.317%, 0.0478%, 0.0081%, 0.0014%, or 1.06, 1.00, 1.09, 1.39, 1.14 times the whole filter. At the two tightest densities the rates rest on a few dozen to a few hundred false positives each, so neighbouring lines there are not distinguishable. Both axes are logarithmic.812162024125bits a keyrate ÷ whole filter's rateone block of 512 bitsthe emptier of two 512-bit blocksone block of 1,024 bitstwo choices, one block asked2,048 keys · 24 filters a point1 is the whole filter
Fig. 3 The same rates as multiples of the whole filter’s. One 512-bit block: 1.11, 1.31, 2.17, 3.96 and 7.00 times. Two choices of 512-bit blocks: 2.06, 2.01, 2.28, 2.73 and 2.36 times. One 1,024-bit block: 1.07, 1.10, 1.48, 2.48 and 2.14 times. A fourth line asks only the first of the two blocks, which is not a usable filter but separates the two effects: 1.06, 1.00, 1.09, 1.39 and 1.14 times. None of the usable blocked designs reaches the whole filter at any density.

Seen as a multiple of the whole filter, the second choice’s penalty is roughly flat at a little over two. The factor of two is the doubled chance. What remains above two is the load that flattening did not remove. One block’s penalty climbs from 1.11 to 7.0, because it is all load. The design that is best at every density on this plate is neither of them. It is a single block of 1,024 bits.

The fourth line is the one that explains the rest. Asking only the first candidate is not something a real lookup can do, because a stored key sent to its second block would be denied. But on absent keys it measures exactly what one block’s false-positive rate becomes once the load has been flattened. Flattening alone removes almost the whole block penalty: at sixteen bits a key one ordinary block is 2.17 times the whole filter and a flattened block is 1.09 times. At 24 bits the figures are 7.00 and 1.14. The balls-in-bins repair does what it did for the hash table, nearly completely.

The usable two-choice filter then sits at almost exactly twice the flattened block’s rate at every density: 2.06 against 1.06, 2.01 against 1.00, 2.28 against 1.09. The doubling is not a side effect that happens to be large. It is the whole remaining cost, and it follows from a lookup asking two blocks. The repair works and the lookup gives the gain back.

The block that was twice as wide all along

A single 1,024-bit block has twice the room of a 512-bit block, and it spreads the same keys over half as many blocks, so each block’s load is closer to its mean in relative terms. It needs no choice and no second lookup, and it matches or beats two 512-bit choices at every density measured. At twenty bits a key it measures 0.0145% against 0.0159%. At 24 the two are 0.0026% and 0.0029%, a difference that rests on about thirty false positives each and cannot be told apart.

The comparison is fair only if both designs cost the same to read, and they do. A 1,024-bit block spans two 512-bit cache lines. Two 512-bit blocks span two lines as well. The quantity a blocked filter exists to reduce is the lines a lookup reads, so the real comparison is between designs at the same number of lines.

At 20 bits a key, false positives against the lines an absent-key lookup reads: at two lines, one block of 1,024 bits and two choices of 512 land togetherFalse-positive rate against the distinct 512-bit lines an absent-key lookup reads, at 20 bits a key, for one block of 256, 512, 1,024 and 2,048 bits, the emptier of two blocks of 256, 512 and 1,024 bits, and no blocks. one block of 256: 0.0512% at 1.00 lines; one block of 512: 0.0210% at 1.00 lines; one block of 1,024: 0.0145% at 2.00 lines; one block of 2,048: 0.0099% at 3.93 lines; two choices of 256: 0.0181% at 2.00 lines; two choices of 512: 0.0160% at 1.99 lines; two choices of 1,024: 0.0149% at 3.97 lines; no blocks: 0.0074% at 12.92 lines. Both axes are logarithmic.12480.00010.00020.0005lines an absent-key lookup readsfalse-positive rateone block of 256one block of 512two choices of 256two choices of 512two choices of 1,024one block of 1,024one block of 2,048no blocks20 bits a key · 2,048 keyslight: one block · dark: two choices
Fig. 4 At twenty bits a key, each design’s false-positive rate against the distinct 512-bit lines an absent-key lookup reads. One block of 256 bits: 0.0512% at one line. One block of 512: 0.0210% at one. One block of 1,024: 0.0145% at two. One block of 2,048: 0.0099% at 3.93. Two choices of 256 bits: 0.0181% at two. Two choices of 512: 0.0160% at 1.99. Two choices of 1,024: 0.0149% at 3.97. No blocks: 0.0074% at 12.92 lines.

At one line there is only one kind of design, and the 512-bit block is the best of it. At two lines three designs sit together: one block of 1,024 bits at 0.0145%, two choices of 512 at 0.0160%, and two choices of 256 at 0.0181%. The single wide block is lowest. At four lines one block of 2,048 bits, 0.0099%, beats two choices of 1,024, 0.0149%, by a third.

For the same lines read, giving a key one wider block beats giving it a choice of two narrower ones. The choice flattens the load, and so does width. Width does not double the chances of a false match, because a lookup asks one block. The choice is a way of spending a second line, and on this measurement it is a worse way than spending the line on a bigger block.

Reading the second block only after a no

There is an obvious refinement, and positions confined to one line predicted it would help: read the first block, answer yes at once if it matches, and read the second only if the first says no. Answers are identical, since yes from either block means yes. Lines should fall.

Cache lines a lookup reads at 16 bits a key: an absent key reads two under every two-block rule, and only a stored key found in its first block reads oneDistinct 512-bit lines touched per lookup, for absent keys (the upper bar of each pair) and stored keys (the lower). The whole filter: 10.18 for an absent key and 10.18 for a stored one. One block of 512 bits: 1.00 for an absent key and 1.00 for a stored one. The emptier of two 512-bit blocks: 1.99 for an absent key and 1.99 for a stored one. Two blocks, second read only on a no: 1.99 for an absent key and 1.31 for a stored one. One block of 1,024 bits: 2.00 for an absent key and 2.00 for a stored one. Reading the second block only when the first says no gives the same answers; it saves nothing on an absent key, because an absent key almost always gets a no from its first block.lines read a lookupthe whole filter10.18 absent10.18 storedone block of 512 bits1.00 absent1.00 storedthe emptier of two 512-bit blocks1.99 absent1.99 storedtwo blocks, second read only on a no1.99 absent1.31 storedone block of 1,024 bits2.00 absent2.00 stored16 bits a key · 2,048 keysdistinct 512-bit lines
Fig. 5 Distinct 512-bit lines a lookup reads at sixteen bits a key, for an absent key and for a stored one. The whole filter: 10.18 either way. One block of 512 bits: 1.00 either way. Two choices, both read: 1.99 either way. Two choices, the second read only after a no: 1.99 for an absent key and 1.31 for a stored one. One block of 1,024 bits: 2.00 either way.

It helps stored keys and does nothing for absent ones. A stored key that went to its first block, 68% of them at this density, is found after one block and reads one line, so stored-key lookups average 1.31. An absent key is almost never matched by its first block. That match would itself be a false positive, 0.05% of the time at this density, so the lookup almost always goes on to read the second block and averages 1.99 lines. The refinement is free on true negatives only in the sense that it costs nothing extra. It saves nothing either, because a negative lookup is exactly the case where the first block cannot end the search.

Whether that matters depends on which lookups dominate. A filter exists to answer “no” cheaply for keys that are not there. A filter allowed to be wrong measured filters whose job is almost entirely negative lookups, standing in front of a slower store. For that workload the second choice is two lines a lookup, and a single 1,024-bit block is two lines with a lower rate. For a workload of mostly present keys, the refinement brings two choices to 1.31 lines. That is a real saving over a 1,024-bit block’s two lines, at a rate 54% higher than the wide block’s at sixteen bits a key and 10% higher at twenty.

Where the second choice’s hashes come from

Every key here computes three hashes: one per candidate block and one for its positions inside a block. That is the independent version, and it is the one where the second choice does best.

A filter built for speed would not spend three hashes. Choices that are not independent measured what happens when a hash table’s two choices come from one hash split in two or from h1h_1 and h1+h2h_1 + h_2, and found that the correlation between choices gives back part of the load balance. Two hash values and the keys they copy measured the same kind of shortcut inside a filter’s positions and found a penalty that grows as the space the positions share shrinks. A two-choice blocked filter built cheaply would pay both at once: less flattening from correlated choices, and positions made from two values inside a block of 512 bits. Neither is measured here, and both would move the crossing at sixteen bits a key towards higher densities, where the second choice already struggles to beat a wide block.

The block size the design is compared against has its own history. The bucket that fits a line chose a hash table’s bucket size by the cache line, and blocked filters inherited 512 bits for the same reason. This measurement suggests the line is the wrong unit to fix. What a lookup pays is lines read, and a block of two lines read once beats two blocks of one line each read together.

The densities filters are actually built at

The crossing at sixteen bits a key matters because of where it falls. A floor on the bits measured an ordinary Bloom filter at 9.59 bits a key for a 1% false-positive rate. Rates of a few per cent to a tenth of a per cent, eight to fifteen bits a key, are what filters in front of a store are commonly sized for. Each halving of the rate costs about 1.44 more bits a key, and the reads a lower rate saves are only the reads of absent keys that would have been wrongly let through, so the memory for a much tighter filter usually does more good spent elsewhere. That whole common range lies below the crossing.

At twelve bits a key, near the middle of it, the numbers on the first plate are 0.318% for the whole filter, 0.417% for one 512-bit block, 0.351% for one 1,024-bit block and 0.640% for two choices. The second choice costs twice the whole filter’s rate and 1.8 times the wide block’s, at the same two lines a lookup as the wide block. Only a filter tuned to one false positive in several thousand, at twenty bits a key or more, reaches the regime where the choice beats a single narrow block. Even there it only ties with a wide one.

So the practical reading is short. For a blocked filter at an ordinary density, a second block choice is a loss on every absent-key lookup. The block penalty it was meant to remove was small at that density to begin with: 31% at twelve bits a key, against the 100% the doubled chance adds. The penalty only becomes large where the filter is tight, which is where the choice starts to help. By then a wider block has also started to help, and it costs no second lookup.

Why a table and a filter differ here

The second choice pays in a hash table and not in a filter because the two structures ask different questions at lookup.

A hash table stores the key itself. A lookup in either of two buckets compares keys, and a key that is not there matches nothing in either bucket. Checking a second bucket costs a second probe and cannot produce a wrong answer. The load balance is pure gain, paid for in probes.

A filter stores only bits, and any block can match an absent key by accident. Checking a second block is a second chance of an accidental match. The load balance still helps each block, but the lookup gives back part of the gain the moment it asks two blocks. It gives back almost all of it when the filter is loose, and a factor of two when it is tight. The second choice measured the table’s version with no second chance to pay for. The filter’s version has the same insertion and a lookup that pays for two.

What is settled and what is not

Settled by measurement over 24 filters of 2,048 keys: a second block choice cuts the spread of keys a block by more than half at every density, cuts the busiest block at sixteen bits a key from 55 keys to 38, doubles the rate at eight bits a key and cuts it by two thirds at 24. Its rate is about twice the whole filter’s at every density, while a flattened block asked alone measures between 1.00 and 1.39 times the whole filter’s rate, so the doubling is the entire remaining cost. A single block of 1,024 bits reads the same two lines and is as good or better at every density measured. Reading the second block only after a no leaves absent-key lookups at 1.99 lines and brings stored-key lookups to 1.31.

Not settled:

The tight end. At 20 and 24 bits a key the rates come from 30 to 230 false positives a design. The ordering of one 1,024-bit block against two 512-bit choices there is within that noise, and the claim is only that the choice does not beat the wider block.

Other block counts. 2,048 keys at these densities means 32 to 96 blocks. With far more blocks the load spread in relative terms is the same, but the rate of an overloaded block interacts with the number of positions, and nothing here varies the key count.

A filter that records the choice. The lookup reads both blocks because it cannot know which was used. A design that spends a few bits a block to say which keys chose it, or that makes the choice depend only on the key, would be a different structure with a different trade.

Still open: a choice the lookup can recompute

The whole difficulty is that the emptier block depends on insertion history the lookup cannot see. There is a family of designs where it does not. Let each key’s two candidate blocks be ordered by a hash of the key, and let the insertion rule depend only on a threshold carried with each block, not on which block is currently emptier. For example, a block accepts keys whose ordering hash is below its current threshold, and it lowers the threshold when it fills. A lookup reads the thresholds and knows which block the key must be in. That is one line of thresholds plus one block.

The measurement that follows builds that filter, with thresholds in a small array read before the block, and measures its load spread, its rate and its lines against one wide block at the same total bits. The thresholds cost bits a block that could instead have gone into the block. The question is whether a lookup that knows where to look can keep the load balance and lose the doubled chance, and at what density the bits spent on thresholds are repaid.

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.

Balls in binsBlocked filterBloom filterCacheDesign parameterFalse-positive rateHonest limitLoad balancingLocalityMaximum loadSpace time tradeTwo choices