โ† Wiki Index / Computation & Algorithms / Quotient Filter
๐Ÿ’ป Computation & Algorithms

Quotient Filter

A quotient filter is a cache-friendly, compact approximate-membership hash table introduced by Michael A. Bender and colleagues in 2012 that splits p-bit fingerprints into a q-bit bucket quotient and an r-bit stored remainder using three metadata bits per slot.

Quotient Filter is a space-efficient, cache-local probabilistic data structure in computation and algorithms that answers approximate set-membership queries over a dynamic set S with zero false negatives and a tunable false-positive probability ฮต โ‰ˆ 2โˆ’r. Introduced by Michael A. Bender, Martin Farach-Colton, Rob Johnson, Russell Kraner, Bradley C. Kuszmaul, Dzejla Medjedovic, Pablo Montes, Pradeep Shetty, Richard P. Spillane, and Erez Zadok in 2012โ€”building on Donald Knuth's 1973 exercise on quotienting in The Art of Computer Programming and Cleary's 1984 compact hash tablesโ€”a quotient filter stores p-bit fingerprints inside an open-addressed linear-probing hash table of m = 2q slots by implicitly encoding the high-order q bits in the slot address and storing only the low-order r = p โˆ’ q remainder bits alongside 3 structural metadata bits per slot.

In computational complexity theory and external-memory database indexing, a classical Bloom filter issues k independent random bit probes across the entire array for every lookup or insertion, incurring up to k CPU cache misses or flash-storage page reads, and neither supports deletions nor allows resizing without re-hashing the original keys. Because a quotient filter stores full p-bit fingerprints in sorted canonical order across contiguous linear-probing clusters, every lookup, insertion, and deletion accesses at most one or two contiguous CPU cache lines, supports dynamic deletions without false negatives, and enables linear-time merging and doubling/halving of filter capacity without re-accessing the original keys.

Quotienting Technique, Metadata Bits, and Run/Cluster Geometry

Fingerprint Quotienting into q Quotient Bits and r Remainder Bits

Let S โІ U be a dynamic set of n elements, and let h : U โ†’ {0, 1}p be a uniform hash function that maps each key x โˆˆ U to a p-bit fingerprint f = h(x). In a table of m = 2q buckets, quotienting partitions the p-bit fingerprint f into two bit fields:

  1. The Quotient fq (High-Order q Bits): fq = โŒŠf / 2rโŒ‹ โˆˆ {0, 1, ..., 2q โˆ’ 1}, which designates the element's canonical slot (home bucket) in the array T[0 .. 2q โˆ’ 1].
  2. The Remainder fr (Low-Order r Bits): fr = f mod 2r โˆˆ {0, 1, ..., 2r โˆ’ 1}, where r = p โˆ’ q. Only the r-bit remainder fr is stored inside the slot array.

If a remainder fr always resided at its canonical slot fq, the complete p-bit fingerprint could be reconstructed losslessly via the identity f = (fq โ‰ช r) | fr. When multiple keys share the same quotient fq (a quotient collision) or are displaced by earlier collisions under linear probing, a remainder fr is shifted to a later slot s โ‰ฅ fq (modulo 2q).

Three Metadata Bits: is-occupied, is-continuation, and is-shifted

To reconstruct the original canonical quotient fq of every stored remainder despite linear-probing shifts, a quotient filter maintains two ordering invariants: all remainders sharing the same quotient fq are stored contiguously in ascending remainder order in a run, and the runs themselves appear in ascending order of their canonical quotients fq. Each slot s โˆˆ {0, ..., 2q โˆ’ 1} carries 3 metadata bits alongside its r-bit remainder slot:

  • *is-occupied*[s]: Set to 1 if and only if there exists at least one stored fingerprint in the filter whose canonical quotient equals s (fq = s), regardless of whether that run is currently stored at slot s or has been shifted rightward to a later slot.
  • *is-continuation*[s]: Set to 1 if and only if slot s holds a remainder that belongs to the same run (same canonical quotient fq) as the remainder in the directly preceding slot s โˆ’ 1. Consequently, *is-continuation*[s] = 0 marks the start of a new run (if slot s is non-empty).
  • *is-shifted*[s]: Set to 1 if and only if the remainder stored in slot s does not reside at its canonical slot (s โ‰  fq).

