perf: keep dependency source locations lazy when sorting dependencies#21228
Conversation
🦋 Changeset detectedLatest commit: d78a954 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
This PR is packaged and the instant preview is available (95ea1f1). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@95ea1f1
yarn add -D webpack@https://pkg.pr.new/webpack@95ea1f1
pnpm add -D webpack@https://pkg.pr.new/webpack@95ea1f1 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21228 +/- ##
==========================================
- Coverage 92.77% 92.76% -0.02%
==========================================
Files 591 591
Lines 64488 64492 +4
Branches 17920 17926 +6
==========================================
- Hits 59829 59825 -4
- Misses 4659 4667 +8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merging this PR will degrade performance by 40.71%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | benchmark "asset-modules-bytes", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
247.3 KB | 1,045.4 KB | -76.34% |
| ❌ | Memory | benchmark "wasm-modules-sync", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
126.2 KB | 254.2 KB | -50.34% |
| ❌ | Memory | benchmark "wasm-modules-async", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
186.7 KB | 348.1 KB | -46.37% |
| ❌ | Memory | benchmark "many-modules-esm", scenario '{"name":"mode-production","mode":"production"}' |
7.4 MB | 10.4 MB | -28.48% |
| ❌ | Memory | benchmark "css-modules", scenario '{"name":"mode-production","mode":"production"}' |
7.1 MB | 9.1 MB | -22.28% |
| ⚡ | Memory | benchmark "many-chunks-commonjs", scenario '{"name":"mode-production","mode":"production"}' |
9.4 MB | 7.6 MB | +24.04% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing claude/webpack-issue-15643-oy9mgi (d78a954) with main (40b972f)
3efd978 to
9960737
Compare
Add a config case where a side-effect-free node_modules barrel re-exports a used and an unused target; the unused target pulls in a deep subtree guarded by a loader that throws if it runs. Verifies the unused subtree is never factorized, built, or run through loaders when only the other export is imported.
NormalModule sorts each module's dependencies by source location using `compareSelect(d => d.loc, compareLocations)`. `get loc` rebuilds and caches the location object, so this sort materialized and retained a loc object on every dependency (even with source maps off), defeating the lazy-loc memory optimization and allocating during the sort. Add `Dependency.compareLocations`, comparing via the stored numeric loc fields without materializing `loc`, and `setLocWithIndex` to replace the `dep.loc = Object.create(loc); dep.loc.index = i` pattern so the index lives in the serialized field and loc stays lazy.
Add test/configCases/dependency-loc-sort exercising the harmony re-export setLocWithIndex sites and the loc-based dependency sort, asserting both the re-export values and source-order execution of the imported modules.
9960737 to
d78a954
Compare
Summary
Profiling a parse-heavy build showed
NormalModule.handleParseResultsorting each module's dependencies withcompareSelect(d => d.loc, compareLocations).get locrebuilds and caches the location object, so this sort materialized and retained alocobject on every dependency — even with source maps off — which defeats the lazy-loc memory optimization from #21183 and allocates during the sort.This adds
Dependency.compareLocations, which compares via the stored numeric loc fields without materializingloc, andsetLocWithIndex, which replaces thedep.loc = Object.create(loc); dep.loc.index = ipattern (8 sites in the harmony-export and HMR parser plugins) so the index lives in the serialized field andlocstays lazy. Behavior and output are unchanged.Measured on a synthetic 1500-module, parse-heavy build (
mode: "production",devtool: false, no cache), cold runs in separate processes:locobjectThis branch also includes a small regression test (
test/configCases/lazy-barrel/skip-build) asserting a side-effect-free barrel's unused subtree is never built or run through loaders (relates to #15643).What kind of change does this PR introduce?
perf
Did you add tests for your changes?
Yes —
test/configCases/dependency-loc-sortexercises the harmony re-exportsetLocWithIndexsites and the loc-based dependency sort, asserting both the re-export values and source-order execution of the imported modules. The change is otherwise behavior-preserving and covered by the existingStatsTestCases/ConfigTestCasessuites, which I ran with results identical tomain.Does this PR introduce a breaking change?
No.
If relevant, what needs to be documented once your changes are merged or what have you already documented?
n/a
Use of AI
Yes. An AI assistant (Claude) was used to CPU-profile the build, identify the loc-materialization hot path, implement the change, and verify equivalence against the existing test suites; all output was reviewed.
🤖 Generated with Claude Code
https://claude.ai/code/session_016yBjCfSr5bwVz6JsxpW85m