Skip to content

perf(cli): allocate through mimalloc in the musl release builds - #310

Merged
sunerpy merged 1 commit into
mainfrom
perf/musl-mimalloc
Oct 3, 2026
Merged

sunerpy merged 1 commit into
mainfrom
perf/musl-mimalloc

Conversation

@sunerpy

@sunerpy sunerpy commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

The Linux release archives are static musl builds, and musl's allocator serializes threads. Indexing therefore got slower as threads were added: a full index of this repository took 122 s at 32 threads against 51 s at one, where a glibc build takes 5 s.

This PR makes mimalloc the global allocator on musl targets only:

  • crates/codegraph-cli/Cargo.toml: mimalloc = { version = "0.1.52", default-features = false, features = ["v2"] } under [target.'cfg(target_env = "musl")'.dependencies]. mimalloc and its bundled C library (libmimalloc-sys 0.1.49, mimalloc 2.3.2) are MIT; cc was already in the graph.
  • crates/codegraph-cli/src/main.rs: a #[cfg(target_env = "musl")] #[global_allocator] static. It is the only code change. The daemon is the same binary.
  • Cargo.lock: the two new crates.
  • README.md and docs/readme/README.zh-CN.md: one matching sentence in the prebuilt-archives sections.

glibc, macOS and Windows builds keep their system allocator, and their dependency graph is unchanged. There is no extraction-version bump and no golden change.

Why v2 and not v3. mimalloc v3's defaults hold memory longer: peak RSS was 750 MB at 32 threads. Getting it down needs MIMALLOC_PURGE_DELAY at runtime plus a compile-time arena setting. v2's defaults purge freed pages after 10 ms (decommitting with MADV_DONTNEED), so a long-lived serve --mcp process should hand an indexing burst back. That last point follows from the documented defaults and was not measured here.

The design passed the kirocodex plan gate in round 2 (1 → 0). Round 1's only item was keeping the Chinese README mirror in sync.

Measurements

Full index of an archive of d26df90 (689 files, viewer bundle excluded) on a 32-core machine. Wall time / peak RSS, from /usr/bin/time:

RAYON_NUM_THREADS 0.53.1 release (musl) this PR (musl) local glibc build
32 122.2 s / 249 MB 8.42 s / 499 MB 5.20 s / 354 MB
4 68.1 s / 195 MB 12.8 s / 252 MB 12.1 s / 227 MB
1 50.6 s / 188 MB 36.0 s / 218 MB 38.3 s / 200 MB
  • Two more runs at 32 threads took 8.24 s and 7.98 s.
  • All three are 14.5–15.3× faster than 0.53.1, and faster than the same binary at 4 threads. Each run met both acceptance thresholds.
  • Every timed run exited 0 and produced the same complete graph (141,628 canonical rows, hashed after each run). The harness stops on a failed run or a different graph; review round 1 asked for that, and the fix was shown to fail closed in three negative cases.
  • The cost is peak memory at high thread counts: about 500 MB at 32 threads.

Verification

  • Release toolchain build (zig 0.15.1, cargo-zigbuild 0.23.0, rustc 1.98.0, cargo zigbuild --locked --release -p codegraph-rs --target <t>):
    • x86_64-unknown-linux-musl: built and statically linked;
    • aarch64-unknown-linux-musl: built and statically linked. It was not run, because this host has no qemu.
  • Output identity. Nodes, edges, unresolved refs and file rows indexed by 0.53.1 (musl), this PR's musl binary and a glibc build are identical, compared over every column except timestamps.
  • Lint. cargo clippy --locked --target x86_64-unknown-linux-musl -p codegraph-rs -- -D warnings is clean, so the musl-only code is checked too.
  • make pre-ci on this tree at rustc 1.98 passed: 4,360 Rust tests and 569 frontend tests.
  • cargo audit: no vulnerabilities. The 452-crate scan runs locally, but its yank checks fail on the local registry mirror. CI's Security Audit job, the authoritative run, passed.

The Release workflow's two musl jobs build this for real. A failed build leaves the draft unpublished.

Not in this PR

  • Tuning mimalloc v3.
  • Switching the Linux archives to glibc.
  • A pull-request-time musl build job. Today the musl C builds (SQLite, tree-sitter, and now mimalloc) run only in the Release workflow.

🤖 Generated with Claude Code

musl's allocator serializes threads, so the static Linux release binaries
got slower as threads were added: a full index of this repository took
129 s at 32 threads against 50 s at one, where a glibc build takes 5 s.

Use mimalloc (v2, MIT) as the global allocator on musl targets only. The
dependency is target-gated, so glibc, macOS and Windows builds keep their
system allocator and their dependency graph is unchanged.

Measured with the release toolchain (cargo-zigbuild 0.23.0, zig 0.15.1,
rustc 1.98.0) on the same archive: 32 threads 129 s -> 8.2-8.4 s over
three runs, 4 threads 70 s -> 13 s, 1 thread 50 s -> 36 s. Peak RSS at 32
threads rises from 249 MB to about 500 MB. Nodes, edges, unresolved refs
and file rows are identical to 0.53.1's.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9bbf9be071

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +46 to +47
/// slower as threads were added: a full index of this repository took over two
/// minutes at 32 threads, against 51 s at one. Through mimalloc it takes 8 s.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove unreproducible benchmark timings

These hard-coded timings publish a performance claim without an in-repository record of the commit, corpus, environment, exact command, run count, and dispersion, so the numbers will become misleading as the repository and hardware change. Keep the allocator rationale here and move the measurements to reproducible benchmark evidence.

AGENTS.md reference: AGENTS.md:L141-L142

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Oct 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #310      +/-   ##
==========================================
- Coverage   95.07%   95.07%   -0.01%     
==========================================
  Files         198      198              
  Lines      108998   108998              
==========================================
- Hits       103632   103630       -2     
- Misses       5366     5368       +2     
Files with missing lines Coverage Δ
crates/codegraph-cli/src/main.rs 90.96% <ø> (-0.04%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sunerpy
sunerpy merged commit a52a64c into main Oct 3, 2026
11 checks passed
@sunerpy
sunerpy deleted the perf/musl-mimalloc branch October 3, 2026 10:43
@github-actions github-actions Bot mentioned this pull request Oct 3, 2026
sunerpy added a commit that referenced this pull request Oct 3, 2026
The release record for #310: the release PR merge and its tag SHA, the
workflow run, the published digest, and the black-box acceptance against
v0.53.1, plus a dated entry for the change itself with its measurement method.
Current alignment lists v0.53.2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant