mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
Every class slice used to opt out of transparent huge pages, so each 4 KiB the bump pointers crossed cost a page fault. Those faults (fault entry, memcg charge, LRU insertion and the page clear) were 6-14% of tsc-ts samples in the kernel. Backing the whole reservation with huge pages removes most of them, but makes the 2 MiB page around every bump pointer resident at once. A worker executable has up to 32 class slices per script thread, so tRPC at 8 checkers grew from 558 to 955 MB. A slice now starts on small pages. Once it has carved 8 MiB, the rest of its range switches to huge pages (MADV_HUGEPAGE). Only slices that already hold 8 MiB pay for a partial last huge page. The reservation is huge-page aligned, so the switch point and every slice start on a 2 MiB boundary. The slow path raises a slice's bump limit in two steps (to the switch point, then to the slice end), so the inline fast paths are unchanged. The policy is Linux-only; other targets keep one limit per slice, as before. The allocator checks now build with _GNU_SOURCE on Linux like the runtime packs, so the madvise advice is compiled in and checked (alignment, contiguous carving across the switch, VmFlags nh/hg). Measured with tsc-ts best-v1 (parallel-check + 6247795 e24057a 64d9af6 1c42c55), built with this runtime vs origin/main, on two Linux Vercel Sandboxes (8 vCPU; 5-7 interleaved runs, medians). Columns: wall, max RSS, minor faults; "all" is MADV_HUGEPAGE on the whole reservation: single-threaded wall RSS faults all: wall / RSS Excalidraw -6.1% +0.8% 331k -> 112k -10.0% / +3.5% Playwright -1.8% +1.0% 371k -> 111k -8.4% / +3.2% TypeORM -7.2% +0.9% 259k -> 75k -11.8% / +4.1% tRPC -5.0% +2.0% 101k -> 47k -9.0% / +11.3% tsc-ts self-check -2.9% +2.6% 158k -> 64k -3.1% / +8.4% --checkers 8 Excalidraw -2.9% +2.6% 1075k -> 508k -6.3% / +10.6% Playwright -10.0% +2.6% 739k -> 381k -10.3% / +12.2% TypeORM -5.9% +2.2% 372k -> 232k -10.1% / +27.7% tRPC -2.5% +2.5% 159k -> 125k -4.3% / +71.2% tsc-ts self-check -4.0% +3.9% 213k -> 172k -12.1% / +46.3% Switching at 2 or 4 MiB was up to about 1.5 points faster single-threaded but cost up to +11% RSS at 8 checkers. 16 MiB gave back most of the gain. Prefaulting small pages with MADV_POPULATE_WRITE did not reduce sys time. Collapsing the small-page prefix with MADV_COLLAPSE at the switch did not help either. The runtime benchmark suite (--layouts=4 --runs=16) shows a geomean change of -1.2%: inventory-report -9.9%, validate-errors -5.3%, log-summary -2.9%, and regex-logs +2.1%. regex-logs peaks at 6.8 MB and never reaches the switch point; with one binary the policy measures -0.9% there, so its delta comes from code layout.