Skip to content

perf(pacquet/install): profile residual babylon gap on clean/lockfile #11995

Description

@zkochan

Problem

On the 2026-05-27 benchmarks.vlt.sh chart (pacquet 0.2.12 vs pnpm 11.3.0), pacquet is at or above parity with pnpm on the babylon fixture in three full-install variations:

Variation pacquet (s) pnpm pacquet ÷ pnpm
clean 33.775 29.675 1.14×
lockfile 24.933 22.250 1.12×
cache+lockfile 10.160 10.403 0.98×

clean and lockfile are slight regressions; cache+lockfile is parity. Adjacent babylon cells (cache = 0.51×, node_modules = 0.59×, cache+node_modules = 0.31×) show that pacquet has substantial headroom over pnpm on this fixture — the regression is specific to the cold-cache and lockfile-driven install paths.

There's also a next × lockfile = 1.11× regression that may be the same shape but on a smaller, wider tree rather than a deeper monorepo.

Variation shapes

  • clean: no cache, no lockfile, no node_modules — fresh resolve + download + link.
  • lockfile: no cache, lockfile present, no node_modules — skip resolve, download + link.
  • cache+lockfile: cache + lockfile present, no node_modules — skip resolve, skip download, link only.

The cache+lockfile cell is at parity, so the gap is not in linking. The clean regression is the resolve path; the lockfile regression is the download path (and may also overlap with #11994's bun-gap diagnosis).

What babylon is

Workspace monorepo with 87 importers (@dev/build-tools and friends) — the worst-case for any per-importer scaling in pacquet's resolution or hoisting.

The babylon clean profile is the most informative: 33.8 s gives plenty of frames, and pnpm at 29.7 s gives a direct reference for "what fraction of each phase pacquet spends extra time on."

Suggested approach

Single profiling pass against babylon × clean. The cheapest framing: install the published pacquet 0.2.12 and pnpm 11.3.0 binaries side-by-side, run with samply record -- pacquet install against a fresh clone of babel/babel, then the same with pnpm under node --prof. Compare flame graphs phase-by-phase.

Expected findings (worth ruling out before deep profiling):

  • Per-importer overhead. 87 importers × any O(importers) routine adds up. Candidates: workspace state file construction, project registry writes, manifest re-reads.
  • Resolver fan-out shape. Babylon's dependency graph has many shared transitive deps; pacquet's try_join_all over siblings might be doing redundant work that pnpm's Promise.all happens to dedupe via the per-resolution lock.
  • Workspace-link resolution. fix(pacquet): port pnpm's workspace-link short-circuit and depPath helpers #11944 fixed the link-depPath panic for workspace nodes; the residual perf cost of routing every workspace link through the same code path is unprofiled.

Scope

babylon × {clean, lockfile, cache+lockfile} and next × lockfile. Single profiling-driven follow-up; concrete fixes get separate PRs.

Tracker: #11902


Written by an agent (Claude Code, claude-opus-4-7).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions