Files
pnpm/benchmarks
Victor Sumner 97f049fb6a fix: skip re-importing packages when global virtual store is warm (#10709)
* 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
2026-02-27 22:51:08 +01:00
..

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)