topic 00: kBigPrime is not prime, and the residue is what matters - #6
Merged
Conversation
reading-rocksdb-db-bench said db_bench scatters hot keys "by multiplying by a large prime" (line 7119 of db_bench_tool.cc). 0x5bd1e995 is 13 x 199 x 457 x 1303 -- MurmurHash2's mixing constant m, which the same tree carries at util/murmurhash.cc:97 and :153. Correct the claim and add the demo that establishes it. kbigprime_demo.py reimplements GetRandomKey (7103-7121 at facebook/rocksdb@7c80a5a) and swaps the constant. Stdlib only, seeded, ~15 s. Measured at --num=1000000 --read_random_exp_range=10, 2M draws, modelling 35 keys per 4 KiB block (16 B key + 100 B value, defaults at 388/337 and include/rocksdb/table.h:400): - The multiply costs 13.9x the block-cache working set for identical traffic: the 6,589 keys carrying 50% of requests sit in 277 blocks unmapped and 3,855 mapped. Ceiling on the ratio is the 35 keys per block. That is FAST'20 7.1's "extremely large number of block reads" as a number. - What makes a multiplier work is M mod FLAGS_num, not its size. The larger, genuinely prime 1000000007 is congruent to 7 and lands hot keys 5 to a block: 1,615 blocks against kBigPrime's 3,855. Coprimality is separately necessary -- 65536 loses 98% of the key space at this --num. - Among covering residues, this one is poor. Its stride walk's smallest gap falls to 16 keys -- under the 35 per block -- from 1,394 hot keys on, so 79% of neighbouring hot keys share a block: 3,765 blocks where a uniform random scatter of the same keys gives ~5,885. It de-localizes worse than chance. The prime nearest 2^32/phi holds out to 9,023 hot keys and beats it. Crossovers are measured by walking the stride, and land two points after the continued-fraction convergent denominator, as they should. - --num=13000000 shares 13 with the constant: every key drawn is a multiple of 13, 12/13 of the database is never read, no warning. Line 7118's 64-bit overflow does not rescue it below ~12 billion keys. check-reading-depth.py passes on the guide and the book builds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
reading-rocksdb-db-bench.md said db_bench scatters hot keys "by multiplying by a large prime", pointing at line 7119 of
tools/db_bench_tool.cc. It is not a prime:0x5bd1e995 = 13 × 199 × 457 × 1303, the mixing constantmof 32-bit MurmurHash2, which the same tree carries atutil/murmurhash.cc:97and:153. This corrects the claim and adds the demo that establishes it.kbigprime_demo.pyreimplementsGetRandomKey(7103-7121 atfacebook/rocksdb@7c80a5a) and swaps the constant on 7117. Stdlib only, seeded, no arguments, ~15 s.Measured
--num=1000000 --read_random_exp_range=10, 2M draws, modelling 35 keys per 4 KiB block (16 B key + 100 B value — defaults atdb_bench_tool.cc:388,:337,include/rocksdb/table.h:400).The multiply costs 13.9× the block-cache working set for identical traffic. The 6,589 keys carrying 50% of requests sit in 277 blocks unmapped and 3,855 mapped. The ceiling on that ratio is the 35 keys per block: unmapped, 35 adjacent hot keys ride in on one block read; mapped, each costs its own. That is FAST'20 §7.1's "extremely large number of block reads" as a number — and the paper's complaint about db_bench is that 7119 does it deliberately.
What makes a multiplier work is
M mod FLAGS_num, not its size. The multiply is a stride walk, so only the residue matters. The larger, genuinely prime 1000000007 is ≡ 7 and lands hot keys 5 to a block: 1,615 blocks against kBigPrime's 3,855. Coprimality is separately necessary — 65536 shares 2^6 with 10^6 and loses 98% of the key space at this--num.Among residues that do cover the key space, this one is poor. Its stride walk's smallest gap falls to 16 keys — under the 35 per block — from 1,394 hot keys on, so 79% of neighbouring hot keys share a block: 3,765 blocks where a uniform random scatter of the same 6,589 keys gives ~5,885. It de-localizes worse than chance. The prime nearest 2^32/φ holds out to 9,023 hot keys and beats it. Crossovers are measured by walking the stride and cross-checked against the continued-fraction convergent denominator, which they land two points after, as they should. The honest general statement: no fixed multiplier keeps hot keys out of shared blocks forever — the golden ratio only maximises how long.
--num=13000000collapses it. 13 divides the constant, so every key drawn is a multiple of 13, 12/13 of the database is never read, and db_bench prints no warning. Line 7118's 64-bit overflow does not rescue it: the product only wraps above ~12 billion keys.Checks
python3 tools/check-reading-depth.pypasses on the guide.mdbook buildrenders the new paragraph.🤖 Generated with Claude Code