the short version
- Swapping in mimalloc does not bring musl to parity with glibc; Brokk measured musl plus mimalloc at 26% slower.
- The regression showed up on 4-core EC2 VMs, so it is not confined to high-concurrency workloads.
- A task that barely allocates still regressed on musl, pointing at slow memory routines beyond the allocator itself.
Published
Key facts
- Brokk measured musl with mimalloc preloaded as 26% slower than glibc for its Rust service Bifrost.
- Benchmarks ran on 4-core EC2 VMs, not a 64-core stress rig, with Bifrost sizing thread pools to that limit.
- scan_usages improved with mimalloc but stayed slower on musl; structural_clone_smells showed no measurable mimalloc difference.
- Brokk removed musl as a prebuilt option for Bifrost while keeping smaller projects such as Hel musl-only with mimalloc.
- No jemalloc number, per-routine microbenchmarks, image size comparison or absolute latency figures were published.
Brokk benchmarked its Rust service Bifrost on musl and on glibc and published the delta: musl with mimalloc preloaded is still 26% slower than glibc. The measurements came from 4-core EC2 VMs, not a 64-core stress rig. The team's response was to remove musl as a prebuilt option for Bifrost entirely.
The path into the problem is a familiar one for anyone shipping containers. The author hit an incompatible libc with containerized Bifrost, GPT suggested musl as the fix, and the appeal of a self-contained binary with a single implementation behind it was enough to move other Rust projects to musl as well. A colleague, Ryan, later pointed out that musl is known to have a suboptimal allocator and linked Daniel Raneland's article on the subject. That prompted the actual measurement.
Why the 4-core detail matters
A common defense of musl is that its allocator only falls apart under heavy thread contention, so single-threaded or lightly-threaded services are safe. Brokk's numbers contradict that directly. The post states the regressions are not confined to high concurrency scenarios, and that the numbers come from 4-core EC2 VMs with Bifrost sizing its thread pools accordingly.
That is the sizing most agent sidecars, indexers and retrieval workers actually get in production, where CPU limits are set in single-digit cores and thread pools are derived from those limits. If you assumed the musl penalty was a large-machine problem, the measured result says otherwise for at least one real Rust workload at four cores.
What mimalloc fixes and what it does not
But unfortunately "just use mimalloc" [or jemalloc] is not a magic wand that gets musl performance parity with glibc; musl with mimalloc is still 26% slower.
The post breaks the residual gap down across the two task types with the worst regressions, and the two behave differently. That difference is the useful part of the writeup, because it tells you whether an allocator preload is even the right lever for your workload.
- scan_usages does get some benefit from mimalloc, but remains slower on musl than on glibc.
- structural_clone_smells barely allocates at all, and the difference between musl with and without mimalloc is noise.
- structural_clone_smells nonetheless suffers proportionally more from musl than scan_usages does, which rules out the allocator as the sole cause.
The conclusion the author draws from that pattern is that the allocator is not the only suboptimal code in musl, and that several common memory routines are particularly slow. For an allocation-light hot loop dominated by copies and comparisons, an LD_PRELOAD of mimalloc buys you nothing measurable. That is a concrete diagnostic: if profiling shows your hot path spends its time in memory routines rather than in malloc, the allocator swap is not going to recover the gap and you need a different libc.
The base image decision this changes
The practical framing in the post is a footgun argument rather than a purity argument. The author's position is that musl should ship without an allocator and make you choose one, so that picking a bad allocator is an explicit decision instead of an undocumented default you discover through a production regression.
Brokk split its own projects on the size of the number. Smaller projects where 25% slower does not matter, such as Hel, stay musl-only for simplicity, with mimalloc added. The much-more-performance-sensitive Bifrost loses musl as a prebuilt option. That is a reasonable template for anyone maintaining a mix of internal tools and latency-sensitive services: keep the static, dependency-free build where the throughput budget is loose, and move to glibc where it is not.
It is worth being precise about what triggered the original migration, because the same trigger is everywhere in container work. The move to musl was a fix for an incompatible libc in a container, not a performance decision. Compatibility fixes that quietly change your allocator and memory routines deserve a benchmark before they become the default build target across a fleet.
What the post does not measure
The writeup reports one aggregate figure, 26% for musl with mimalloc, plus a directional breakdown of two task types. It does not publish per-routine microbenchmarks for the slow memory routines it names, and it does not report a separate jemalloc number; jemalloc appears only bracketed alongside mimalloc in the parity claim. There is no measurement of static-pie versus dynamically linked glibc builds, no container image size comparison, and no absolute latency figures to anchor the percentages.
So the transferable claim is narrower than "musl is slow": for one Rust workload on 4-core EC2, glibc beat musl by a margin that mimalloc reduced but did not close, and the residual came from something other than allocation. If you are running Rust agent tooling or inference-adjacent services on Alpine-style images, the cheap next step is to build the same binary against glibc, run your own two worst tasks on the CPU limit you actually deploy with, and see whether your gap looks like scan_usages or like structural_clone_smells. Only the first of those is fixable with a preload.
Questions this raises
does mimalloc fix musl performance
Only partially. Brokk found musl with mimalloc preloaded was still 26% slower than glibc, and the allocation-light task structural_clone_smells showed no measurable difference with or without mimalloc. The residual gap points at slow memory routines in musl rather than the allocator alone.
is the musl allocator problem only a high-core-count issue
Brokk's numbers say no. The regressions came from 4-core EC2 VMs with thread pools sized to that limit, which is the sizing most sidecars, indexers and retrieval workers get in production. The post states the regressions are not confined to high concurrency scenarios.
should I use musl or glibc for a Rust container image
Brokk split on how much the throughput budget matters. Smaller projects where 25% slower is irrelevant stayed musl-only with mimalloc, while the performance-sensitive Bifrost lost musl as a prebuilt option. Build the same binary against glibc and run your two worst tasks at your real CPU limit before deciding.
These daily notes are drafted by a model I run and operate myself - the same kind of pipeline this site is about - from sources published in the previous 24 hours, and every one lists what it read. The longer essays, the talks and the preprint are mine, written by hand.
