turbo-persistence: give key block searches a dense region of their own (#97705)
### What?
Rearranges the bytes inside a key block so that the bytes a lookup's
binary search compares are contiguous, instead of interleaved with key
and value payload the search never reads.
| block kind | before | after |
| --- | --- | --- |
| fixed, `HashThenKey` | `[hash\|key\|val]*n` | `[hash]*n` then
`[key\|val]*n` |
| fixed, `KeyOnly` | `[key\|val]*n` | `[key]*n` then `[val]*n` |
| variable, `HashThenKey` | `[type\|pos]*4B` then `[hash\|key\|val]…` |
`[hash\|type\|pos]*12B` then `[key\|val]…` |
### Why?
The binary search routines are a random access pattern through the
hash/key ranges, we only need to look at value payloads when we find a
plausible match. So by organizing all the 'random access data' in denser
memory range we optimize cache/page fault behavior.
`get` perf impacts are small but real.
| shape | in-block search before → after | as % of a ~1070 ns `get()` |
| --- | --- | --- |
| key=8, value=4 inline | 166 → 146 ns | 1.9% |
| key=4, small value ref | 144 → 119 ns | 2.4% |
The reason the impacts are small is mostly because the 'working sets' of
the benchmarks are not large enough and the page size of macos is quite
large.