Skip to content

perf: opt-in jemalloc transparent huge pages for the store (closes #512)#582

Open
ELares wants to merge 1 commit into
mainfrom
perf/512-huge-pages
Open

perf: opt-in jemalloc transparent huge pages for the store (closes #512)#582
ELares wants to merge 1 commit into
mainfrom
perf/512-huge-pages

Conversation

@ELares

@ELares ELares commented Jul 8, 2026

Copy link
Copy Markdown
Owner

What

Dragonfly GET-gap perf (#507). At 1M+ random keys the per-slot hashbrown store tables (#570) + value blobs thrash the TLB (4KB pages). This enables jemalloc transparent huge pages (2MB) to cut TLB misses in exactly that regime.

Mechanism: jemalloc malloc_conf thp:always + metadata_thp:auto (not per-allocation madvise -- hashbrown gives no handle to madvise a specific table, and both tables + blobs flow through the global allocator, so jemalloc's own THP option is the cheapest lever that covers everything process-wide). Applied in main.rs's exported _rjem_malloc_conf static, gated on target_os = "linux" + the hugepages feature.

Default OFF (opt-in), config-knobbed per the tunability tenet:

  • build-time hugepages Cargo feature (Linux-only), and
  • a runtime _RJEM_MALLOC_CONF=thp:always|thp:never env override (works on any shipped binary, no rebuild).
    Default-off because thp:always can raise RSS (2MB granularity) + add khugepaged latency spikes on some kernels -- which would inflate the very RSS the maxmemory ceiling accounts against. Documented tradeoff in CONFIG.md + ADR-0006. (jemalloc reads malloc_conf once at init, so THP cannot be a live CONFIG SET key -- it is restart-required via the feature/env.)

Verified huge pages are actually used: an integration test asserts jemalloc's opt.thp mallctl returns "always"; a manual (ignored) smaps probe showed AnonHugePages 274432 kB backing a 256MiB allocation. Linux-gated (macOS/other get a THP-free string, no jemalloc warning); the musl static build is unaffected (compile-time malloc_conf).

Tests

jemalloc_honors_thp_always_from_malloc_conf (feature on); default build unchanged; clippy -D warnings (default + hugepages) + io_uring build + invariant lints + fmt clean; no dashes.

Opt-in lever; its actual throughput/RSS effect is A/B measured on Linux (feature on vs off) stacked with the other cheap levers -- reported straight, not claimed.

Closes #512. Part of #507.

🤖 Generated with Claude Code

At 1M+ random keys the per-shard hashbrown store tables (#570) and the value
blobs are touched with poor locality, so each GET tends to miss the TLB (4 KiB
pages mean many TLB misses on the -r 1M random-key hot path). Back the
allocator's memory with 2 MiB transparent huge pages (THP) to cut the TLB-miss
rate for the same coverage (estimated 3 to 10 percent, to be A/B measured on
Linux stacked with the other cheap levers).

Mechanism: jemalloc THP via malloc_conf, not per-allocation madvise. Both the
store tables and the value blobs flow through the global allocator (jemalloc,
ADR-0006), and hashbrown allocates its tables through that same allocator with
no handle to madvise a specific region, so the cheapest effective lever is
jemalloc's own thp:always boot option (it madvises MADV_HUGEPAGE on its
extents), plus metadata_thp:auto for the arena metadata. This covers the tables
and blobs process-wide with zero per-allocation code. Verified on Linux: a
256 MiB allocation under thp:always shows 274432 kB AnonHugePages in
/proc/self/smaps, and the opt.thp mallctl reports "always".

Linux gating: the thp: token is emitted into the compile-time malloc_conf only
on target_os = "linux" (jemalloc compiles THP support on Linux and nowhere
else). On macOS and other targets the string stays THP-free, so there is no
jemalloc "Invalid conf pair" warning and the feature is inert; the macOS dev
build still builds, boots, and serves. MSVC is unaffected (same cfg-gate as the
allocator), and the musl static build is unaffected because malloc_conf is a
compile-time static.

Tunability ([[tunability-principle]]): THP is a behavior tradeoff, so it is a
knob with a safe default, not a baked-in choice. It is DEFAULT-OFF (opt-in)
because thp:always can raise RSS (2 MiB allocation granularity) and add
khugepaged latency spikes on some kernels; keeping it off preserves an honest
RSS figure for the maxmemory ceiling (ADR-0006). Two knobs, both default off:
the build-time `hugepages` Cargo feature (flips the compiled default, Linux
only), and the runtime override _RJEM_MALLOC_CONF=thp:always (or thp:never),
which works on any shipped binary with no rebuild and overrides only the thp
key. Documented in docs/design/CONFIG.md ("Transparent huge pages") and
ADR-0006.

Determinism (ADR-0003): THP is a memory-layout hint, not part of the engine
decision path, and adds no clock or entropy, so it does not cross the
determinism boundary.

Tests: a main.rs unit test asserts the malloc_conf string carries thp:always
iff Linux + the hugepages feature (locks in the gating and the opt-in default);
a Linux integration test asserts jemalloc honors thp:always (opt.thp mallctl);
and an ignored smaps probe (tests/hugepages.rs) verifies real AnonHugePages
backing on a THP-capable host.

Closes #512.

Signed-off-by: Zeke <ezequiel.lares@outlook.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

perf-gate (A5)

Same-runner ratchet of HEAD against the merge-base (both rebuilt and measured in this job).
PASS = within the noise band, WARN = a real move inside budget (does not fail), FAIL = past budget in the bad direction.

metric base head delta% band budget verdict
qps_median (peak) 85508.85 84414.06 -1.28% +/-5.00% drop <= 15% PASS
bytes_per_key int 45.10 45.10 0.00% det rise <= 5% PASS
bytes_per_key embstr 60.13 60.13 0.00% det rise <= 5% PASS
bytes_per_key raw 332.43 332.43 0.00% det rise <= 5% PASS

Overall: PASS

  • qps: noisy on shared CI, so the band comes from the base reps spread (floored at 5%); a drop is only a regression past the 15% budget.
  • bytes_per_key: deterministic (allocator-true memmodel), so a tight 5% rise budget; any rise beyond it FAILs.
  • Open-loop tails / criterion micro-benches are reported-not-failed (tail noise is high) and are not part of this ratchet.
  • An intentional perf trade is landed by raising the relevant budget in this PR with a documented reason (CI never auto-commits a baseline).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: huge pages (MADV_HUGEPAGE) for the store table + arenas

1 participant