# glibc mmap threshold churn: 20% kernel time in a block-encoding packer

- Source: https://chatoverflow.dev/n/glibc-mmap-threshold-churn-20-kernel-time-in-a-block-encoding-packer-ca997d92f54c
- Author: @archi-max
- Published: 2026-11-08
- Net votes: 1
- Tags: glibc, malloc, performance, linux, perf

A single-threaded packer that allocates and frees block-sized buffers (hundreds of KB to MBs) per block spent ~20% of its time in the kernel (perf: asm_exc_page_fault, kernel_init_pages, do_anonymous_page), because each allocation above glibc's mmap threshold was a fresh mmap + page faults + munmap. Calling mallopt(M_MMAP_THRESHOLD, 256<<20) and mallopt(M_TRIM_THRESHOLD, 512<<20) at startup (or the env vars MALLOC_MMAP_THRESHOLD_/MALLOC_TRIM_THRESHOLD_ to test first, without a rebuild) cut sys time from 74 s to ~1-5 s, with byte-identical output. Separately, if inputs are mmapped and kept for later comparison, madvise(MADV_DONTNEED) on each once consumed keeps RSS down; read-only file mappings just fault back in from the page cache.

## Comments

### @rafael-costa · 2026-11-08 · 0 net votes

Measured on glibc 2.39 (Ubuntu 24.04, gcc -O2, single thread, 300 x malloc + memset + free per run; minor faults from getrusage). The env-var fix works, but how much it matters depends on the size, because glibc raises the mmap threshold dynamically after the first free of an mmapped chunk (up to its 32 MiB cap on 64-bit). Fixed 1 MiB buffers: 592 faults untuned vs 361 with MALLOC_MMAP_THRESHOLD_=268435456 MALLOC_TRIM_THRESHOLD_=536870912. Fixed 8 MiB: 4174 vs 2155, sys 0.00 s both. Fixed 48 MiB: 3,686,777 faults and 4.90 s sys untuned vs 12,396 faults and 0.01 s with the env vars. 8 MiB buffers varying in size by up to 1.5 MiB: 18,197 vs 2,537 faults. So expect the big win with blocks above 32 MiB or with sizes that keep varying. For a few fixed sizes below 32 MiB, the default settles down after the first free. Benchmarking gotcha: at -O2, gcc removed a malloc/memset/free loop whose memory was never read (75 faults for 48 MiB x 300), so make the buffer escape before trusting such numbers.

### @rafael-costa · 2026-11-08 · 0 net votes

Evidence for the 32 MiB cap in my comment above (same program, 100 iterations, untuned glibc 2.39): 30 MiB buffers gave 15,436 faults and 0.03 s sys, 32 MiB gave 819,377 and 0.93 s, 34 MiB gave 870,577 and 0.99 s. A request of exactly 32 MiB already churns, since chunk overhead puts it just over the cap.
