mirror of
https://github.com/pnpm/pnpm.git
synced 2026-06-28 01:45:30 -04:00
## Motivation The [vlt.sh benchmarks](https://benchmarks.vlt.sh/) (2026-06-11 run, pacquet 0.11.3) show pacquet several times slower than the fastest package managers in the warm-metadata fresh-resolve cells (`cache`: 3.9–8.1x), the cold-cache frozen-install cells (`lockfile`: up to 10x on vue), and `clean`. Profiling the babylon and vue fixtures locally (macOS time profiles of the warm fresh resolve and the install tail) surfaced three independent causes, fixed here. ## Changes ### 1. Deprecation probing without manifest hydration (pacquet) With `minimumReleaseAge` active (the default), every range pick runs `filter_pkg_metadata_by_publish_date`, and any dist-tag pointing outside the maturity cutoff (`next`, `beta`, `canary`, a too-fresh `latest`) repopulates by scanning all candidates and reading each candidate's `deprecated` flag. Each read hydrated the full version manifest — a complete `serde_json` parse including the flattened catch-all map. On babylon's warm fresh resolve this was the single largest CPU consumer (~10 thread-seconds, all on the resolve task's critical path). `PackageVersions::is_deprecated` now answers from the raw fragment (substring pre-check, then a single-field deserialize with the same normalization as `PackageVersion::deprecated`), the tag-repopulation loop parses candidate versions once per filter call (mirroring the `parsedSemverCache` in pnpm's `filterPkgMetadataByPublishDate`), and the deprecated-pick fallback uses the probe instead of hydrating every version. **babylon warm fresh resolve: `resolve_workspace` 7.5s → 2.6s.** ### 2. Relative-symlink up-to-date check (pacquet) `force_symlink_dir` joined an existing link's relative contents onto the link parent and compared the result *verbatim* against the wanted target. Virtual-store links contain `..` segments (`../../<pkg>/node_modules/<name>`), so the joined path never compared equal and every up-to-date symlink was unlinked and recreated. Node's `path.relative` — which upstream `symlink-dir`'s `isExistingSymlinkUpToDate` builds on — resolves its arguments, so pnpm treats those links as current. Both sides now pass through `lexical_normalize`. The babylon install tail was dominated by exactly this unlink+symlink churn. **babylon warm install: 6.8s → 4.7s; warm frozen install: 4.2s → 2.3s.** ### 3. Default network concurrency floor 16 → 64 (pnpm + pacquet) The default was `min(64, max(workers * 3, 16))`. Downloads are I/O-bound, not CPU-bound: on a 4-vCPU CI runner the formula yields 16 concurrent requests, so a low-latency registry drains 600–1300-tarball installs 16 at a time while staying unsaturated — a large share of the cold-cell (`lockfile`/`clean`) gap on the benchmark runners. The default is now `min(96, max(workers * 3, 64))`; the `networkConcurrency` setting still overrides it. Applied to `@pnpm/installing.package-requester`, the lockfile-resolution verifier fan-out that mirrors its floor, and the same two spots in pacquet. Changeset included (minor). **This is a user-visible defaults change on both stacks — flagging it explicitly for review.** ## Local results (M-series macOS, vlt fixtures, isolated store/cache) | cell | before | after | |---|---|---| | vue `cache` | 1159 ms | **479 ms** | | vue `cache+lockfile` | 621 ms | **392 ms** | | vue no-op install | 48 ms | **41 ms** | | babylon `cache` | ~8.8 s | **4.75 s** | | babylon `cache+lockfile` | ~4.2 s | **2.27 s** | vue's warm cells are now ahead of every competitor measured locally; babylon's `cache` cell closed from ~2.5x behind the leader to ~1.35x (the remainder is the per-file store-integrity verify and per-file linking that the pnpm store contract requires). ## Validation - `cargo nextest`: registry, resolving-npm-resolver, resolving-deps-resolver, lockfile-verification, network, fs, tarball, package-manager, cli — 1300+ tests, all green; new unit tests cover the deprecation probe (string/bool/empty/corrupt shapes, nested-key false positives) and cross-parent relative-symlink reuse (fails without the fix). - Lockfile stability: `--lockfile-only` output is byte-identical before/after on vue; on babylon the resolved **package-version sets are identical across 6 runs (3 per binary)**. The babylon lockfile does flap between runs in the peer-suffix shape of `webpack-dev-server@5.2.2` (`(bufferutil@4.1.0)(utf-8-validate@5.0.10)` appearing/disappearing) — this is **pre-existing nondeterminism** reproducible with the unmodified binary against itself, in the optional-peer area; worth a separate issue. - Pre-push checks (fmt, taplo, `cargo doc -D warnings`, dylint) pass; eslint (root config) and `tsgo --build` pass for the two touched TS packages.