flan/runtime
Joseph Ferano 6b34dc85c8 The lookup was 35ns and is 18ns, and a profile said where every time
Measured rather than guessed, and the guesses were wrong twice: the
per-slot cell division and the block-size divisions were each replaced
first, and neither moved the number. A profile named the four that did.

The hash was FNV one byte at a time, a serial multiply chain per byte
and a quarter of the operation. It is eight bytes at a time now, and a
key that is one machine word — every integer, every enum, every bool,
so very nearly every key — is one load and one mix with no loop at all.
This is where "the hash is compiled concretely per key type" stops
describing the arrangement and starts being the reason it is quick.

Equality on eight bytes was a call into libc's vectorised memcmp, an
eighth of the operation, and copying a value out was a call into
memmove. Both are a load and a compare now for the sizes that are one
word.

The block geometry was recomputed five times over inside one function,
and that function ran twice per lookup — once in the probe and once
again in get. It is one struct built once and handed back. The seed was
a five-multiply avalanche on the critical path of every probe, for
mixing the hasher does again immediately afterwards; one multiply is
all it has to do. And 64/size is a table, which is Odin's Map_Cell_Info
by another route — Odin precomputes it per type because the probe loop
must not divide, and the sizes reach this runtime as plain arguments.

Numbers, on this machine, i64 to i64, against CPython 3.13's dict on
the same workload. Cache-resident, 10k entries, 10M lookups: 21ns
against 132ns, so about six times quicker. That is the answer to "is
this another Python dict", and it is the one the design predicted.

At a million entries it loses, 1.41s to 1.16s, and that is worth
writing down rather than leaving out. Both are waiting on memory there,
and this layout waits longer: keys, values and hashes are three
separate runs, so a lookup that misses everything takes three cache
misses where a compact dict takes two, and the hash run is a full eight
bytes a slot. The layout buys probe locality, which is a win while the
hash run is resident and a loss once nothing is.
2026-09-12 16:26:39 +07:00
..