Skip to content

perf: release inner-graph state, fast-path inlined-export checks and skip inlined entry re-parse#21167

Merged
alexander-akait merged 4 commits into
mainfrom
perf/inner-graph-release-and-concat-fast-paths
Jun 11, 2026
Merged

perf: release inner-graph state, fast-path inlined-export checks and skip inlined entry re-parse#21167
alexander-akait merged 4 commits into
mainfrom
perf/inner-graph-release-and-concat-fast-paths

Conversation

@alexander-akait

Copy link
Copy Markdown
Member

Summary

Profiling a 7k-module three.js production build against v5.107.2 showed regressed memory (+34 MB retained heap) and large per-build costs: inner-graph state was retained for the whole compilation, every harmony import connection became conditional with an allocating inline-export check, and avoidEntryIife re-parsed the entire rendered entry (the whole concatenated app) on every build. This PR releases the inner-graph state after inference, adds allocation-free fast paths for the inlined-export checks, skips the entry re-parse when codegen-reported top-level declarations show no collision, and trims allocations in export hashing and concatenation lookups. On the benchmark: initial build ~14.0s → ~8.8s, watch rebuild ~8.9s → ~3.4s, retained heap 347 MB → 303 MB, with byte-identical output.

What kind of change does this PR introduce?

perf

Did you add tests for your changes?

No new tests; behavior is unchanged and covered by the existing suites (ConfigTestCases, StatsTestCases, TestCasesProduction/Development, inline-exports, inner-graph, side-effects all pass).

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

This PR was developed with Claude Code (AI agent): it profiled the builds, located the regressions, wrote the patches and benchmarks, and ran the test suites; the work was directed and reviewed by the requester.


Generated by Claude Code

…export checks

- Release per-module InnerGraph state once inferDependencyUsage has fired
  its callbacks; the state (two Maps plus Sets of closures capturing AST
  nodes per module) was retained for the whole compilation lifetime.
- Add ExportsInfo.hasInlinedUsedName as an allocation-free check used by
  isExportInlined on the connection-condition hot path, with a per-
  ExportsInfo flag so modules without inlined exports answer with a
  single property read.
- Keep HarmonyImportSpecifierDependency connections unconditional when
  usedByExports is true, no branch guards exist and inlineExports is
  disabled, restoring the fast path for development and cached builds.

https://claude.ai/code/session_01Ab1MSNJvUMuuesmS81226v
…eded

avoidEntryIife re-parsed and scope-analyzed every chunk module during
renderMain, including the inlined entry, which for a concatenated entry
is the whole application source. Codegen already reports the rendered
entry's top-level declarations, so for a single inlined entry the
collision check runs against that set and the parse only happens when a
collision actually requires renaming.

https://claude.ai/code/session_01Ab1MSNJvUMuuesmS81226v
- ExportsInfo._updateHash tracks the visited path with add/delete on a
  shared set instead of copying it on every level.
- getPathInAst binary-searches ordered sibling ranges instead of a
  linear scan per lookup.
- getUsedNamesInScopeInfo uses nested maps instead of building a
  string key per lookup.

https://claude.ai/code/session_01Ab1MSNJvUMuuesmS81226v
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 187d2fd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack Patch

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

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

This PR is packaged and the instant preview is available (309ff77).

Install it locally:

  • npm
npm i -D webpack@https://pkg.pr.new/webpack@309ff77
  • yarn
yarn add -D webpack@https://pkg.pr.new/webpack@309ff77
  • pnpm
pnpm add -D webpack@https://pkg.pr.new/webpack@309ff77

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.59%. Comparing base (6e52971) to head (187d2fd).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #21167   +/-   ##
=======================================
  Coverage   92.58%   92.59%           
=======================================
  Files         587      587           
  Lines       63666    63750   +84     
  Branches    17636    17664   +28     
=======================================
+ Hits        58946    59027   +81     
- Misses       4720     4723    +3     
Flag Coverage Δ
css-parsing 28.70% <13.46%> (-0.03%) ⬇️
html5lib 31.05% <14.42%> (-0.03%) ⬇️
integration 88.63% <100.00%> (+0.01%) ⬆️
test262 45.47% <95.19%> (+0.11%) ⬆️
unit 40.30% <13.46%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented Jun 11, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 57.08%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "lodash", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 130 KB 858.8 KB -84.86%
Memory benchmark "many-modules-esm", scenario '{"name":"mode-production","mode":"production"}' 7.8 MB 11.4 MB -31.37%
Memory benchmark "many-modules-commonjs", scenario '{"name":"mode-production","mode":"production"}' 7.4 MB 9.7 MB -23.88%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/inner-graph-release-and-concat-fast-paths (187d2fd) with main (6e52971)

Open in CodSpeed

@github-actions

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging perf/inner-graph-release-and-concat-fast-paths into main will be
99.35%
Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
bin
   webpack.js98.77%100%100%98.77%91
examples
   build-common.js100%100%100%100%
   buildAll.js100%100%100%100%
   examples.js100%100%100%100%
   template-common.js98.21%100%100%98.21%72
examples/custom-javascript-parser
   test.filter.js100%100%100%100%
examples/custom-javascript-parser/internals
   acorn-parse.js100%100%100%100%
   meriyah-parse.js100%100%100%100%
   oxc-parse.js91.30%100%100%91.30%140, 142–143, 145, 147, 153–154, 161, 168, 90
examples/markdown
   webpack.config.mjs100%100%100%100%
examples/typescript
   test.filter.js100%100%100%100%
examples/typescript-non-erasable
   test.filter.js50%100%100%50%5
examples/virtual-modules
   test.filter.js100%100%100%100%
examples/wasm-bindgen-esm
   test.filter.js100%100%100%100%
examples/wasm-complex
   test.filter.js100%100%100%100%
examples/wasm-simple
   test.filter.js100%100%100%100%
examples/wasm-simple-source-phase
   test.filter.js100%100%100%100%
lib
   APIPlugin.js100%100%100%100%
   AsyncDependenciesBlock.js100%100%100%100%
   AutomaticPrefetchPlugin.js100%100%100%100%
   BannerPlugin.js100%100%100%100%
   Cache.js98.21%100%100%98.21%101
   CacheFacade.js100%100%100%100%
   Chunk.js99.72%100%100%99.72%39
   ChunkGraph.js100%100%100%100%
   ChunkGroup.js100%100%100%100%
   ChunkTemplate.js100%100%100%100%
   CleanPlugin.js99.15%100%100%99.15%206, 226
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.44%100%100%98.44%1609, 1905, 1912, 1920, 1942, 2838, 3317–3318, 3350, 4014, 4044, 4097–4098, 4102, 4107, 4123–4124, 4138–4139, 4144–4145, 4622, 4648, 513, 518, 5456, 5488, 5505, 5521, 5537, 5552, 5577–5578, 5580, 5908, 5913, 5919, 5922, 5934, 5936, 5940, 5956, 5971, 6003, 6057, 6081, 6195, 763–764
   Compiler.js99.56%100%100%99.56%1135–1136, 1144
   ConcatenationScope.js98.59%100%100%98.59%189
   ConditionalInitFragment.js100%100%100%100%
   ConstPlugin.js100%100%100%100%
   ContextExclusionPlugin.js100%100%100%100%
   ContextModule.js100%100%100%100%
   ContextModuleFactory.js97.40%100%100%97.40%258, 395, 418, 420, 424, 433–434
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js99%100%100%99%170–171, 187, 206, 280
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.15%100%100%98.15%379, 425
   DependencyTemplate.js100%100%100%100%
   DependencyTemplates.js100%100%100%100%
   DotenvPlugin.js98.41%100%100%98.41%378, 391–392
   DynamicEntryPlugin.js100%100%100%100%
   EntryOptionPlugin.js100%100%100%100%
   EntryPlugin.js100%100%100%100%
   Entrypoint.js100%100%100%100%
   EnvironmentPlugin.js97.14%100%100%97.14%49
   ErrorHelpers.js100%100%100%100%
   EvalDevToolModulePlugin.js100%100%100%100%
   EvalSourceMapDevToolPlugin.js100%100%100%100%
   ExportsInfo.js100%100%100%100%
   ExportsInfoApiPlugin.js100%100%100%100%
   ExternalModule.js98.97%100%100%98.97%425–429, 577
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.50%100%100%99.50%182, 2252–2253, 2256, 2267, 2278, 2289, 278, 3693, 3708, 3732
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.85%100%100%98.85%434, 436, 440
   FlagDependencyUsagePlugin.js100%100%100%100%
   FlagEntryExportAsUsedPlugin.js100%100%100%100%
   Generator.js100%100%100%100%
   HotModuleReplacementPlugin.js100%100%100%100%
   HotUpdateChunk.js100%100%100%100%
   IgnorePlugin.js100%100%100%100%
   IgnoreWarningsPlugin.js100%100%100%100%
   InitFragment.js100%100%100%100%
   JavascriptMetaInfoPlugin.js100%100%100%100%
   LibraryTemplatePlugin.js100%100%100%100%
   LoaderOptionsPlugin.js100%100%100%100%
   LoaderTargetPlugin.js100%100%100%100%
   MainTemplate.js100%100%100%100%
   ManifestPlugin.js100%100%100%100%
   Module.js98.50%100%100%98.50%1311, 1316, 1376, 1390, 1452, 1461
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%106, 108
   ModuleGraph.js99.73%100%100%99.73%1005
   ModuleGraphConnection.js100%100%100%100%
   ModuleInfoHeaderPlugin.js100%100%100%100%
   ModuleNotFoundError.js100%100%100%100%
   ModuleProfile.js100%100%100%100%
   ModuleSourceTypeConstants.js100%100%100%100%
   ModuleTemplate.js100%100%100%100%
   ModuleTypeConstants.js100%100%100%100%
   MultiCompiler.js99.69%100%100%99.69%659
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.90%100%100%97.90%1219, 1222, 1239, 1256, 1503, 1537, 1553, 1640, 1994, 2293, 2298–2308, 417, 421, 575
   NormalModuleFactory.js99.47%100%100%99.47%1083, 1392, 486, 498
   NormalModuleReplacementPlugin.js100%100%100%100%
   NullFactory.js100%100%100%100%
   OptimizationStages.js100%100%100%100%
   OptionsApply.js100%100%100%100%
   Parser.js100%100%100%100%
   PlatformPlugin.js100%100%100%100%
   PrefetchPlugin.js100%100%100%100%
   ProgressPlugin.js98.85%100%100%98.85%519–520, 525, 527, 591
   ProvidePlugin.js100%100%100%100%
   RawModule.js100%100%100%100%
   RecordIdsPlugin.js100%100%100%100%
   RequestShortener.js100%100%100%100%
   ResolverFactory.js100%100%100%100%
   RuntimeGlobals.js100%100%100%100%
   RuntimeModule.js100%100%100%100%
   RuntimePlugin.js100%100%100%100%
   RuntimeTemplate.js100%100%100%100%
   SelfModuleFactory.js100%100%100%100%
   SingleEntryPlugin.js100%100%100%100%
   SourceMapDevToolModuleOptionsPlugin.js100%100%100%100%
   SourceMapDevToolPlugin.js98.62%100%100%98.62%220, 224, 226, 419, 430, 891
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js99.17%100%100%99.17%176–177
   UseStrictPlugin.js100%100%100%100%
   WarnCaseSensitiveModulesPlugin.js100%100%100%100%
   WarnDeprecatedOptionPlugin.js100%100%100%100%
   WarnNoModeSetPlugin.js100%100%100%100%
   WatchIgnorePlugin.js100%100%100%100%
   Watching.js100%100%100%100%
   WebpackError.js100%100%100%100%
   WebpackIsIncludedPlugin.js100%100%100%100%
   WebpackOptionsApply.js100%100%100%100%
   WebpackOptionsDefaulter.js100%100%100%100%
   buildChunkGraph.js99.87%100%100%99.87%371
   cli.js98.62%100%100%98.62%10, 119, 545, 577, 627, 897
   index.js99.72%100%100%99.72%165
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js96.33%100%100%96.33%10, 198, 220, 222
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModule.js100%100%100%100%
   AssetModulesPlugin.js97.31%100%100%97.31%279, 303, 306, 358, 36, 41
   AssetParser.js100%100%100%100%
   AssetSourceGenerator.js100%100%100%100%
   AssetSourceParser.js100%100%100%100%
   RawDataUrlModule.js100%100%100%100%
