Skip to content

Benchmarks ​

The numbers below are measured end-to-end against the same medium-sized real-world fixture — ~1400 packages — using tak to time each scenario under identical conditions. Samples are interleaved: each round times every package manager that still has runs left, in a freshly shuffled order, so drift on the benchmark machine is spread across all of them rather than landing on whichever one happened to be running. Slower managers get fewer runs, spread evenly across the rounds. Each number is the median of a manager's runs, so an occasional slow sample on a busy machine doesn't decide a close comparison.

Methodology

Every scenario assumes a committed lockfile is present. The main axis is how warm the tool's cache/store is before the command runs; each tool's row uses its default local install model, which means aube's global virtual store is enabled and pnpm's comparable feature is left at pnpm's default of off. The aube (no GVS) row is the one exception, described below.Warm clears node_modules but keeps each tool's store/cache populated; cold wipes the store and cache too. That untimed cleanup ends with a sync, so filesystem writeback left over from deleting the previous node_modules finishes before the next timed run starts. The fixture, scripts, and raw results live at benchmarks/. Reproduce locally with mise run bench.

aube 2.7.0 aube (no GVS) 2.7.0 bun 1.4.2 deno 2.9.7 pnpm 12.10.1 npm 11.9.0 yarn 4.18.1node 24.14.0
Fresh install (warm cache)
aube
336ms
aube (no GVS)
462ms
bun
790ms
deno
529ms
pnpm
689ms
npm
5.70s
yarn
5.32s
Fresh install (cold cache)
aube
1.39s
aube (no GVS)
1.30s
bun
1.65s
deno
1.79s
pnpm
21.26s
npm
8.22s
yarn
9.57s
Dependency update after git pull
aube
193ms
aube (no GVS)
215ms
bun
157ms
deno
255ms
pnpm
435ms
npm
2.19s
yarn
3.25s
npm install && npm run test
aube
24ms
aube (no GVS)
11ms
bun
55ms
deno
80ms
pnpm
105ms
npm
1.18s
yarn
2.50s

The chart highlights the warm-cache, no-node_modules install case. aube (no GVS) is aube with the global virtual store turned off, which is the layout aube uses by default under CI.

Last updated: 2026-10-08 · Source: benchmarks/results.json, regenerated by mise run bench:bump.

What the comparison includes ​

aube starts from pnpm's isolated symlink model — a global content-addressable store plus a per-project virtual store — and enables a global virtual store by default for local installs. pnpm supports a similar global virtual store, but leaves it off by default, so the benchmark rows compare the tools as users get them out of the box.

The aube (no GVS) row runs the same aube binary with enableGlobalVirtualStore=false. Every install then materializes packages into the project's own node_modules/.aube, the way pnpm does with node_modules/.pnpm, instead of linking into a shared global virtual store. aube turns the global virtual store off under CI, so this row is the closer match for CI installs. It installs the same lockfile as the aube row, from its own store and cache.

Scenarios ​

  • Fresh install (warm cache) — frozen lockfile, node_modules wiped, store and packument cache warm.
  • Fresh install (cold cache) — frozen lockfile but the store, packument cache, and node_modules are all wiped. Measures registry fetch, store import, and link/materialization work from scratch.
  • Dependency update after git pull — the previous commit is installed, then its package.json and lockfile are replaced with a commit that bumps ten pinned direct dependencies (Babel, MUI, ESLint, TypeScript, webpack, and others) and adds two more, the shape of a Renovate or Dependabot merge. Each tool updates its own lockfile ahead of time, so the timed run is the install a developer runs after pulling. Store and cache stay warm, so this measures finding what changed and relinking it rather than downloading. npm runs npm install here, since npm ci deletes node_modules first.
  • npm install && npm run test (already installed) — models the developer loop after dependencies are already installed. Each timed run repeats the tool's normal "install if needed, then run tests" command. aube can skip the install work when its install-state file is fresh; other tools still revalidate their lockfile or install state before dispatching the script. The fixture's test script is a no-op node -e, so this scenario mostly measures install short-circuiting and script dispatch.

Interpreting the results ​

These measurements describe this fixture, tool versions, hardware, and cache state. They do not establish that one tool is fastest for every project. The script scenario uses a no-op test command, so it measures command overhead, not the time needed to run a real test suite. Each row scales its bars independently; compare the printed times across scenarios.

The README and landing page read the same checked-in results. Their benchmark version can be older than the current aube release; the legend above records the versions that were actually measured.

Reproducing ​

sh
# One-shot: runs the package managers through the full scenario matrix,
# writes results to benchmarks/results.json, and emits the markdown
# summary to stdout.
mise run bench:bump

The task calls benchmarks/bench.sh through mise x, installs the benchmark toolchain declared on the task, sets WARMUP=3 with tak choosing each run count, and points RESULTS_JSON at benchmarks/results.json so the file is rewritten in-place. Commit the updated JSON to refresh the chart above — the VitePress data loader at docs/benchmarks.data.ts reads it at build time, so you don't need to edit benchmarks.md or hand-write any chart data.

Benchmark runs use a hermetic local registry with the link shaped to a fixed 500 Mbit/s bandwidth and 50 ms latency. This reduces variation from the network; CPU, filesystem, and machine load can still affect the results. The benchmark harness serializes runs with /tmp/aube-bench.lock:

sh
mise run bench
MIT LicensejdxCopyright © 2026 jdx.dev