# pnpm vs npm install benchmark

These notes go with the shipmunks.dev post [pnpm vs npm](/blog/pnpm-vs-npm/).

The runner is `scripts/benchmarks/pnpm-vs-npm/bench.mjs`. It copies this repo's `package.json` into four directories under `/tmp/pnpm-vs-npm-bench` (override with `BENCH_WORK`). It does not copy a lockfile. Each tool writes its own lockfile once, untimed. Timed runs keep that lockfile and delete `node_modules`.

Cold runs also delete that tool's store or cache. Warm runs leave the store or cache in place. Three runs of each, round-robin, npm then pnpm then yarn then bun. The median in the post is the middle wall time. Wall time is `hrtime` around the child process.

Versions on the published run, November 5, 2026: Node v22.23.4, npm 10.9.9, pnpm 12.16.1, Yarn classic 1.22.22, Bun 1.4.4 (`1.4.4+38afee6c8`).

Commands:

```sh
npm install --cache <work>/npm/cache
pnpm install --allow-build esbuild --allow-build sharp --store-dir <work>/pnpm/store --state-dir <work>/pnpm/state
yarn install --cache-folder <work>/yarn/cache --non-interactive
BUN_INSTALL_CACHE_DIR=<work>/bun/cache bun install
```

`pnpm install` without `--allow-build` exited 1 on this tree with `ERR_PNPM_IGNORED_BUILDS`. That one run is in `results.json` under `extra.default_pnpm_install`. It is not in the median.

`du -sb` is apparent size. The combined column is one `du` of `node_modules` and the store or cache, so a hard link is counted once.

A rerun will not match the medians. The rows from the run published with the post are [pnpm-vs-npm-benchmark.csv](/files/pnpm-vs-npm-benchmark.csv).