lib/async-modules
   AsyncModuleHelpers.js100%100%100%100%
   AwaitDependenciesInitFragment.js100%100%100%100%
   InferAsyncModulesPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%71, 83, 91
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%106, 113–114, 122, 89
   PackFileCacheStrategy.js96.40%100%100%96.40%1250, 1350, 1354, 1416, 628, 647, 657–659, 661, 677–678, 683, 686, 688, 693, 698, 722, 728, 762, 768, 774, 779, 790, 799, 804–805, 807, 824, 830–831, 833
   ResolverCachePlugin.js100%100%100%100%
   getLazyHashedEtag.js100%100%100%100%
   mergeEtags.js100%100%100%100%
lib/config
   browserslistTargetHandler.js100%100%100%100%
   defaults.js99.30%100%100%99.30%1439–1441, 1449, 274, 277, 282, 286
   normalization.js99.01%100%100%99.01%191–192, 258, 273
   target.js100%100%100%100%
lib/container
   ContainerEntryDependency.js100%100%100%100%
   ContainerEntryModule.js100%100%100%100%
   ContainerEntryModuleFactory.js100%100%100%100%
   ContainerExposedDependency.js100%100%100%100%
   

Copy link
Copy Markdown
Member Author

Regarding the two CodSpeed memory regressions: I verified many-modules-commonjs (mode-production) locally against the actual merge base (6e52971) with 3 interleaved runs each — HEAD uses slightly less heap (27.72 MB vs 27.90 MB), with identical build time and peak RSS. The CodSpeed report itself notes it compared across different runtime environments and used 7917dd1 instead of the merge base, and the same dev-rebuild scenario family shows ×6.8 (lodash) and ×4.1 (asset-modules-inline) improvements, so the flagged deltas appear to be measurement noise rather than real regressions.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Follow-up on the updated CodSpeed report (now claiming 3 memory regressions): I re-verified all three locally against the exact base (6e52971) using a sampling heap profiler measuring total allocations (CodSpeed's Memory mode metric), 3 interleaved runs per side:

  • many-modules-commonjs (production): base 25.1–25.7 MB vs head 24.0–24.5 MB — head allocates ~1 MB less.
  • many-modules-esm (production): base 26.1–26.6 MB vs head 25.7–26.5 MB — parity.
  • lodash (dev-rebuild): CodSpeed's own two runs reported the same pair of values (~130 KB / ~860 KB) in opposite directions (first run: 859→127 KB as a ×6.8 improvement, second run: 130→859 KB as a −85% regression) — a bimodal measurement, and both reports carry the "different runtime environments" warning.

Retained heap and peak RSS were also checked earlier with the same result. I believe all three flags are measurement instability and can be acknowledged on CodSpeed.


Generated by Claude Code

@alexander-akait alexander-akait merged commit 309ff77 into main Jun 11, 2026
66 checks passed
@alexander-akait alexander-akait deleted the perf/inner-graph-release-and-concat-fast-paths branch June 11, 2026 13:02
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.

1 participant