Skip to content

perf(rolldown): reduce stalls on cold session open - #633

Open
webfansplz wants to merge 2 commits into
mainfrom
perf/rolldown-cold-open-stalls
Open

webfansplz wants to merge 2 commits into
mainfrom
perf/rolldown-cold-open-stalls

Conversation

@webfansplz

Copy link
Copy Markdown
Member

Summary

When a Rolldown session is opened for the first time, the DevTools server parses logs.json and then writes the .cache/ sidecars. Writing those sidecars blocked the event loop in large chunks: building the module metrics and plugin summaries ran in one synchronous pass, and each sidecar was serialized with a single JSON.stringify (41 MB of module metrics on a large project). When DevTools runs inside the Vite dev server, this shares the process with Vite, so page loads and HMR stalled too.

This PR keeps the cache output identical and splits that work so the server stays responsive.

Changes

  • Build the cache data in slices. One manager.snapshot() is shared across the sidecars (it was taken three to four times), and module metrics compaction and plugin summaries are built entry by entry, yielding about every 8 ms.
  • Accumulate plugin summaries while parsing. RolldownEventsManager keeps plugin_build_summaries in step with plugin_build_metrics, so writing the plugin summary no longer rescans every call. Summaries are rebuilt on restore and cleared on dispose; metrics hydrated from the index fall back to computing from calls.
  • Serialize sidecars in slices. stringifyInSlices produces the same output as JSON.stringify, but serializes large arrays item by item and yields in between.

Impact

Measured locally on a lobehub SPA build log (~29k modules, 6.9M events, 4 GB), cold open with no cache, 5 runs each:

Before After
Longest event-loop stall 182–675 ms (median 450 ms) 54–157 ms (median 90 ms)
Stalls over 100 ms 1–3 0–1
Cold open total 15.8–17.5 s 16.6–17.8 s (within noise)

The total cold-open time is unchanged; it's dominated by parsing the log.

Copilot AI balanced review requested due to automatic review settings November 6, 2026 09:56
@pkg-pr-new

pkg-pr-new Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@vitejs/devtools

npm i https://pkg.pr.new/@vitejs/devtools@633

@vitejs/devtools-kit

npm i https://pkg.pr.new/@vitejs/devtools-kit@633

@vitejs/devtools-oxc

npm i https://pkg.pr.new/@vitejs/devtools-oxc@633

@vitejs/devtools-rolldown

npm i https://pkg.pr.new/@vitejs/devtools-rolldown@633

@vitejs/devtools-vite

npm i https://pkg.pr.new/@vitejs/devtools-vite@633

@vitejs/devtools-vitest

npm i https://pkg.pr.new/@vitejs/devtools-vitest@633

commit: 7c6cd8b

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Active issues cause excessive small filesystem writes that slow cold opens and incomplete metric summaries that omit hydrated counts and durations when logs grow.

2 open issues
What changed in this PR

This PR reduces event-loop stalls when opening uncached Rolldown sessions, helping keep the shared Vite dev-server process responsive during cache generation.

Changes:

  • Reuses one manager snapshot and builds cache summaries in slices.
  • Accumulates plugin summaries during parsing and rebuilds them on restore.
  • Adds sliced JSON serialization and tests for output equivalence and summary consistency.
File Description
packages/​rolldown/​src/​node/​rolldown/​log-cache.ts Shares snapshots and slices cache preparation and serialization.
packages/​rolldown/​src/​node/​rolldown/​events-manager.ts Tracks incremental plugin summaries through parsing, restore, and disposal.
packages/​rolldown/​src/​node/​rolldown/​__tests__/​cold-open-cache.test.ts Tests plugin summary consistency and serialized JSON equivalence.

Review effort: Balanced

Comment on lines +188 to +189
summary = createEmptyPluginBuildMetricsSummary(pluginId, pluginName)
this.plugin_build_summaries.set(pluginId, summary)
const temporaryFilepath = `${this.filepath}.tmp`
await fs.mkdir(dirname(this.filepath), { recursive: true })
await fs.writeFile(temporaryFilepath, JSON.stringify(this.createFile(stat, data)), 'utf8')
await fs.writeFile(temporaryFilepath, await stringifyInSlices(this.createFile(stat, data)), 'utf8')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants