Repository navigation
Perf regression: yarn-classic/hosted wall +165% (5096c41a, #1328) #1423
Description
[agent] Bench: yarn-classic/hosted wall time is +165% and yarn-classic/rescan +153% on today's main (17793173), compared with f3c6313a (the previous bench run, 2026-10-09). The hosted scan also makes 60 more requests (127 → 187), one GET per patched package's served tarball.
Culprit: 5096c41a, #1328 ("Fix fragmentless yarn classic hosted pins (#558)"). This was found by profiling, not by bisect builds, and git log -L puts the hot function's only change in that range in that commit.
Same-machine interleaved compare
Base f3c6313a → head 17793173, on a 4 vCPU Intel Xeon @ 2.10GHz cloud sandbox. An A/A run on the same machine was clean.
| scenario | round | base wall | head wall | Δ wall [95% CI] | Δ CPU | Δ RSS | requests |
|---|---|---|---|---|---|---|---|
yarn-classic/hosted |
1 (15 pairs) | 176.7 ms | 444.5 ms | +159.2% [+128.4, +167.9] | +137.6% | +4.7% | 127 → 187 (+60) |
yarn-classic/hosted |
2 (20+10 pairs) | 171.8 ms | 462.0 ms | +164.8% [+149.2, +178.2] | +144.6% | +4.2% | 127 → 187 (+60) |
yarn-classic/rescan |
1 | 132.6 ms | 342.9 ms | +153.7% [+142.0, +170.3] | +123.5% | +5.7% | 127 |
yarn-classic/rescan |
2 | 123.4 ms | 314.3 ms | +152.7% [+140.7, +168.2] | +123.8% | +4.8% | 127 |
The rescan makes no extra requests, so most of the cost is local CPU work rather than the tarball downloads.
Hot spot
Callgrind on the head binary, yarn-classic/rescan (3000 packages, 60 patched), inclusive instructions:
93.8% scan::run_scan
72.3% scan::hosted::run_redirect_selected_untracked
67.8% hosted::engine::yarn_classic_artifact_targets
60.8% hosted::engine::classic_locks_registry_copy
43.4% formats::yarn::blocks::scan_blocks
10.5% drop_in_place<Vec<LockBlock>>
8.0% formats::yarn::patterns::split_key_patterns
yarn_classic_artifact_targets runs classic_locks_registry_copy(lock, name, version) once per candidate dep. Each call re-runs scan_blocks over the whole yarn.lock and splits every block's key patterns, so the cost is O(patched deps × lock size). The registry-copy filter also runs before the cheap lock.contains("\"<url>#") test, so a rescan pays it for every dep even though every dep is already pinned and nothing gets fetched.
Possible fixes:
- Scan the blocks once per lock and build a
(real name, version) → registry copyset. - Put the
sha1.is_some() && lock already pins the urlcheck first. - The hosted run (+60 GETs) also downloads every patched tarball to read its
package.jsonfor the Yarn classic: a patch that adds a dependency to the package's own package.json leaves vendored yarn.lock with a dangling dependency (offline frozen install fails, lock churns), and hosted silently installs without it #591 unlocked-dependency check. That is intentional and explained in Fix fragmentless yarn classic hosted pins (#558) #1328, but the downloads run inside the scan's critical path.
Repro
export CARGO_PROFILE_PERF_INHERITS=release CARGO_PROFILE_PERF_LTO=thin CARGO_PROFILE_PERF_STRIP=none
git worktree add /tmp/base f3c6313a && (cd /tmp/base && CARGO_TARGET_DIR=/tmp/tb cargo build --locked --profile perf -p socket-patch-cli)
CARGO_TARGET_DIR=/tmp/th cargo build --locked --profile perf -p socket-patch-cli -p socket-patch-bench
/tmp/th/perf/socket-patch-bench compare --base /tmp/tb/perf/socket-patch --head /tmp/th/perf/socket-patch -f '^yarn-classic/' --runs 20On main, the yarn-classic scenarios fail validation (60 unexpected tarball GETs) until the bench fixture update in #1422 lands. The PR serves real .tgz artifacts. Run the repro from that PR's branch.
Runner: nproc 4, Intel(R) Xeon(R) Processor @ 2.10GHz.
Generated by Claude Code
Activity
mikolalysenko commentedon Oct 12, 2026
[agent] Claiming this issue (shared root cause: yarn classic hosted artifact-target selection re-scans the whole yarn.lock once per patched dep). Branch: agent/fix-yarn-classic-registry-copy-rescan. Claim-ID: 2026-10-12T08:20:57Z-4fc440
Generated by Claude Code
mikolalysenko commentedon Oct 12, 2026
[agent] Draft PR: #1426
Generated by Claude Code
Read yarn.lock once when picking classic tarballs
Skip lock reads for absent names; count reads
Fetch yarn classic tarballs concurrently
Check served yarn deps without re-splitting keys
Look up pinned yarn artifacts in one lock pass
Check served yarn deps against keys split once