Skip to content

perf(cli): reduce repeated config resolution and startup imports - #2782

Draft
fengmk2 wants to merge 1 commit into
bench/config-performancefrom
perf/config-resolution
Draft

fengmk2 wants to merge 1 commit into
bench/config-performancefrom
perf/config-resolution

Conversation

@fengmk2

@fengmk2 fengmk2 commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

From a workspace package, vp check --fix evaluates the Vite config seven times. Reuse its resolved config when selecting the config for each tool phase. This reduces evaluations to four and limits reuse to one command invocation.

Load Vite and Vitest only when config resolution is necessary. Return empty metadata when the workspace has no Vite config. Existing config files still use the full Vite resolver, including config functions and plugin hooks.

Reduce the benchmark limit to one config evaluation per process.

Single-run comparison with time

Before: a07ad93b9. After: 76ce17ced. Each case ran once per version, without warmup. Both versions used debug native bindings on Apple M4 Max, macOS 26.2, and Node v22.23.2.

The fixture contains three unformatted TypeScript files in packages/app/src. Config cases use export default { lint: {}, fmt: {} };. Each invocation starts with identical input files and an empty NODE_COMPILE_CACHE directory.

The before and after columns show real / user / sys from /usr/bin/time -p, in seconds.

Case and CLI arguments Before (s) After (s) Real-time change
Root, no config: check --fix 0.72 / 0.37 / 0.12 0.54 / 0.30 / 0.09 -25.0%
Root, with config: check --fix 0.73 / 0.57 / 0.15 0.70 / 0.55 / 0.14 -4.1%
Root, with config: fmt 0.38 / 0.31 / 0.07 0.30 / 0.23 / 0.05 -21.1%
Root, with config: lint --fix 0.33 / 0.30 / 0.07 0.25 / 0.24 / 0.05 -24.2%
Package, with config: check --fix 0.74 / 0.59 / 0.16 0.71 / 0.57 / 0.15 -4.1%

Example invocation from the fixture root:

