jonnify·bcs /bcs/ideas/id-system/the-new-hash-table

B1.3 · dive 1 of 4 — the engine change

One allocation, two reads.

The retired dict resolved one key in four memory reads across three allocations. Valkey 8.1 inverts the layout around the cache line: a 64-byte bucket holds up to seven entries, key and value embed in one object, and a lookup costs two memory reads.

The design is Viktor Söderqvist's open-addressing, cache-line-bucket table. The chapter is content/bcs1.3.md, and the threshold detail below is read from the 8.1.8 tree the chapter built.

Two lookup paths. Select a design to read its exact anatomy — reads, allocations, and what each box is.
the retired dict — 4 reads, 3 allocations bucket array pointers, one per chain dictEntry three pointers, own allocation the key its own allocation the value +2 reads per collision hop the 8.1 bucket — 2 reads, 1 allocation per entry 64-byte bucket 8-byte metadata + up to seven entries serverObject key + value embedded (+ inline expire when set) the dictEntry is gone — one allocation per entry, two memory reads per lookup
readout · Select a design. The retired chained table charged four memory reads to resolve one key; the cache-line design charges two — and the difference lands on every identity the system stores.
§1

The retired dict

content/bcs1.3.md · What

The old table was chained: a bucket array of pointers, each entry a separately allocated dictEntry of three pointers, the key its own allocation — four memory reads to resolve one key, plus two per collision hop, plus a second table during rehash.

Its cost ledger, per key: a bucket-pointer share, a 24-byte dictEntry plus allocation overhead, a separate key allocation, and — with a TTL — a second entry in the expires dict. Every line of that ledger is a pointer to chase, and every pointer is a cache line the CPU may not have.

§2

The cache-line bucket

Söderqvist 2025 · object.c:47

The 8.1 design inverts the layout around the cache line: buckets of 64 bytes holding up to seven entries, an 8-byte metadata section — one child-pointer bit, seven presence bits, and seven one-byte secondary hashes built from the bits not spent addressing the bucket, so mismatched slots are skipped without touching their keys at a 1-in-256 false-positive rate. The dictEntry is gone; key and value embed in the serverObject, one allocation per entry, two memory reads per lookup.

Incremental rehash, the SCAN guarantee, and random sampling — the features that ruled out adopting Swiss tables wholesale — are kept by construction. And one source detail matters to ID design: every key embeds regardless of length, and keys of 128 bytes or more pre-reserve an expire slot (KEY_SIZE_TO_INCLUDE_EXPIRE_THRESHOLD, object.c:47 in the 8.1.8 tree this chapter built).

§3

The headline, reproduced

bench/valkey-id/valkey_id_bench.out

The new table charges a share of 64 bytes across up to seven entries and one object that already contains key, value, and, when set, an inline 8-byte expire. The publication's headline is roughly 20 bytes saved per pair, roughly 30 with a TTL — and the claims reproduce on this project's own keys: 23 to 39 bytes saved, 29 on the TTL row.

content/echo_data/bench/valkey-id/valkey_id_bench.out · three rowsfrozen
fmt keylen redis7 valkey81 saved
brd14 14 88 65 23
uuid16 16 104 65 39
brd14+ttl 14 128 99 29
§4

The staircase

content/bcs1.3.md · What · jemalloc 5.3.0

jemalloc allocates in 16-byte classes in this range, so key length stops mattering within a class and matters sharply at its edges: an identifier's storage cost is a staircase, and the design question is which step each format stands on. The five measured key lengths plot the steps:

The measured staircase. Key length against bytes per key on Valkey — the five measured points of valkey_id_bench.out. Select a length to read its step.
14 65 16 65 19 73 26 81 36 97 key length (bytes) → · vertical axis: bytes per key, Valkey
readout · Select a key length. The dataset is the measured table's keylen and valkey8.1 columns — the steps are jemalloc-5.3.0's 16-byte classes, and they move with the malloc.

The staircase is the durable model: the 9.x line extends the same bucket structure with SIMD lookups and batch prefetching, so class-edge thinking outlives this release.

§5

References

sources · related

Sources

Related