next.js
e2e7393c - turbo-persistence: prefetch multi-page SST blocks with MADV_WILLNEED (#100283)

Commit
10 days ago
turbo-persistence: prefetch multi-page SST blocks with MADV_WILLNEED (#100283) ### What? turbo-persistence SST lookup mappings keep `MADV_RANDOM`, and also prefetch whole multi-page blocks: - **Block hint:** before a block's first full read in an SST open (CRC check and decompression), the block's page-aligned range gets `MADV_WILLNEED`. The hint is issued before any byte of the block is read, header included, and only when the block spans more than one page (using the system page size). - Medium value blocks bypass the block cache, so they get the hint on every read. - Already-verified blocks and single-page blocks get no syscall. - **Block-offset table:** gets `MADV_WILLNEED` at open, instead of `MADV_SEQUENTIAL`. - **Unknown page size:** if `sysconf(_SC_PAGESIZE)` fails, block hints can't be computed, so `MADV_RANDOM` isn't applied either and the mapping keeps default advice (read-around still prefetches). The offset-table hint is applied in both cases. - **Unchanged:** compaction iteration (`MADV_SEQUENTIAL`), meta/blob files, File access mode, non-Unix platforms and the on-disk format. ### Why? See #100250. - With `MADV_RANDOM` alone, a cold multi-page block is faulted in one page at a time. - Dropping the advice (the first version of this PR) lets kernel read-around fix that, but it also reads neighboring blocks that are often never used. The two variants were compared on a real workload: - **Setup:** warm `next build`s of `bench/nested-deps-app-router-many-pages`, with a new `NEXT_DEPLOYMENT_ID` each build. The cache snapshot contained fresh, compacted, hot_bottom and cold_bottom SSTs. - **Runs:** cold guest page cache, Linux, 4 KiB pages, 5 rotated trials per variant. | Variant | Median build | Major faults | Cache resident after | | --- | --- | --- | --- | | canary (`Random`) | 40.0 s | ~101k | 527 MiB | | default advice | 39.6 s | ~14.2k | 545 MiB | | `Random` + `WillNeed` everywhere (this PR) | 38.2 s | ~13.4k | 527 MiB | | `WillNeed` for cold_bottom only, default for the rest | 38.8 s | ~13.8k | 533 MiB | - Build times are within run-to-run noise. Cache reads are a small part of this build, so this is not a claimed wall-time win. - The targeted hints match default advice's fault reduction, with less data pulled into the page cache. - Reading unneeded pages costs more on slow or compressed backing storage such as squashfs. That is another reason to avoid read-around, though it wasn't benchmarked here. - Not measured: memory pressure, macOS and 16 KiB pages. With 16 KiB pages more blocks fit in one page and get no hint. ### How? - `get_raw_block_slice` takes a `will_need` flag and issues the hint after locating the block, before reading its header. - Lookups pass `true` when the block's verified-CRC bit is unset; the index block and medium values always pass `true`; iteration passes `false`. - Adds a `libc` dependency on Unix for `sysconf(_SC_PAGESIZE)`, queried once and cached. If it can't be determined, both the block hints and `MADV_RANDOM` are skipped. - Tests: - Linux `/proc/self/smaps` check that lookup mappings carry `rr` and not `sr`; - a test-only hint log verifying the offset-table hint, multi-page-only first-read hints, no hints on re-reads, and per-read medium-value hints; - pure page-boundary tests for 4 KiB and 16 KiB. - README documents the advice policy. <!-- NEXT_JS_LLM --> <!-- fleet 5f39f2b2-0882-4566-ae42-cf64574c5747 --> --------- Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com> Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>
Author
Parents
Loading