perf(bench): collect mimalloc memory in benchmark setup - #15355
perf(bench): collect mimalloc memory in benchmark setup#15355stormslowly wants to merge 1 commit into
Conversation
Reclaim retained allocator memory before every measured iteration so each sample starts from a comparable heap state.
📦 Binary Size-limit
⏳ The base commit triggered a linux binding build, but its binary size data has not been generated yet, so the size comparison is skipped. Please re-run this workflow once the ecosystem-benchmark data for that commit is published. Warning Reference only — not the real baseline. The base commit's data isn't ready yet, so this compares against the nearest earlier commit that has data ( 🙈 Size remains the same at 68.18MB |
Rsdoctor Bundle Diff Analysis
Found 5 projects in monorepo, 0 projects with changes. 📊 Quick Summary
Generated by Rsdoctor GitHub Action |
Merging this PR will not alter performance
Comparing Footnotes
|
|
Closing: measured, and it does not improve stability. I ran the simulation benchmark 5× on this branch and 5× on its base (
Paired per benchmark: with-collect is tighter on 18, base is tighter on 16, 16 ties. Restricted to the 25 benchmarks that are not already deterministic ( Where the noise actually lives: entirely in the small The one real effect is a small systematic shift, not a variance reduction: of the 28 benchmarks that moved by more than 0.01%, 22 moved down (max −0.78%, mean −0.086%). That is a one-off bias absorbed at the next baseline. Cost was not the problem — the |
Why
Between two measured iterations a benchmark leaves the allocator dirty: thread-local free lists and retired pages are laid out according to the previous iteration's allocation and free order. The next iteration then walks a different allocator fast path, which was the suspected source of run-to-run variance.
This calls
mi_collect(true)in everyiter_batched/iter_batched_refsetup closure, so each measured iteration starts from a comparable heap state.Setup is outside the measured region —
codspeed-criterion-compatonly wraps the routine withstart_benchmark()/end_benchmark()(src/compat/bencher.rs:127-139), and criterion's walltime bencher excludes setup from timing — so the collect cost is not counted.What
rspack_benchmark::collect_memory()— thin wrapper overlibmimalloc_sys::mi_collect(true), no-op on wasm.libmimalloc-sysadded with theextendedfeature (that feature is what exposes themi_collectbinding); feature set kept aligned with the existingmimallocdependency.compilation_stages,build_chunk_graph,bundle,persistent_cache,scan_dependenciesandwalltime.Measured effect: no stability gain
Ran the simulation benchmark 5× on this branch and 5× on its base (
faba242cd6) as independent CI runs, then read the per-benchmark instruction counts straight out of the uploaded callgrind dumps (50 benchmarks, matching the 50 CodSpeed reports).mi_collectPaired per benchmark: with-collect is tighter on 18, base is tighter on 16, 16 ties. Restricted to the 25 benchmarks that are not already deterministic (
max CV > 0.01%): 12 vs 13. That is a coin flip, so allocator carry-over is not what drives the residual noise.The noise that remains is concentrated in the small
rust@<stage>benchmarks (a few million Ir); everysources@*,bundle@*andbuild_*benchmark is already deterministic.rust@create_chunk_assetsis the worst case in both arms (1.21% / 1.93%), which is the benchmark previously traced to glibcmemcpyifunc dispatch — somethingmi_collectcannot address.One real effect did show up: collecting shifts the measured level slightly down (22 of the 28 benchmarks that moved by more than 0.01%, up to −0.78%, mean −0.086%). That is a one-off bias absorbed at the next baseline, not a variance reduction.
Wall-clock cost is nil: the
Run benchmark (simulation)step took 727s here versus a 759s median over the last 22 main runs on the same runner type.