fix(mem): restore mimalloc's Linux arena-commit default (#1654) - #1666
Open
DeusData wants to merge 1 commit into
Open
fix(mem): restore mimalloc's Linux arena-commit default (#1654)#1666DeusData wants to merge 1 commit into
DeusData wants to merge 1 commit into
Conversation
Since #1360 routed ordinary malloc/new through mimalloc on Linux, the arena policy governs every allocation in the process rather than just the bound sqlite/tree_sitter populations. cbm sets arena_eager_commit=0, so mimalloc commits sub-ranges with mprotect(PROT_READ|PROT_WRITE) over a PROT_NONE reservation, and each partial commit SPLITS the reserved VMA. Measured on the Go corpus, Linux arm64, shipped binaries: v0.9.0 10 mappings, at ANY worker count v0.10.5 ~22k mappings, peak; the count tracks CONCURRENCY (999 at 1 worker, 8460 at 4, 11965 at 18) Two consequences, both of which #1654 reported from a 96-CPU/376 GB host: the mmap/mprotect churn serialises on the kernel's per-process mmap_lock, and the VMA count climbs toward vm.max_map_count, after which mmap fails for ANY size -- so mimalloc reported it could not allocate 10 KB while `free -g` still showed 246 GB available. mimalloc's own default for this option is 2, meaning "eager-commit arenas only on an OS that overcommits (i.e. linux)", precisely because commit is free there until pages are touched. Overriding it to 0 opted Linux out of the default written for Linux. Restore it on Linux only; every other platform keeps the lazy setting, where commit is NOT free and the upfront-memory reason still holds (Windows especially, #581). Measured effect, same corpus and host, baseline build vs this build: mappings 22450 -> 17312 (-23%) wall 92.4s -> 92.6s (unchanged) peak RSS 19.14 -> 19.22 GB (unchanged) This is a partial mitigation, not a cure: the remaining ~17k mappings are individual 64 KB-3 MB extraction buffers, each taking its own mmap (the worker reserves ~40 GB of address space for ~19 GB of RSS). Pooling those is the durable fix and is deliberately left out of this change. Guard: mem_arena_eager_commit_follows_platform_commit_cost pins the platform split so the Linux default cannot be silently opted out again. Reproduction and controlled 2x2 (only vm.max_map_count varied) are recorded on #1654. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Restores mimalloc's own Linux default for
arena_eager_commit, which cbm had overridden to0.Since #1360 routed ordinary
malloc/newthrough mimalloc on Linux, the arena policy governs every allocation in the process rather than just the bound sqlite/tree-sitter populations. With lazy arena commit, mimalloc commits sub-ranges viamprotect(PROT_READ|PROT_WRITE)over aPROT_NONEreservation, and each partial commit splits the reserved VMA.Measured on the Go corpus (Linux arm64, shipped binaries):
The count tracks concurrency, not corpus size. That produces both symptoms reported in #1654 from a 96-CPU/376 GB host: the
mmap/mprotectchurn serialises on the kernel's per-processmmap_lock(they saw 1.2% of extraction in 45 minutes, where v0.9.0 finished the tree in ~13), and the VMA count climbs towardvm.max_map_count, after whichmmapfails for any size — hence mimalloc reporting it could not allocate 10 KB whilefree -gstill showed 246 GB available.mimalloc's default for this option is
2, meaning "eager-commit arenas only on an OS that has overcommit (i.e. linux)" — precisely because commit is free there until pages are touched. Overriding it to0opted Linux out of the default written for Linux. Every other platform keeps the lazy setting, where commit is not free and the upfront-memory reason still holds (Windows especially, #581).Measured effect
Baseline build vs this build, same corpus and host:
Scope — this is a mitigation, not a cure
Deliberately narrow. The remaining ~17k mappings are individual 64 KB–3 MB extraction buffers, each taking its own
mmap; the worker reserves ~40 GB of address space for ~19 GB of RSS. Pooling those is the durable fix and is not attempted here.Also measured and rejected for this PR: additionally raising
purge_delaycuts mappings to 6,362 (−72%) and wall time by 40%, but raises peak RSS ~16% and OOM-killed a 24 GB host on a corpus the baseline completed. That trade is wrong for exactly the memory-pressured users hitting #1654, and would need a memory-headroom guard.Verification
=== All tests passed ===51 passed, 1 failed); re-applied, green (52 passed). The test binds to the fix.memsuite: 52 passed.#if defined(__linux__)so Windows takes the unchanged branch, but CI is the check.Checklist
git commit -s)make -f Makefile.cbm lint-ci) — cppcheck + clang-format + NOLINT check all cleanRefs #1654.