From 9f6a11c3fef12fd8fddc63b11ceced53350bf445 Mon Sep 17 00:00:00 2001 From: Malte Ubl Date: Mon, 12 Oct 2026 11:32:33 -0700 Subject: [PATCH] Correct the reason the prefault helper leaves a huge-page heap alone The previous commit said the helper and the owner cleared the same huge pages twice. Clean per-run page counters don't show that: with and without the helper's heap mode a Sentry type check allocates the same number of pages (1.07M). The cost is the page size: heap ranges the helper populates before the owner advises them, and the partial huge pages at the ends of each extension, stay on 4 KiB pages (Sentry 1816 instead of 1930 huge pages, VS Code 2740 instead of 3051), next to the mmap-lock waits and the smaller top pad. Also splits the THP-mode ternary to fit the line width. --- packages/runtime/src/scr_alloc.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/runtime/src/scr_alloc.c b/packages/runtime/src/scr_alloc.c index 9e86873f6..fa0ba6f47 100644 --- a/packages/runtime/src/scr_alloc.c +++ b/packages/runtime/src/scr_alloc.c @@ -55,7 +55,9 @@ static char scr_sa_thp_mode(void) { size_t n = fread(buf, 1, sizeof buf - 1, f); fclose(f); buf[n] = 0; - return strstr(buf, "[always]") ? 'a' : strstr(buf, "[madvise]") ? 'm' : strstr(buf, "[never]") ? 'n' : 0; + if (strstr(buf, "[always]")) return 'a'; + if (strstr(buf, "[madvise]")) return 'm'; + return strstr(buf, "[never]") ? 'n' : 0; } #endif @@ -144,15 +146,14 @@ static void scr_sa_pf_populate(char *lo, char *hi) { * SCR_SA_PF_TOP_PAD of committed memory beyond what malloc would have; * SCR_SA_PREFAULT_HEAP=0 keeps glibc's defaults and no polling. * - * Where THP is in madvise mode none of this runs: the heap policy below moves - * the heap to huge pages at the same 32 MiB, the owner then takes one fault - * per 2 MiB, and faulting ahead costs more than it saves. An extension is - * populated from its start, exactly where the owner carves, so both threads - * clear the same huge pages and the kernel drops one copy (about a thousand - * huge pages on a large type check); slab requests queue behind - * multi-megabyte heap populates; the owner's brk and madvise wait for the - * mmap lock those populates hold; and the helper's top pad would replace the - * heap policy's larger one. */ + * Where THP is in madvise mode none of this runs. The heap policy below + * moves the heap to huge pages at the same 32 MiB, so the owner takes one + * fault per 2 MiB and faulting ahead saves it little, while the helper's + * populates cost more: ranges it reaches before the owner advises them (and + * the partial huge pages at the ends of each extension) stay on small pages, + * 230-620 MB on large type checks; the owner's brk and madvise + * wait for the mmap lock a multi-megabyte populate holds; and the helper's + * top pad would replace the heap policy's larger one. */ #if defined(__GLIBC__) #include #include