Together, these three bits define the structural hierarchy of runs and clusters:

  • A run is a contiguous block of slots storing all remainders that share a single canonical quotient fq; its first slot has is-continuation = 0 and all subsequent slots in the run have is-continuation = 1.
  • A cluster is a maximal contiguous sequence of occupied slots beginning at an anchor slot s0 that holds an unshifted run-start (*is-occupied*[s0] = 1, *is-continuation*[s0] = 0, *is-shifted*[s0] = 0) and continuing through all directly following slots that have is-shifted = 1.
Metadata Tuple (is-occupied, is-continuation, is-shifted)Structural Interpretation of Slot sQuotient Recovery Role
(0, 0, 0)Empty Slot: No run starts here, and canonical bucket s has no elementsTerminates any preceding cluster
(1, 0, 0)Cluster Anchor (Unshifted Run Start): First element of the run for quotient s, sitting at its home slot sEstablishes synchronized base quotient qcurr = s
(0, 1, 1) or (1, 1, 1)Run Continuation: Subsequent element of the currently active run (shifted from earlier slot)Retains current run quotient qcurr
(0, 0, 1) or (1, 0, 1)Shifted Run Start: First element of a new run that was pushed rightward from an earlier canonical slot fq < sAdvances qcurr to the next slot โ‰ฅ qcurr + 1 where is-occupied = 1
(1, 0, 0) โ†’ (0, 0, 0)Cleared Run: Only valid during transient update before is-occupied is clearedโ€”

Lookup, Insertion, Deletion, and Cache-Local Merging

Cluster Scanning and Membership Query Algorithm

To test whether a key x with fingerprint f = (fq, fr) is present in a quotient filter:

  1. Fast Rejection: Inspect *is-occupied*[fq]. If *is-occupied*[fq] == 0, no element with quotient fq is stored in the filter; return false in a single memory read.
  2. Walk Backward to Cluster Anchor: Starting at s โ† fq, walk leftward (s โ† s โˆ’ 1) while *is-shifted*[s] == 1. During this backward scan, count the number c of slots encountered that have *is-occupied*[s] == 1. The scan stops at the cluster anchor s0 where *is-shifted*[s0] == 0.
  3. Walk Forward to Target Run: Because runs occur in the same order as the is-occupied = 1 bits inside the cluster, advance s rightward from s0 until c run starts (*is-continuation*[s] == 0) have been passed. Slot s now points to the start of the run for quotient fq.
  4. Scan Target Run: Compare fr against the remainders in the run (while is-continuation == 1 in subsequent slots). Because remainders within a run are sorted in ascending order, stop as soon as a remainder โ‰ฅ fr is seen or the run ends.

When the load factor ฮฑ = n / 2q is bounded away from 1 (e.g., ฮฑ โ‰ค 0.75), standard linear-probing analysis by Knuth shows that the expected cluster length is O(1 / (1 โˆ’ ฮฑ)2) = O(1) slots (typically 2 to 6 slots), so the entire backward and forward walk executes inside a single 64-byte CPU cache line.

Dynamic Deletions and Keyless Resizing / Merging

Unlike a Bloom filter, deleting a fingerprint (fq, fr) from a quotient filter simply removes fr from its run, shifts subsequent remainders in the same cluster leftward by one slot (clearing is-shifted on any run that returns to its canonical home slot), and clears *is-occupied*[fq] โ† 0 if the deleted remainder was the sole member of its run.

Furthermore, because every p-bit fingerprint f = (fq โ‰ช r) | fr can be reconstructed sequentially by streaming through the table in O(2q) time, two quotient filters QF1 and QF2 can be merged in a single sequential pass (identical to merging two sorted arrays in merge sort), or doubled in capacity (qโ€ฒ = q + 1, rโ€ฒ = r โˆ’ 1) by moving the most significant bit of each remainder fr into the least significant bit of its quotient fq without storing or re-hashing the original keys.

Filter ArchitectureBits per Slot at ฮต = 2โˆ’rDeletions Supported?Resizable / Mergeable Without Keys?Memory Locality per LookupHigh-Load Behavior (ฮฑ โ†’ 95%)
Standard Bloom Filter1.44 r bits/elementNoNo (fixed m only)k = 0.693 m/n random missesDegrades False-Positive Rate ฮต
Standard Quotient Filter (2012)(r + 3) / ฮฑ bits/elementYes (O(1) amortized)Yes (O(m) sequential stream)1 cache line (contiguous)Linear probing cluster growth at ฮฑ > 80%
Rank-and-Select QF (RSQF, 2017)(r + 2.125) / 0.95 bits/elementYes (O(1))Yes (O(m) sequential stream)1 cache line (SIMD POPCNT + PDEP)Stable up to ฮฑ = 95% load
Cuckoo Filter (Fan et al., 2014)(r + 3) / 0.95 bits/elementYes (O(1))No (partial-key XOR fixed to 2q)2 cache linesStable up to ฮฑ = 95% via cuckoo hashing

