Repository navigation
perf: cap default tokio worker threads at 16 - #11029
Conversation
Tokio spawns every worker thread eagerly when the runtime is built, so worker count is pure startup cost paid by every invocation — clone + stack + TLS pages per thread before any work happens. The default was nprocs with a floor of 8, which scales that cost linearly with core count for no benefit: async I/O doesn't need a worker per core, and blocking work runs on tokio's separate on-demand pool. Measured on a 32-core Linux box (Arch, x86_64, hyperfine -N, warm, v2026.7.7 release binary, simulated via MISE_JOBS): - mise version: 6.3ms (32 workers) -> 5.6ms (8) / 5.8ms (16) - hook-env early-exit fast path (per shell prompt): 4.1ms -> 3.3ms - system time: 6.1ms -> 4.0ms Machines with <=16 cores are unaffected. An explicit MISE_JOBS still raises the worker count without limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe Tokio multi-thread runtime now caps its default worker count at 16 when ChangesRuntime defaults
Estimated code review effort: 1 (Trivial) | ~5 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR reduces startup overhead on many-core Linux machines by capping the default Tokio worker thread count at 16 (was
Confidence Score: 5/5Safe to merge — it's a single arithmetic expression change with a well-understood effect, backed by benchmark data. The change is minimal (one expression, well-commented), the floor of 8 is preserved, edge cases like nprocs=0 and MISE_JOBS values below the floor all behave identically to before, and the cap only applies to the default path. Parallel installs were verified in the benchmarks. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "style: format tokio worker count express..." | Re-trigger Greptile |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.7 x -- echo |
19.9 ± 1.5 | 17.4 | 31.4 | 1.00 |
mise x -- echo |
21.9 ± 2.2 | 17.5 | 41.6 | 1.10 ± 0.14 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.7 env |
23.5 ± 1.5 | 20.3 | 29.6 | 1.04 ± 0.09 |
mise env |
22.5 ± 1.4 | 19.4 | 29.2 | 1.00 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.7 hook-env |
25.2 ± 1.4 | 21.0 | 30.8 | 1.11 ± 0.09 |
mise hook-env |
22.7 ± 1.4 | 18.9 | 28.0 | 1.00 |
✅ Performance improvement for hook-env is 11% |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.7 ls |
20.6 ± 1.6 | 17.2 | 28.4 | 1.04 ± 0.11 |
mise ls |
19.7 ± 1.3 | 16.2 | 25.1 | 1.00 |
xtasks/test/perf
| Command | mise-2026.7.7 | mise | Variance |
|---|---|---|---|
| install (cached) | 182ms | 183ms | +0% |
| ls (cached) | 73ms | 70ms | +4% |
| bin-paths (cached) | 72ms | 73ms | -1% |
| task-ls (cached) | 147ms | 145ms | +1% |
Entire-Checkpoint: 9e11d637148a
Summary
Linux leg of the startup-perf work (#11025, #11027). Benchmarked the shipped v2026.7.7 binary on a 32-core Arch Linux box against asdf 0.20 and profiled where the time goes.
The Linux-specific finding: startup is system-time dominated (6ms sys for
mise version, ~2200 minor faults), and a big contributor scales with core count — the tokio runtime is built withworker_threads = nprocs.max(8), and tokio spawns every worker eagerly at build. On a 32-core machine that's 32 threads (clone + stack + TLS faults each) before any work happens, on every invocation including the per-prompt hook-env fast path. Async I/O doesn't need a worker per core; blocking work runs on tokio's separate on-demand pool.This caps the default at 16 (floor of 8 unchanged). Machines with ≤16 cores see no behavior change. Explicit
MISE_JOBSstill raises it without limit.Benchmarks (32-core Linux, hyperfine -N, warm, isolated env; worker count simulated via MISE_JOBS on the release binary)
mise versionParallel install of multiple tools verified working with the cap.
Other Linux findings (no PR needed)
LD_DEBUG=statisticsshows relocation processing of the 125k RELA entries takes only ~0.15ms (~84% of loader time but tiny in absolute terms).-z pack-relative-relocs(RELR) is not shippable: binaries would fail to load on glibc < 2.36, and the release pipeline explicitly validates Amazon Linux 2 (glibc 2.26) compatibility. It would only save ~0.15ms + ~3MB anyway.benchmarks/pgo.bashpipeline is a ready-made template.🤖 Generated with Claude Code
Note
Low Risk
Startup-only default thread cap with unchanged explicit
MISE_JOBSoverride; machines with ≤16 cores are unaffected.Overview
Reduces CLI startup cost on many-core machines by changing how the default Tokio multi-thread worker count is chosen in
main.Previously the runtime used
available_parallelism()with a floor of 8, so a 32-core host eagerly spawned 32 workers at build time (each paying clone/stack/TLS cost on every invocation, including hook-env). The default is nowmin(nprocs, 16)with the same floor of 8; hosts with ≤16 cores behave as before. An explicitMISE_JOBSstill sets the count with no cap.Inline comments document why async I/O does not need one worker per core and that blocking work uses Tokio’s separate pool.
Reviewed by Cursor Bugbot for commit 7301b72. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit