mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-26 19:12:12 -04:00
* fix: skip re-importing packages when global virtual store is warm
When node_modules is deleted but the global virtual store directories
survive, pnpm previously re-fetched every package because the skip
logic required currentLockfile to be present. Add a fast-path that
checks pathExists(dir) for GVS directories even when currentLockfile
is missing, since the GVS directory hash encodes engine, integrity,
and full dependency subgraph.
* fix: remove includeUnchangedDeps guard from GVS fast-path
The includeUnchangedDeps flag is true whenever currentHoistPattern
differs from the desired hoistPattern. After deleting node_modules,
currentHoistPattern is always undefined (read from .modules.yaml),
so the flag is always true when hoisting is configured — defeating
the optimization in the exact scenario it targets.
The guard is unnecessary because the fast-path only skips fetch/import
(fetchResponse = {}), not graph inclusion. The package is still added
to the graph with children populated, so hoisting recalculation works.
* perf: add GVS warm reinstall benchmark scenario
Adds benchmark 6: frozen lockfile reinstall with a warm global virtual
store after deleting node_modules. This measures the reattach fast-path
where all packages are skipped (no fetch/import) because their GVS
hash directories already exist.
* fix: use proper types in fetchPackage spy to pass tsgo strict checks
pnpm Benchmarks
Compares pnpm install performance between the current branch and main.
Prerequisites
- hyperfine — install via
brew install hyperfine - The current branch must be compiled (
pnpm run compile) - If providing a pre-existing main checkout path, it must also be compiled
Usage
pnpm run compile
./benchmarks/bench.sh
If a git worktree with main already exists, the script finds and uses it automatically. Otherwise it creates one at ../.pnpm-bench-main (a sibling of the repo), installs dependencies, and compiles.
You can also point to a specific checkout of main:
./benchmarks/bench.sh /path/to/main
Scenarios
| # | Name | Lockfile | Store + Cache | Description |
|---|---|---|---|---|
| 1 | Headless | ✔ frozen | warm | Repeat install with warm store |
| 2 | Re-resolution | ✔ + add dep | warm | Add a new dependency to an existing lockfile |
| 3 | Full resolution | ✗ | warm | Resolve everything from scratch with warm store and cache |
| 4 | Headless cold | ✔ frozen | cold | Typical CI install — fetch all packages with lockfile |
| 5 | Cold install | ✗ | cold | True cold start — nothing cached |
All scenarios use --ignore-scripts and isolated store/cache directories per variant.
Output
Results are printed to the terminal and saved as:
results.md— consolidated markdown table<scenario>-main.json/<scenario>-branch.json— raw hyperfine data
All files are written to a temp directory printed at the end of the run.
Configuration
Edit the variables at the top of bench.sh:
WARMUP— number of warmup runs before timing (default: 1)RUNS— number of timed runs per benchmark (default: 10)