This note separates three changes that are often discussed together: kernel base-page size, huge-page policy and memory reserves. They affect different parts of Linux memory management. They must be measured separately.
Measurements below come from four GB10 hosts running two live, two-host vLLM groups on 2 October 2026. Host labels are anonymous. No isolated inference benchmark was completed.
- A 64 KiB kernel uses fewer physical-page descriptors than a 4 KiB kernel for the same RAM.
- Physical-page descriptors are not process page-table entries. Reserving virtual memory does not create more physical RAM descriptors.
- With these 64 KiB kernels, the PMD transparent huge page is 512 MiB, not 2 MiB.
- A 2 MiB multi-size transparent huge page remains PTE-mapped. It contains 32 base pages.
- These custom-image vLLM processes have huge-page-advised mimalloc arenas. Application advice and THP are not competing approaches.
- The enabled pair successfully allocates 2 MiB folios. Their per-process residency and performance benefit remain unmeasured.
- The pairs also differ in startup history and
min_free_kbytes. This is not a clean THP-only experiment.
flowchart TD
RAM[Physical RAM] -->|contains| FRAME[Physical page frames]
FRAME -->|have| META[Kernel page descriptors]
PROC[Process virtual addresses] -->|resolve through| TABLE[Process page tables]
TABLE -->|map to| FRAME
CPU[CPU translation cache] -->|caches translations from| TABLE
The diagram separates physical-memory accounting from each process's address translation. The CPU translation cache is the translation lookaside buffer, or TLB. It caches translations, not page-table objects themselves.
Analogy: physical-page descriptors inventory the storage, while page tables record which process addresses refer to that storage.
Linux associates a struct page with each physical base-page frame represented by its memory model.
With sparse vmemmap, Linux populates the backing metadata for present memory sections during initialization.
It can also populate metadata when memory is added later. Physical memory model
The checked-out kernel source makes this explicit in sparse_init_nid().
It calls __populate_section_memmap() for present sections and accounts for PAGES_PER_SECTION * sizeof(struct page).
This is physical-memory metadata, not metadata allocated once per process mapping. Kernel initialization source
Page tables instead describe mappings in a process's address space. Linux creates their required levels as mappings develop. Threads sharing an address space share that memory context. There is not a separate user page table for every thread. Kernel threads do not each require an independent user address space. Page tables
A process can reserve 200 GiB of virtual addresses without owning 200 GiB of physical RAM.
A large model mapping therefore does not create another physical struct page inventory for its virtual size.
Physical backing, file cache, swap and page tables have their own accounting.
For a simplified 128 GiB physical-memory example, assume a 64-byte struct page:
| Base-page size | Physical base-page count | Descriptor storage |
|---|---|---|
| 4 KiB | 33,554,432 | 2 GiB |
| 64 KiB | 2,097,152 | 128 MiB |
The calculated difference is 1.875 GiB. This is theoretical, not a measured saving on these hosts. The actual descriptor size depends on the kernel build. Memory holes, section alignment, reservations and metadata optimizations can change the physical backing cost. The example uses GiB, not a vendor's decimal GB capacity figure.
Ordinary THP does not change the kernel's base-page size or reproduce this descriptor-count reduction. HugeTLB has a separate vmemmap optimization, which must not be confused with ordinary THP. HugeTLB
A TLB is hardware, not a multi-gigabyte RAM allocation. Larger pages can improve translation coverage. They do not recover RAM by shrinking the TLB.
flowchart TD
APP[vLLM mimalloc arenas] -->|request| ADVICE[MADV_HUGEPAGE]
ADVICE -->|permits| POLICY[Kernel THP policy]
POLICY -->|can select| SMALL[2 MiB mTHP]
POLICY -->|can select| LARGE[512 MiB PMD THP]
SMALL -->|uses| PTE[Base-page PTE mappings]
LARGE -->|uses| PMD[PMD block mapping]
The diagram shows application advice feeding the kernel's transparent huge-page allocation policy. It does not promise either allocation size.
A folio groups physical pages that Linux manages together. A 2 MiB folio contains 32 of these 64 KiB base pages. A 512 MiB folio contains 8,192 base pages. Both require contiguous physical backing when allocated as those folio sizes.
Multi-size THP can reduce fault frequency without requiring a full PMD-sized allocation. It remains PTE-mapped. Architecture-specific translation coalescing can provide additional benefits. It does not automatically remove the leaf PTE table as a PMD block mapping can. THP
The relevant controls are independent:
| Control | Meaning |
|---|---|
enabled=always |
Allows THP beyond explicitly advised regions, subject to mapping eligibility |
enabled=madvise |
Restricts ordinary THP allocation to huge-page-advised regions |
Per-size inherit |
Uses the top-level enabled policy |
defrag |
Controls efforts to obtain suitable contiguous backing |
MADV_HUGEPAGE |
Marks a mapping as suitable for huge pages |
MADV_COLLAPSE |
Requests collapse and can bypass sysfs never settings |
THP is therefore not inherently a policy that forces every small allocation into a huge page.
always can waste backing in sparsely touched regions. madvise provides selective use.
Selection still does not guarantee a benefit. Clearing larger allocations and performing compaction can add latency.
HugeTLB uses a dedicated pool and explicit mappings. Reserved HugeTLB memory cannot serve ordinary allocations and cannot be swapped. Available HugeTLB sizes depend on the architecture and kernel. Do not copy x86 assumptions about 2 MiB or 1 GiB HugeTLB into a 64 KiB Arm kernel. HugeTLB
An application calling MADV_HUGEPAGE is already cooperating with THP.
Teaching an allocator to give selective advice is not the same as replacing THP with HugeTLB.
CUDA registration and allocation compatibility would need verification before substituting a HugeTLB pool.
Both pairs run kernel 7.0.0-1019-nvidia-64k and NVIDIA driver 615.71.09.
Both use 64 KiB base pages and the same vLLM container image and serving configuration.
The leader address differs as expected.
The comparison did not test a 4 KiB kernel or an older driver.
| Setting | Pair A | Pair C |
|---|---|---|
| Global THP | madvise |
madvise |
| 2 MiB THP | never |
madvise |
| 512 MiB THP | inherit, therefore madvise |
inherit, therefore madvise |
| Defragmentation | madvise |
madvise |
| Kubelet | v1.37.0 | v1.37.1 |
min_free_kbytes |
1,048,576 KiB | 6,469,696 KiB |
A matched 60-second window produced these host-wide counter deltas:
| Counter | A1 / A2 | C1 / C2 |
|---|---|---|
| 2 MiB fault allocations | 0 / 0 | 36 / 40 |
| 2 MiB fault fallbacks | 0 / 0 | 0 / 0 |
| 2 MiB splits or failed splits | 0 / 0 | 0 / 0 |
| 512 MiB fault allocations | 0 / 0 | 0 / 0 |
| 512 MiB fault fallbacks | 12 / 12 | 12 / 12 |
| Compaction stalls | 40 / 38 | 40 / 40 |
| Failed compactions | 40 / 38 | 40 / 40 |
These counters prove successful host-level 2 MiB allocation in pair C. They do not identify the allocating process or compare equivalent request workloads. The host-wide compaction counters do not identify which allocation triggered compaction.
A subsequent snapshot found 95 and 45 anonymous 2 MiB folios on C1 and C2. That represents 190 MiB and 90 MiB of allocated folio capacity, not exact process RSS. The partially mapped counters were 58 and 15. Linux treats these counters as indications, not exact unmapped-byte accounting.
Mimalloc is a feature of the custom image used here, not an assumed property of upstream vLLM images.
The API, engine and tensor-parallel worker processes contain [anon:mimalloc] mappings with VmFlags: hg.
Every advised mapping inspected reported THPeligible: 1.
This confirms huge-page advice in vLLM processes, including pair A.
It does not identify the calling stack or prove advice on CUDA weights and KV-cache allocations.
| Host | Process | RSS in advised mappings, rounded MiB | PMD THP in those mappings, MiB |
|---|---|---|---|
| A1 | API / serve | 914 | 0 |
| A1 | Engine core | 11,053 | 0 |
| A1 | TP worker | 3,238 | 0 |
| A2 | Headless serve | 504 | 0 |
| A2 | TP worker | 3,222 | 0 |
| C1 | API / serve | 1,480 | 0 |
| C1 | Engine core | 5,130 | 1,536 |
| C1 | TP worker | 3,808 | 2,048 |
| C2 | Headless serve | 512 | 512 |
| C2 | TP worker | 3,737 | 512 |
These rows are live snapshots, not synchronized equivalent workloads.
AnonHugePages records PMD-mapped THP. Here, that means 512 MiB pages.
Zero in this field does not mean zero 2 MiB mTHP. Process mapping fields
Per-process 2 MiB residency was not measured.
A privileged pagemap and kpageflags inspection could attribute physical folios to mappings.
That requires careful compound-folio identification and handling of concurrent mapping changes.
Hypothesis, not a measured result: removing mimalloc would change host allocation behaviour and potentially huge-page coverage. It would not change the kernel's base-page size or THP policy.
The replacement allocator matters. Removing mimalloc does not identify which allocator every library will use. Python, native libraries and CUDA can use different allocation paths within one process.
Under the measured madvise policy, an anonymous mapping needs huge-page advice to receive ordinary THP.
If the replacement allocator does not provide that advice, its allocations would normally retain 64 KiB backing.
Merely enabling the 2 MiB order would not make those unadvised allocations eligible.
Another library could still advise its own mappings. Explicit collapse is a separate path. THP
The inspected glibc source calls madvise_thp() from its ordinary mmap and heap-growth paths.
That helper returns without advice when mp_.thp_pagesize is zero.
The default initializer leaves that field zero. Thus default glibc does not issue this huge-page advice.
Large requests can use direct mmap when the dynamic allocation threshold and mapping-count limit permit it.
The threshold does not guarantee that every large request receives a fresh mapping. glibc allocation source
GLIBC_TUNABLES=glibc.malloc.hugetlb=1 enables glibc's THP advice when the top-level kernel policy is madvise.
However, it obtains its minimum advice size from hpage_pmd_size.
On these hosts that is 512 MiB, not 2 MiB.
Its helper skips smaller allocation regions, even when the kernel enables 2 MiB mTHP.
Advice on sufficiently large regions can still permit smaller enabled THP sizes. glibc size probe
This is an important limitation of treating the glibc tunable as a direct mimalloc replacement. It does not request huge pages for every allocation of at least 2 MiB. The inspected code also supports explicit HugeTLB modes through other tunable values. Those are a different allocation path.
The running container exposes a mimalloc LD_PRELOAD, but no GLIBC_TUNABLES, MIMALLOC_* or THP_MEM_ALLOC_ENABLE variables.
That supports default glibc behaviour as a conditional replacement scenario.
It does not prove compiled allocator defaults or identify the exact code path that set each observed hg flag.
No replacement-allocator run was performed.
| Allocation path | Expected effect of removing mimalloc |
|---|---|
| Host allocations currently served by mimalloc | Arena layout, advice, retention and fragmentation could change |
| Host mappings explicitly advised by another component | Can remain eligible under madvise |
| Python objects served by its own allocator | No automatic one-to-one replacement with kernel pages |
| CUDA weights and KV-cache allocations | No page-size change established by the mimalloc evidence |
| File-backed model mappings | No automatic conversion into anonymous advised arenas |
The likely direct difference is fewer allocator-provided hg mappings if the replacement does not request huge pages.
That could reduce THP backing in host heaps. The amount and performance effect are unknown.
Smaller allocations could reduce unused backing or compaction work, but could increase fault and translation overhead.
Neither lower RSS nor slower inference follows automatically.
A global switch to always could make more unadvised mappings eligible.
That changes a second variable and expands the risk of unused backing. It is not an equivalent allocator comparison.
The theoretical physical-descriptor saving from a 64 KiB kernel still applies without mimalloc. It comes from the kernel's base-page size, not the userspace allocator.
The smallest useful check is the same image and workload with only mimalloc disabled, during a quiet window. Record the effective replacement allocator, advised mappings, page backing and host RSS after a process restart. Keep THP policy and watermarks fixed. A restart does not reset host fragmentation. No such comparison was performed here.
At the fragmentation snapshot, pair A hosts had approximately 92.5 hours of uptime. Pair C hosts had approximately 8.5 hours. Pod startup occurred approximately 86 hours after boot in pair A and 1.6 hours after boot in pair C. These offsets use host boot timestamps and pod start timestamps. They are not exact arena-fault times.
| Host | Normal-zone unusable index for 2 MiB | Largest free Normal-zone block |
|---|---|---|
| A1 | 0.543 | 16 MiB |
| A2 | 0.143 | 256 MiB |
| C1 | 0.311 | 256 MiB |
| C2 | 0.382 | 256 MiB |
Lower unusable-index values mean more free memory is usable for allocations of that size. Pair A is not uniformly more fragmented: A2 has the lowest 2 MiB unusable index.
All hosts had zero free 512 MiB buddy blocks and an order-13 unusable index of 1.000.
That describes current free memory, not conditions when vLLM faulted its arenas.
Pair C's existing 512 MiB backing cannot be attributed to its 2 MiB policy from these snapshots.
Different startup histories and reserve settings remain confounders.
min_free_kbytes establishes free-memory watermarks used by the allocator.
It is not an application RSS allocation or framebuffer reservation. VM watermarks
The checked-out kernel's set_recommended_min_free_kbytes() raises the recommendation for THP.
Its formula uses pageblock size, populated zones and migration types.
It caps that recommendation at nr_free_buffer_pages() / 20.
That is a 5% cap on the formula's input, not a universal rule that THP reserves 5% of installed RAM.
An explicitly higher user setting can also exceed that cap. THP reserve source
The measured values correspond to 1.000 GiB in pair A and 6.170 GiB in pair C. Their difference is 5.170 GiB of watermark setting, not measured recovered memory. The source explains a possible mechanism. It does not establish why each live host has its current setting.
Disabling desktop services, changing framebuffer reservations, changing base-page size and adjusting watermarks are separate changes. A total memory difference cannot assign savings to each change without controlled accounting. No framebuffer recovery experiment was performed here.
Unified physical memory describes shared hardware storage. CUDA Unified Memory describes an access and management model. Neither means every CUDA allocation follows the same path as a CPU allocator arena.
NVIDIA documents GPU access to system allocations on supported systems.
Its unified-memory documentation includes malloc, managed allocations and file-backed mappings.
Linux-backed memory therefore does not disappear from the kernel's accounting merely because a GPU accesses it. CUDA memory
The allocator API, mapping type, registration, residency and driver determine the relevant path. Evidence from mimalloc arenas cannot establish the page size of CUDA device allocations. Likewise, advice suitable for anonymous CPU memory does not prove huge-page treatment of file-backed model weights.
CPU translation benefits are plausible for host-side model work and offload. GPU translation benefits, prefill gains and decode gains require separate measurements. No host-offload experiment was completed in this comparison.
A defensible experiment needs matched hosts, software, reserve settings and startup conditions. It must separate the 4 KiB versus 64 KiB kernel question from the 2 MiB THP question.
- Record the kernel, driver, watermarks and allocator settings.
- During a quiet window, compare one fixed workload with 2 MiB THP off and on.
- Restart the tested processes between configurations so their existing backing does not dominate the result.
- Record actual page backing, counter deltas, startup time, prefill and decode.
A separate controlled kernel comparison is needed for the 4 KiB versus 64 KiB question.
This comparison confirms selective advice and successful 2 MiB allocation. It does not quantify a 64 KiB speedup, a 2 MiB speedup or additional usable KV-cache capacity. Previously reported improvements need their original baseline and measurement record before becoming numerical claims.
| Term | Meaning |
|---|---|
| Base page | The kernel's fundamental physical-memory unit |
struct page |
Kernel metadata describing a physical base-page frame |
| Page table | A hierarchy describing virtual-to-physical address mappings |
| PTE | A leaf page-table entry |
| PMD | A page-table level that can directly map a large block |
| TLB | A hardware cache of address translations |
| Folio | Physical pages managed together as one allocation |
| THP | Transparent huge pages selected by Linux |
| mTHP | THP smaller than the PMD mapping size |
| HugeTLB | Explicit huge pages using a dedicated pool |
| RSS | Memory currently resident in a process's mappings |
| Compaction | Moving eligible pages to create contiguous free blocks |
| Watermark | An allocator threshold that preserves free-memory headroom |
| Confounder | Another difference that prevents assigning an observed effect to one change |
Primary sources were read on 2 October 2026.
The online documentation describes current upstream Linux, not necessarily every detail of the deployed vendor kernel.
Source references pin the inspected local kernel revision 93f51579e7df248780214094418f205253383cc5.
The inspected source is not claimed to be the deployed kernel's exact source tree.
Live evidence came from getconf PAGESIZE, THP sysfs, /proc/uptime, /proc/stat, /proc/buddyinfo,
/sys/kernel/debug/extfrag/unusable_index, /proc/vmstat, /proc/<pid>/smaps, NVIDIA's version file,
and Kubernetes pod and node status.
The measurements are dated observations, not a reproducible benchmark result or a published raw-data archive.