benchmarking

SkillDev tools

Lets your agent run fair install-speed benchmarks comparing the nub package manager against npm, pnpm, or bun.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the benchmarking skill

About this capability

Comparative install-benchmarking methodology for nub vs npm/pnpm/bun — cold/warm protocol, genuine-cold cache isolation, load-robust measurement, and the anti-juicing honesty bar. Invoke (via the Skill tool) whenever you need to benchmark `nub install` against another package manager, produce or upd

What this skill tells your AI

The instructions your AI receives, as published by nubjs/nub in .claude/skills/benchmarking/SKILL.md and read by ahel’s review.

Honest, reproducible comparative install benchmarks of nub install against npm / pnpm / bun — the external, wall-clock-and-file-count method. For decomposing where the time goes INSIDE a single nub install, use pm-perf-tracing instead.

Applies to: benchmarking against another PM, refreshing homepage/blog install numbers, verifying a perf claim before it ships. A single non-genuine cell discredits the whole table.

The tool: hyperfine

/opt/homebrew/bin/hyperfine is the canonical timer. The load-bearing flag is --prepare, which runs setup BEFORE each timed run, untimed. Never time setup.

# COLD: empty the tool's REAL cache + wipe node_modules before each run (untimed), then time the install.
hyperfine --warmup 0 --runs 5 \
  --prepare 'rm -rf node_modules && d="$(bun pm cache)" && rm -rf "${d:?}"' \
  'bun install --ignore-scripts'

# WARM-RELINK: cache populated, wipe ONLY node_modules before each run.
hyperfine --warmup 1 --runs 5 \
  --prepare 'rm -rf node_modules' \
  'nub install --ignore-scripts'

# WARM-SAT: node_modules already present (idempotency path) — no prepare wipe.
hyperfine --warmup 1 --runs 5 'nub install --ignore-scripts'

Prefer median + spread (min–max) over the mean — contention skews the mean.

The cold / warm protocol

node_modules is deleted before EVERY timed run, cold and warm, always in --prepare. The cold/warm axis differs only in global-cache state:

  • cold — the tool's real cache is EMPTY (genuine download).
  • warm-relink — cache populated, node_modules wiped (the link-from-store path; the homepage number).
  • warm-satnode_modules already present; a separate, clearly-labeled scenario, not the headline.

GENUINE-COLD per tool — the cache is on DISK

A "cold" run is only cold if the tool's real on-disk cache is gone. Setting a cache-dir env var does not guarantee that — bun ignores BUN_INSTALL_CACHE_DIR and $HOME, resolving its cache via the OS passwd home. You must wipe the disk path.

toolreal cache pathclear command for cold
nubits store (NUB_CACHE_DIR + XDG_DATA_HOME/XDG_CACHE_HOME)rm -rf "$NUB_CACHE_DIR" "$XDG_DATA_HOME" "$XDG_CACHE_HOME"
npm~/.npm/_cacache (or --cache <dir>)rm -rf <cache> (the --cache dir you pass)
pnpmpnpm store path (or --store-dir <dir>)rm -rf <store> (or pnpm store prune)
bunbun pm cache = the real ~/.bun/install/cacherm -rf "$(bun pm cache)" — env vars do NOT relocate it

VERIFY each cold is genuine — the offline-fails check

After wiping a cache, an --offline install MUST FAIL. A pass means the cache wasn't cleared (wrong disk path), and any "cold" number from it is a warm-link artifact. Run this for every tool before trusting a cold number:

toolexpected after cache wipegenuine?
nubrc≠0, "not available in the local cache"FAIL → genuine
pnpmrc=1, ERR_PNPM_NO_OFFLINE_TARBALLFAIL → genuine
npmrc≠0, cache-miss errorFAIL → genuine
bunif bun install --offline SUCCEEDS → cache NOT cleared → bun cold is NOT genuine

A true bun cold needs a clean container, or wiping the user's real cache (destructive — don't, unless in Docker).

Apples-to-apples isolation

  • Each tool gets its OWN cache dir.
  • --ignore-scripts for ALL tools (or --allow-scripts for all — same on both sides).
  • Identical fixture, identical lockfile throughout (use a --frozen-lockfile/--frozen equivalent where the tool offers one).
  • Interleave tool order round-robin (nub → npm → bun → pnpm, repeat) — never all-of-one-then-the-other — so drift in host load hits every tool equally.

Load discipline — measure only on a quiet machine

  • Check the load average before measuring and only proceed when the machine is quiet. A dedicated quiet box or a CI runner near zero load is ideal for anything that will be published.
  • On a shared dev host, WAIT for load to fall below a threshold — don't measure above it. A practical gate is a 1-minute load average under ~40 (pick a threshold the machine actually reaches; below ~5 is ideal on a dedicated box). Poll, wait, then run; abort and retry if it spikes mid-run.
  • Report the load that held during the run alongside the numbers.
  • File counts and store-entry counts are exact and load-independent — a robust cross-check, and the clearest way to tell the dedup story. They complement a properly-measured time, not replace it.

File-count forensics (the load-independent crux)

find node_modules -type f -o -type l | wc -l          # total materialized entries
ls -d node_modules/**/core-js node_modules/core-js    # physical copies of a duplicated dep (dedup story)

Reach for this first — exact, reproducible, immune to load.

The honesty bar (anti-juicing)

  • VERIFY every cold is genuine (offline-fails check) BEFORE citing it.
  • NEVER compare one tool's genuine-cold to another's warm-link — the exact misleading comparison the offline check exists to prevent.
  • Report what was actually measured, caveats included (which colds are genuine, host load, sample size).
  • The homepage cites the WARM number because it is the honest, reproducible one.
  • A single non-genuine cell discredits the whole table. When in doubt, exclude the cell and say why.

Process hygiene (this runs on the maintainer's machine)

Short-lived install/measurement processes reap themselves. The hazard is a long-lived process a bench starts and forgets:

  • Never leave a dev server running. If a bench starts one, tear it down in the same run (trap '…kill…' EXIT INT TERM).
  • Docker: docker run --rm, and confirm docker ps is empty when done.

Reference template

/tmp/cs-bench-final.sh is a working 4-tool harness (nub / npm / bun / pnpm) — per-tool isolated cache dirs, --ignore-scripts for all, interleaved round-robin order, node_modules wiped before every run, median/spread helpers. Read its structure before hand-rolling a new one; adapt the cache paths and fixture, keep the protocol.

Internal decomposition

When a comparative number raises "WHY is nub's phase X slow?", switch to pm-perf-tracing: RUST_LOG=debug nub install for the phase:resolve/fetch/link split, and the gated AUBE_DIAG_FILE per-file linker strategy tally.

Signals

GitHub stars
4k
Forks
60
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
benchmarking-nubjs
Source
github.com/nubjs/nub