Rank-and-Select Quotient Filter (RSQF) and Counting Quotient Filter (CQF)

In 2017, Prashant Pandey, Michael A. Bender, Rob Johnson, and Rob Patro introduced the Rank-and-Select Quotient Filter (RSQF) and Counting Quotient Filter (CQF). Instead of walking backward across long clusters at high load factors (ฮฑ = 90%โ€“95%), RSQF organizes the table into 64-slot blocks aligned to 64-byte cache lines and stores two 64-bit metadata bitmaps per block: occupieds and runends (reducing metadata overhead from 3 bits to 2.125 bits per slot, including block offset counters).

Using x86 hardware bit-manipulation instructions (POPCNT for rank and BMI2 PDEP / TZCNT for select), RSQF locates the precise slot where the run for quotient fq ends in O(1) constant instruction cycles even at 95% occupancy:

RunEnd(fq) = Select1(runends, Rank1(occupieds, fq))

The Counting Quotient Filter (CQF) further encodes variable-length multiplicity counters directly inside each run using empty remainder slots when an item appears multiple times, outperforming the Countโ€“Min sketch on skewed Zipfian distributions in network telemetry and bioinformatics k-mer counting (Squeakr) alongside de Bruijn graph assembly indexes.

Frequently Asked Questions

How can a quotient filter double its capacity without re-hashing the original keys?

Each stored entry in a quotient filter with parameters (q, r) implicitly preserves the full p-bit fingerprint f = (fq โ‰ช r) | fr, where p = q + r. To double the number of buckets from 2q to 2q+1, a single sequential scan extracts each (fq, fr) pair, shifts the top bit of the remainder fr into the bottom bit of the new (q + 1)-bit quotient, and writes the remaining (r โˆ’ 1)-bit remainder into the new table in sorted order.

Why does a Quotient Filter outperform a Bloom filter on SSD and NVMe flash storage?

In a flash-backed storage engine (such as a Bฮต-tree or LSM-tree), merging two SSTable runs with standard Bloom filters requires either keeping all raw keys to rebuild a larger Bloom filter or querying a stack of separate Bloom filters. Because quotient filters can be merged in a single sequential streaming pass over the filters alone, compaction never needs to re-hash keys or perform random I/O seeks.

References

  1. Cleary, J. G. (1984). "Compact hash tables using bidirectional linear probing." IEEE Transactions on Computers, C-33(9), 828โ€“834. https://doi.org/10.1109/TC.1984.1676499
  2. Bender, M. A., Farach-Colton, M., Johnson, R., Kraner, R., Kuszmaul, B. C., Medjedovic, D., Montes, P., Shetty, P., Spillane, R. P., & Zadok, E. (2012). "Don't thrash: How to cache your hash on flash." Proceedings of the VLDB Endowment, 5(11), 1627โ€“1637. https://doi.org/10.14778/2350229.2350275
  3. Pandey, P., Bender, M. A., Johnson, R., & Patro, R. (2017). "A general-purpose counting filter: Making every bit count." Proceedings of the 2017 ACM International Conference on Management of Data (SIGMOD '17), 775โ€“787. https://doi.org/10.1145/3035918.3035963
  4. Pandey, P., Bender, M. A., Johnson, R., & Patro, R. (2018). "Squeakr: An precise and approximate k-mer counting system." Bioinformatics, 34(4), 568โ€“575. https://doi.org/10.1093/bioinformatics/btx636
  5. Conway, A., Farach-Colton, M., & Shilane, P. (2018). "Optimal hashing in external memory." Proceedings of the 45th International Colloquium on Automata, Languages, and Programming (ICALP 2018), LIPIcs Vol. 107, 39:1โ€“39:14. https://doi.org/10.4230/LIPIcs.ICALP.2018.39
๐Ÿงฌ Support Open Science
Explore interactive science games on LowKey Games or browse scientific apparel & accessories in the CRV Science Shop.
๐Ÿ’ก Spot an inaccuracy or have primary literature to expand this article?
Submit a citation or revision note using your shared CRV Science / LowKey Games profile ID.
Suggest Revision