Claude Code binary silently disables transparent huge pages (THP)
Status Fixed / completed
Maintainer reply ✓ Yes — bcherny
Workaround ✓ Mentioned in thread ↓
Activity 5 comments · opened Jun 26, 2026 · closed Aug 20, 2026
💡 Likely answer: A maintainer (bcherny, collaborator)
responded on this thread — see the highlighted reply below.
Hello! Please revive this issue. Many compute/memory-intensive workloads that are run inside of Claude Code are severely degraded due to the binary's own THP flags being inherited by shell calls via fork/exec. Like the OP mentioned, this is completely silent and even explicitmadvisecalls from the child proc are ignored. ``systemd THP=1 └─ tmux server (new-session) THP=1 └─ -zsh (your pane shell) THP=1 └─ claude --resume THP=0 ← disabled here └─ this bash shell THP=0 ← inherits from claude`There is an explicit call toprctl(PR_SET_THP_DISABLED, 1)somewhere on Claude Code's start-up path. My current workaround is to nudge Claude into wrapping all shell calls with a wrapper to re-enable THP:`c #include <sys/prctl.h> #include <unistd.h> int main(int argc, char **argv) { prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0); execvp(argv[1], &argv[1]); return 127; }`CLAUDE.md`## Transparent Huge Pages (THP) We never want a shell session running with THP *explicitly* disabled. THP should be governed only by the OS default or by a process disabling it for itself — never by an inherited disable flag. Theclaudebinary disables THP for itself, and that flag is inherited by every command it spawns, which pessimizes compute-heavy work. Therefore wrap all shell invocations with~/.local/bin/thp-madvise, which clears the inherited disable flag (restoring the OS default) before exec'ing the real command. For example, run~/.local/bin/thp-madvise my-commandrather thanmy-command.`Gemini CLI does not have this behavior. THP foragycan be explicitly disabled withTCMALLOC_TESTONLY_DISABLE_THP_ON_INIT=1` and when it is set, a very prominent warning is shown in the CLI about the potential for degraded performance.
_Originally posted by @y4n9squared in #53471_
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
(Another user — your root-cause analysis and the
prctlwrapper look right; one addition on the enforcement side.)Wrapping each call by instructing the model in CLAUDE.md ("run
~/.local/bin/thp-madvise my-command") is the fragile part: prose instructions degrade under task momentum — the model will silently drop the wrapper on some fraction of calls, and you won't notice until a compute-heavy step is slow again. It's the same failure mode behind "Claude ignores my CLAUDE.md rule" reports.Make it deterministic with a
PreToolUsehook onBashthat rewrites the command centrally, so the model never has to remember:(Exact return-shape per the hooks docs — the point is the hook prepends the wrapper to every Bash invocation regardless of what the model emits, and you can drop the CLAUDE.md section entirely.) A central wrapper also avoids the per-call quoting hazards you'd hit asking the model to compose
thp-madvise … my-commanditself.For the upstream report, the sharp ask is: the startup
prctl(PR_SET_THP_DISABLED, 1)shouldn't be inheritable by spawned shells — either clear it before fork/exec of child commands, or gate it behind an opt-out env var. The Gemini-CLI contrast you noted is good evidence it isn't a necessary behavior. Worth confirming whether it persists with--resumevs a fresh session, since your tree shows it set on theclaude --resumepath specifically.Hit this today, and spent some time with claude trying to figure out why. fairly confident its an upstream issue in bun & its vendored mimalloc, i'd report it there
anything claude-specific.
memory cost isn't worth it."
mimalloc is wrong about the scoping i think, so it applies to all downstream processes, inc. claude code, and then anything below that
I don't think mimalloc is at fault here. My interpretation of "rather than touching system wide settings" means that it's preferring to use
prctlrather than mutating the/proc/sysfilesystem. The behavior that process-wide settings are inherited by child processes throughforkis a general Linux behavior, not special to Bun, mimalloc, or Claude Code.I think the issue is that Claude Code _is a shell_ -- its job is to fork. So it has unique responsibilities to ensure that child processes of the shell (i.e. user code), does not inherit idiosyncratic behavior of itself (in this case, Bun deciding that THP is bad for the app's own garbage collection).
Thanks for the detailed report and the process-tree analysis — this was accurate, and it's now fixed.
What I tested on Linux (kernel 6.18, THP policy
[madvise]):straceshows the binary callingprctl(PR_SET_THP_DISABLE, 1)at startup, so the process reportsTHP_enabled: 0in/proc/<pid>/statusand every spawned shell inherits the disable, exactly as described.straceshows noPR_SET_THP_DISABLE, the running process reportsTHP_enabled: 1, and child processes inherit the OS default again — no wrapper needed.The fix landed via a runtime update; it isn't called out in the changelog, but I verified it directly by bisecting released builds. Please upgrade to 2.1.233 or later and drop the
thp-madviseworkaround.Closing as fixed — reply here if you still see
THP_enabled: 0on a current build and we'll reopen.🤖 Generated with Claude Code