Compare commits

...
Author SHA1 Message Date
rdbtCVSandClaude Opus 5.5 b8ef219633 Allocator: no transparent huge pages on Linux
mimalloc asks for huge pages on its arenas (MADV_HUGEPAGE), and a 2 MiB
huge page stays resident whole while one 64 KiB mimalloc page in it is in
use. configure_allocator now sets mimalloc's allow_thp to 0 and
PR_SET_THP_DISABLE for the process.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-12 16:26:38 +00:00
+35
View File
@@ -555,16 +555,33 @@ pub fn heap_tag(name: &'static str) -> Option<heap_census::Tag> {
/// `mi_option_purge_delay` in mimalloc.h (index 15 in v2 and v3); the bindings
/// don't name it.
const PURGE_DELAY: libmimalloc_sys::mi_option_t = 15;
/// `mi_option_allow_thp` in mimalloc.h (index 43 in v3); unnamed in the bindings too.
#[cfg(target_os = "linux")]
const ALLOW_THP: libmimalloc_sys::mi_option_t = 43;
/// Set the allocator up, first thing in `main`: free pages go back to the OS
/// at once instead of a second later. With mimalloc's delay the game held a
/// quarter more memory than with the system heap; without it a few percent,
/// and generation keeps most of its speed-up.
///
/// On Linux it also turns transparent huge pages off for the process.
/// mimalloc asks for them on its arenas (`MADV_HUGEPAGE`, so `madvise` systems
/// give them too), and a 2 MiB huge page stays resident whole while one
/// 64 KiB mimalloc page in it is used: in a world at render distance 12 the
/// game held 200 MiB more that way, at the same load time and frame rate.
pub fn configure_allocator() {
// SAFETY: sets one integer option, which mimalloc reads at each purge.
unsafe {
libmimalloc_sys::mi_option_set(PURGE_DELAY, 0);
}
#[cfg(target_os = "linux")]
// SAFETY: an integer option mimalloc reads at each new mapping, and a prctl
// that only sets this process's flag; the kernel reads it on each page fault,
// so the arenas mapped before `main` take small pages from now on too.
unsafe {
libmimalloc_sys::mi_option_set(ALLOW_THP, 0);
libc::prctl(libc::PR_SET_THP_DISABLE, 1, 0, 0, 0);
}
}
/// The bytes the allocator has committed now and at its peak (mimalloc's
@@ -897,6 +914,24 @@ mod tests {
super::configure_allocator();
assert_eq!(get(), 0);
}
/// Configured, the process takes no transparent huge pages, whatever the
/// system's setting.
#[cfg(target_os = "linux")]
#[test]
fn configuring_the_allocator_turns_transparent_huge_pages_off() {
super::configure_allocator();
// SAFETY: reads this process's flag.
assert_eq!(
unsafe { libc::prctl(libc::PR_GET_THP_DISABLE, 0, 0, 0, 0) },
1
);
// SAFETY: reads one integer option.
assert_eq!(
unsafe { libmimalloc_sys::mi_option_get(super::ALLOW_THP) },
0
);
}
}
#[cfg(all(test, windows))]