Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions benchmarkFixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,43 @@ export default async function benchmark (pm, fixture, opts) {
size = await getFolderSize.loose(path.join(cwd, 'cache'))
}

console.log('# re-warm the cache for the update scenario (not measured)')

// The rows above drained the cache twice, and what each manager holds by
// now depends on how it behaved on the rows in between — pnpm restores a
// warm `node_modules` from the lockfile copy it keeps inside it, touching
// the registry not at all, while Bun re-downloads the whole graph on the
// same row. Left like that, the update row hands Bun a cache its previous
// row just filled and pnpm a cold one, and what gets measured is the
// wipe's echo rather than the update. A developer who bumps versions has
// the cache their installs left, so every manager gets the base graph
// re-warmed here, untimed: a from-scratch resolve-and-fetch in a throwaway
// copy of the project whose `cache/` is a symlink into the real one. No
// lockfile and no `node_modules` go with it, or the fast managers would
// short-circuit and warm nothing, which is how the imbalance arose in the
// first place.
const rewarmDir = path.join(cwd, '.rewarm')
rimraf.sync(rewarmDir)
await fs.mkdir(rewarmDir, { recursive: true })
try {
cpSync(path.join(cwd, 'package.json'), path.join(rewarmDir, 'package.json'))
for (const name of ['.npmrc', 'pnpm-workspace.yaml', '.yarnrc.yml', '.yarnrc']) {
try {
cpSync(path.join(cwd, name), path.join(rewarmDir, name))
} catch (err) {
// A file a manager doesn't use was never written; nothing to copy.
// Anything else has to surface: a warm-up that silently lost its
// `.npmrc` would run against the public registry and hand the update
// row a cache warmed over the wrong network.
if (err?.code !== 'ENOENT') throw err
}
}
await fs.symlink(path.join(cwd, 'cache'), path.join(rewarmDir, 'cache'), 'dir')
measureInstall(pm, rewarmDir, env)
} finally {
rimraf.sync(rewarmDir)
}

console.log('# with updated dependencies')

// update all dependency versions to '*' and install again
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const explanationByTest = {
withWarmCacheAndLockfile: '`cache+trusted lockfile`: a developer reinstalling a known project.',
withWarmModulesAndLockfile: '`trusted lockfile+node_modules`: the cache is deleted and install is run again.',
repeatInstall: '`cache+trusted lockfile+node_modules`: re-running install when nothing has changed.',
updatedDependencies: '`update`: dependency versions are bumped in `package.json` and install is run again.',
updatedDependencies: '`update`: dependency versions are bumped in `package.json` and install is run again, from a warm cache.',
}

// Sort tests by descending time on the first PM (npm) — slowest first.
Expand Down Expand Up @@ -384,6 +384,7 @@ async function writePage ({ formattedNow, registryVersion, sections, svgs, sorte
- **Server-side resolution pays off when there is a graph to resolve.** Resolving one means walking it level by level, and each level costs a round trip, so the cost is roughly the depth of the graph times the latency. pnpr does that walk next to the registry — its own metadata access stays on loopback, the co-located shape the [pnpm monorepo's integrated benchmark](https://github.com/pnpm/pnpm) measures — and answers with the whole resolved lockfile at once, which is why the rows without a lockfile, and the row that changes dependencies, are the ones where it pulls ahead of plain pnpm.
- **The lockfile is trusted, so every manager is asked for the same work.** pnpm verifies a lockfile against the registry before installing it — a supply-chain pass that costs a packument per package, and one no other manager here performs. The rows with a lockfile run every pnpm column with [\`trustLockfile\`](https://pnpm.io/settings#trustlockfile), so what they compare is the install rather than a safety check only one participant was asked for. It is on by default outside this benchmark, and pnpm's own resolution still applies its release-age policy on the rows that resolve.
- **With an up-to-date lockfile there is nothing to resolve.** pnpm doesn't ask the server then, so those rows measure the same install in both pnpm 12 columns.
- **The update row starts from a warm cache.** The rows before it delete the cache twice, and how much of it a manager has rebuilt by the time update runs is an accident of row ordering — one manager's registry-free reuse of a warm \`node_modules\` left it cold on exactly the row where another's full re-download had just re-warmed itself. A developer who bumps versions has the cache their installs left, so before the update is timed, every manager re-fetches the base graph once, untimed.
- Tarballs are still fetched by the client, in parallel and directly, on every row.
`

Expand Down
180 changes: 0 additions & 180 deletions results/bun/1.3.14/alotta-files-pnpr.yaml

This file was deleted.

Loading