Install benchmarks
Compare package install time and peak memory across six states, with LPM Firewall results.
Tables show median peak RSS and wall time from ten attempts per result. Lower is better. Bold marks the lowest median in each column.
Measured on September 28, 2026, on an Apple M5 Pro with 48 GiB RAM.
Benchmark script: run-t3-install-six-states.mjs.
First install
No lockfile, a cold dependency cache, and no installed tree.
| Package manager | ||
|---|---|---|
| LPM CLI | 355.6 | 1,594.5 |
| Bun | 393.5 | 1,825 |
| LPM Firewall Enabled | 383.7 | 2,171 |
| pnpm | 624.4 | 4,174 |
| UPM | 1,125.0 | 4,431.5 |
| Yarn | 1,750.6 | 6,055 |
| Deno ¹ | 710.7 | 8,156 |
| vlt | 1,540.4 | 8,495.5 |
| Nub | 917.9 | 10,834.5 |
| npm | 863.2 | 13,787.5 |
¹ Deno completed 9/10 first-install attempts. One attempt timed out after 180,002 ms and has no RSS value. Its medians use the nine successful attempts. Every other result on this page has 10/10 successes.
Fresh / warm
No lockfile, a warm dependency cache, and no installed tree. The manager resolves dependencies again with cached package data.
| Package manager | ||
|---|---|---|
| LPM CLI | 90.4 | 66 |
| Bun | 49.6 | 191.5 |
| Deno | 230.9 | 222.5 |
| LPM Firewall Enabled | 96.0 | 321.5 |
| pnpm | 123.5 | 355 |
| Yarn | 520.2 | 1,169 |
| vlt | 507.7 | 1,352 |
| Nub | 483.6 | 1,457.5 |
| UPM | 391.4 | 2,276 |
| npm | 1,448.7 | 3,318.5 |
CI cold
An existing lockfile, a cold dependency cache, and no installed tree.
| Package manager | ||
|---|---|---|
| LPM CLI | 239.0 | 1,374.5 |
| Bun | 129.7 | 1,538 |
| LPM Firewall Enabled | 258.3 | 1,797 |
| Deno | 536.0 | 1,897.5 |
| npm | 395.3 | 2,655.5 |
| Yarn | 556.9 | 2,661.5 |
| vlt | 936.9 | 2,753 |
| UPM | 860.6 | 3,081.5 |
| Nub | 472.8 | 3,441.5 |
| pnpm | 481.9 | 3,455.5 |
The CI labels describe the initial files and cache. Each manager uses its normal install command and its own lockfile, rather than a dedicated ci command.
CI warm
An existing lockfile, a warm dependency cache, and no installed tree.
| Package manager | ||
|---|---|---|
| LPM CLI | 61.6 | 102 |
| Bun | 7.2 | 186.5 |
| Deno | 16.0 | 190.5 |
| pnpm | 60.8 | 329.5 |
| LPM Firewall Enabled | 59.0 | 387.5 |
| Nub | 82.9 | 390.5 |
| Yarn | 231.1 | 654.5 |
| vlt | 503.2 | 1,394 |
| UPM | 128.0 | 2,049.5 |
| npm | 865.9 | 2,293 |
Installed / cache gone
An existing lockfile and installed tree, with the dependency cache removed. LPM CLI retains its backing content store because installed symlinks depend on it. Only its ephemeral cache is removed.
| Package manager | ||
|---|---|---|
| Deno | 14.6 | 8 |
| pnpm | 18.5 | 10 |
| LPM CLI | 25.3 | 12 |
| LPM Firewall Enabled | 25.5 | 12 |
| Bun | 7.9 | 19 |
| UPM | 49.0 | 29 |
| Nub | 55.3 | 131 |
| vlt | 104.6 | 162.5 |
| npm | 158.7 | 667.5 |
| Yarn | 546.2 | 1,748.5 |
Up to date
An existing lockfile, a warm dependency cache, and an installed tree. No project files changed.
| Package manager | ||
|---|---|---|
| Deno | 14.6 | 9 |
| Yarn | 21.3 | 9.5 |
| pnpm | 18.5 | 10 |
| LPM CLI | 25.5 | 12 |
| LPM Firewall Enabled | 25.5 | 12 |
| Bun | 7.9 | 19 |
| UPM | 49.0 | 29 |
| Nub | 67.6 | 129.5 |
| vlt | 104.6 | 163.5 |
| npm | 149.2 | 440.5 |
The small results in the last two tables include process startup and timing-wrapper overhead. Differences of a few milliseconds do not establish a stable ranking.
Versions and host
| Package manager | Measured version |
|---|---|
| LPM CLI | 0.78.0 |
| LPM Firewall Enabled | LPM CLI 0.78.0, monitor mode |
| Bun | 1.4.2 |
| pnpm | 12.6.0, native executable |
| npm | 12.1.0 |
| Nub | 0.9.5 |
| Deno | 2.9.7 |
| vlt | 1.2.0 |
| UPM | 1.2.0 |
| Yarn | 6.0.0-rc.22, native preview executable |
| Aube | 2.5.1, blocked during preflight (no scored timings) |
The host ran macOS on arm64, with Node.js 24.19.0 and AC power. LPM CLI used a release build of commit 5cc8869, built with Rust 1.94.0. Executable hashes matched before and after the run.
Aube rejected next-auth@5.0.0-beta.30 under its default trust policy, with ERR_AUBE_TRUST_DOWNGRADE. This policy block is not a timing result or evidence that the package is malicious. The benchmark did not bypass the policy.
Methodology
The T3 fixture contains 25 direct dependencies and development dependencies, including Next.js, React, tRPC, Drizzle, and TypeScript. The baseline contains 540 scored attempts: 539 successes and one timeout. The separate firewall run adds 60 successful scored installs. Medians use successful installs only. No slow scored samples were discarded.
Preparation and cache cleanup occur outside the measured interval. The harness rotates manager order and state order across rounds. Each manager has an isolated home and cache, with CI=1 and NO_COLOR=1. Cold means empty dependency caches, not empty operating-system or CDN caches.
Peak RSS comes from /usr/bin/time -l. It is a memory high-water mark, not the simultaneous memory total for a process tree. Detached vlt workers are not fully represented. The harness drains those workers outside the foreground measurement.
Lifecycle scripts remained disabled. Release-age and security policies were not normalized across managers. No proxy or LPM CLI concurrency override was present at launch.
| Manager | Benchmark configuration |
|---|---|
| LPM CLI | JSON output, V2 store, --no-security-summary, --no-skills, and --no-editor-setup. Dependency scripts remained blocked. Release cooldown and LPM Firewall were off. |
| LPM Firewall Enabled | The same LPM CLI configuration, with LPM Firewall in monitor mode and package_only lookups. |
| Bun | --ignore-scripts. No release-age override. |
| pnpm | --ignore-scripts, silent reporter, and an isolated store. No release-age override. |
| npm | --ignore-scripts, --no-fund, and silent logs. Default audit behavior remained enabled. |
| Nub | --ignore-scripts and --prefer-frozen-lockfile. Its one-day release-age policy remained enforced. |
| Deno | --node-modules-dir=auto, without script permission or a release-age override. |
| vlt | Explicit public npm registry and matching npm registry alias, with an isolated cache. Dependency scripts remained blocked. |
| UPM | An isolated store, without a release-age override. Dependency scripts remained disabled. |
| Yarn | The node-modules linker, with scripts and immutable installs disabled. No release-age override. |
Each run includes 18 separate LPM CLI timing diagnostics, excluded from these tables. Nub has separate measurements despite its shared package-manager engine.
Limits of this run
- Different dependency graphs. Deno selected PostCSS 8.5.23. The other measured managers selected 8.5.28. Transitive versions also differ.
- A release-age boundary. Nub paused after three preparation failures while Sharp and an optional native dependency reached one day old. The run resumed with unchanged policies and preserved samples. Nub lockfiles can contain different Sharp versions before and after the pause.
- Timeout interference. The Deno timeout left a child process until manual cleanup. That process adds possible interference to later measurements. The corrected harness handles this case in future runs.
- Background activity. macOS indexing and other services stayed active. Host snapshots do not establish interference for each install.
- Small sample count. Ten attempts describe a median, not a precise tail estimate. Manager positions are not perfectly balanced across ten rounds and nine managers.
- Install checks only. All successful installs contained the 25 direct manifests and resolved
next/package.json. These checks do not establish that the application builds or runs.
An earlier run stopped after a vlt worker-cleanup race. None of its 36 measurements contributes to this page. This run also differs from earlier published benchmarks in manager versions, registry data, dependency graphs, and host activity.