Run Rspack's perf-guided optimization loop for `cases/all` and similar workloads: create an isolated worktree, build a profiling binding, benchmark with `RSPACK_BINDING`, collect and compare `perf` hotspots, implement small Rust changes, validate, commit, push, and trigger the Ecosystem Benchmark workflow after each pushed commit. Use this when the goal is iterative performance work, not just one-off profiling.
This skill is for a perf-guided optimization loop, not Rust/LLVM -C profile-generate/-C profile-use.
Use this when the task is "keep finding the next hotspot and improve it". For raw perf capture details or samply import, also read ../rspack-perf-profiling/SKILL.md.
https://github.com/hardfist/rspack-bench-repo<bench-repo>/cases/allRSPACK_BINDING; do not rely on the installed @rspack/binding.Prefer a temp worktree based on origin/main:
git fetch origin
git worktree add /tmp/rspack-perf-$(date +%Y%m%d) -b <branch-name> origin/main
If the active branch already has user changes, do not reuse it for perf experiments.
pnpm run build:binding:profiling
The binding used for local benchmark runs should be:
<worktree>/crates/node_binding/rspack.linux-x64-gnu.node
Run the benchmark from the benchmark repo, but point it at the temp worktree binding:
If the benchmark repo is not present yet, clone it to a sibling directory or another scratch location first:
git clone https://github.com/hardfist/rspack-bench-repo ../rspack-bench-repo
cd ../rspack-bench-repo/cases/all
env RSPACK_BINDING=<worktree>/crates/node_binding/rspack.linux-x64-gnu.node \
/usr/bin/time -f '%e real %U user %S sys' \
./node_modules/.bin/rspack -c ./rspack.config.js
For cases/all, compare against a recent valid baseline built the same way. One run is enough to reject obviously bad ideas; use 2-3 runs before keeping a change.
perf profile firstStart with a flat cycles:u profile. On this repository the .node binary is large enough that DWARF callgraphs can become too expensive.
perf record -o /tmp/rspack-cases-all.perf.data \
-e cycles:u -F 2500 -- \
env RSPACK_BINDING=<worktree>/crates/node_binding/rspack.linux-x64-gnu.node \
./node_modules/.bin/rspack -c ./rspack.config.js
perf report -i /tmp/rspack-cases-all.perf.data \
--stdio --no-children -g none --percent-limit 0.2 | head -n 160
Use DWARF callgraphs only when flat symbols are insufficient.
Prioritize changes that can move wall time, not just symbol percentages.
Good candidates in cases/all have been:
ExportsInfoGetter::prefetch, get_used_nameOverlayMap::get, GetExportsTypeCache::getstart_send, Stealer::steal, tiny parallel tasksmi_free, repeated String or ArcPath creation, reserve_rehashTreat low-single-digit hash micro-hotspots carefully. Keep them only if the end-to-end benchmark moves.
Common winning patterns:
IdentifierMap, IdentifierSet, UkeyMap, UkeySet(Identifier, bool) when one dimension is tinywith_capacity / reserve in hot temporary collectionscollect plus sequential applyString when an Atom, Identifier, or borrowed string is enoughMinimum validation for Rust-side perf changes:
cargo fmt --all --check
cargo check -p <crate>
pnpm run build:binding:profiling
Run targeted tests if the touched area already has coverage. Run broader checks only when they are relevant and not blocked by known unrelated failures.
Keep a change only if at least one of these is true:
cases/all compile time improvesRevert experiments that only reshuffle percentages without improving compile time.
After every kept change:
git commit -am "refactor(<scope>): <perf change>"
git push
gh workflow run 'Ecosystem Benchmark' -f pr=<pr-number>
gh run list --workflow 'Ecosystem Benchmark' --limit 5
The benchmark workflow input is defined in .github/workflows/ecosystem-benchmark.yml. Use pr=<pr-number>; a run on main does not benchmark the PR branch.
If git push is unavailable because of local network or DNS issues, use gh/GitHub API as a fallback to update the PR branch, then trigger the workflow. In that fallback mode, local git status may stay dirty because local HEAD does not know about the remote-only commit.
Before opening a new PR or after long-running iteration:
git fetch origin
git rebase origin/main
If the branch was updated through the GitHub API instead of local git, sync the local branch before assuming git status represents unpushed work.
When reporting progress, include:
perf.data path for the kept profileperf percentage is not enough; keep changes only when compile time improves or stays flat with a clearly better hotspot profile.gh run list may show an in-progress Ecosystem Benchmark on main; that does not replace a workflow_dispatch run for the PR.git status shows a modified tracked file after a fallback remote update, compare the local file blob hash with the remote contents API before assuming something is uncommitted.perf.data, scratch logs, and benchmark-only files under /tmp or the temp worktree.