NODE_COMPILE_CACHE="$(mktemp -d)" /usr/bin/time -p \
  node "$CHECKOUT/packages/cli/bin/vp" fmt packages/app/src/*.ts

Set $CHECKOUT to the before or after worktree. Point the fixture's node_modules/vite-plus link to that worktree's packages/cli. Reset the input files before each invocation. Package cases run from packages/app with src/*.ts as the file arguments.

Stacked on #2779. Related to #2698.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://perf-config-resolution-viteplus-dev.voidzero-docs.workers.dev (commit 76ce17c)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://9c4b659c-viteplus-dev.voidzero-docs.workers.dev 76ce17c 2026-09-22T09:27:49.479Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://b327d77d-viteplus-dev.voidzero-docs.workers.dev 7fe3395 2026-09-22T08:54:16.036Z Visit the dashboard ↗

@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

CLI artifact sizes (76ce17c)

Final release artifacts built by the canonical build-upstream and build-windows-cli actions.
The dist rows use the Linux build. The core total excludes .node files to match the release artifact.

Artifact Format Base PR Change
packages/cli/dist Directory total 2.12 MiB 2.12 MiB +125 B (+0.01%)
packages/core/dist Directory total 3.95 MiB 3.95 MiB 0 B (0.00%)
Combined package dist Directory total 6.07 MiB 6.07 MiB +125 B (+0.00%)
vp (Linux x64) Binary 11.28 MiB 11.28 MiB 0 B (0.00%)
vp (Linux x64) gzip -9 4.88 MiB 4.88 MiB 0 B (0.00%)
NAPI (Linux x64) Binary 32.11 MiB 32.11 MiB 0 B (0.00%)
NAPI (Linux x64) gzip -9 12.74 MiB 12.74 MiB +691 B (+0.01%)
vp (macOS ARM64) Binary 8.41 MiB 8.41 MiB 0 B (0.00%)
vp (macOS ARM64) gzip -9 4.26 MiB 4.26 MiB 0 B (0.00%)
NAPI (macOS ARM64) Binary 39.72 MiB 39.72 MiB 0 B (0.00%)
NAPI (macOS ARM64) gzip -9 17.04 MiB 17.04 MiB +158 B (+0.00%)
vp (Windows x64) Binary 9.16 MiB 9.16 MiB 0 B (0.00%)
vp (Windows x64) gzip -9 4.00 MiB 4.00 MiB +2 B (+0.00%)
NAPI (Windows x64) Binary 27.07 MiB 27.07 MiB +1.00 KiB (+0.00%)
NAPI (Windows x64) gzip -9 10.84 MiB 10.84 MiB +210 B (+0.00%)
Trampoline (Windows x64) Binary 13.50 KiB 13.50 KiB 0 B (0.00%)
Trampoline (Windows x64) gzip -9 7.09 KiB 7.09 KiB 0 B (0.00%)
Installer (Windows x64) Binary 4.56 MiB 4.56 MiB 0 B (0.00%)
Installer (Windows x64) gzip -9 2.13 MiB 2.13 MiB 0 B (0.00%)

@fengmk2
fengmk2 added this pull request to stack #2783 September 22, 2026 09:13
@fengmk2
fengmk2 force-pushed the perf/config-resolution branch from 7fe3395 to 76ce17c Compare September 22, 2026 09:26
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark run for PR head 76ce17ced5f46d54ac16ad0627e714d0a8270bf5.

4 case(s) changed by more than ±5% in median time. Negative changes are faster; positive changes are slower.

Config performance

Baseline revision: 5bab3cf10565cfd983993348f67e05a1d5abf282.

Current revision: daef3076e332d6f0074b2484be29cecc612d9b34. Node v22.18.0; linux/x64; AMD EPYC.

Case Median (ms), baseline → current p95 (ms), baseline → current Median change Config evaluations (current, separate probe)
root/check/no-config 280.5 → 223.0 304.6 → 244.0 -57.6 ms (-20.5%) 0
root/check/minimal 484.4 → 472.5 491.4 → 557.1 -11.9 ms (-2.4%) check: 1, oxfmt: 1, oxlint: 1, oxfmt: 1
root/check/defineConfig 491.5 → 479.5 505.0 → 730.7 -12.0 ms (-2.4%) check: 1, oxfmt: 1, oxlint: 1, oxfmt: 1
package/check/minimal 503.4 → 472.7 510.6 → 507.1 -30.7 ms (-6.1%) check: 1, oxfmt: 1, oxlint: 1, oxfmt: 1
package/check/blocks 497.0 → 476.3 506.9 → 537.0 -20.6 ms (-4.2%) check: 1, oxfmt: 1, oxlint: 1, oxfmt: 1
root/fmt/minimal 222.2 → 167.3 226.9 → 195.8 -54.9 ms (-24.7%) oxfmt: 1
root/lint/minimal 189.2 → 138.3 206.2 → 158.4 -50.9 ms (-26.9%) oxlint: 1
root/staged/minimal 625.8 → 609.8 652.8 → 680.2 -15.9 ms (-2.5%) staged: 1, check: 1, oxfmt: 1, oxlint: 1, oxfmt: 1
root/staged/noop 151.1 → 146.3 164.7 → 170.0 -4.8 ms (-3.2%) staged: 1

Fresh processes, warm filesystem and Node compile caches. Config logging is disabled during timing.
A timing regression requires >20% and >40 ms median growth, with current p25 above baseline p75.

@liangmiQwQ liangmiQwQ left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@fengmk2 Thanks for your working on that. Just wanna notice: according to my profile and a contributor's analyze, the main cost of a vp check run is actually vite-plus package's import for every single Oxlint & Oxfmt child process (70%~80%, about 200ms in reality), analyzing and loading Vite+ config itself only takes about 20% for now. Improving config resolver is good but cannot completely solve the biggest performance cost.

I suggest a possible solution: instead of calling Oxlint / Oxfmt as child process, we can load them in the same Node.js process (using ESM import), the JS runtime should reuse the first import automatically.

Alternatively, we can change Oxlint / Oxfmt's behavior, to stop forcing import vite-plus package, and let Vite's config loader remove defineConfig macro import. This approach is a bit more complicated.

This is just some personal ideas, haven't been tested or verified. However, if our goal is to just improve that as soon as possible, we can treat these stacks as a incremental improvements.

Profiling: https://github.com/liang-demos/vite-plus-config-import-bench#readme

@fengmk2

fengmk2 commented Sep 23, 2026

Copy link
Copy Markdown
Member Author

@liangmiQwQ I was only trying to conduct some experiments; the data results were not ideal, so I have stopped.

This branch has not been deployed

No deployments
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