Skip to content

Instantly share code, notes, and snippets.

@randomvariable
Created October 2, 2026 10:14
Show Gist options
  • Select an option

  • Save randomvariable/f58f4e4a0377d8b8009e2a73d0f0607f to your computer and use it in GitHub Desktop.

Select an option

Save randomvariable/f58f4e4a0377d8b8009e2a73d0f0607f to your computer and use it in GitHub Desktop.
DGX Spark: 64 KiB pages, transparent huge pages, and measured allocator behaviour

DGX Spark: 64 KiB pages, THP and what the measurements establish

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.

The short version

  • 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.

Physical-page metadata and page tables are different

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
Loading

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.

The theoretical metadata saving

For a simplified 128 GiB physical-memory example, assume a 64-byte struct page:

$$ \text{descriptor bytes} = \frac{\text{physical RAM bytes}}{\text{base-page bytes}} \times \text{descriptor size} $$

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.

What 2 MiB THP means on a 64 KiB kernel

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]
Loading

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.

Explicit huge pages are a different interface

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.

What the live comparison shows

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

Allocation counters under existing traffic

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.

This custom image requests huge pages through mimalloc

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.

Expected behaviour without mimalloc

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

What glibc actually does

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.

Startup history and fragmentation prevent causal attribution

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.

Memory reserves are not allocated metadata

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 memory does not bypass Linux memory management

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.

What would establish a benefit

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.

  1. Record the kernel, driver, watermarks and allocator settings.
  2. During a quiet window, compare one fixed workload with 2 MiB THP off and on.
  3. Restart the tested processes between configurations so their existing backing does not dominate the result.
  4. 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.

Words

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

Sources and measurement record

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.

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