Skip to content

perf(@angular/build): read rendered module length once per module in chunk optimizer - #34045

Merged
clydin merged 3 commits into
angular:mainfrom
roman-garcia:chunk-optimizer-rendered-length
Sep 8, 2026
Merged

perf(@angular/build): read rendered module length once per module in chunk optimizer#34045
clydin merged 3 commits into
angular:mainfrom
roman-garcia:chunk-optimizer-rendered-length

Conversation

@roman-garcia

Copy link
Copy Markdown
Contributor

PR Checklist

Please check to confirm your PR fulfills the following requirements:

No test added: the change is a pure performance fix with no behavioral difference. The generated metafile is byte-identical before and after (verified by replaying optimizeChunks on a dumped esbuild result and comparing the serialized metafile and initialFiles). Happy to add a spec if you can point me at a suitable harness for chunk-optimizer.

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe: performance

What is the current behavior?

Issue Number: #34044

In bundleOutputToEsbuildMetafile, renderedModule.renderedLength is read inside the loop over each module's original inputs. On rolldown's rendered module object that property is a lazy getter (bindingRenderedModule.code?.length) which transfers the entire module code from native memory on every access. Because each "module" in the chunk optimizer is a whole esbuild output chunk, a 17 MB main chunk with 3,588 inputs performs 3,588 copies of a 17 MB string (~62 GB). On the application in the linked issue this is ~157 s out of a ~161 s OPTIMIZE_CHUNKS phase, while the rolldown bundling itself takes ~3 s. Rolldown also emits a misleading [PLUGIN_TIMINGS] warning about the angular-bundle plugin as a side effect.

What is the new behavior?

The rendered length is read once per module before iterating its inputs. Measured on the same application and machine (production configuration, sourceMap: true):

Before After
DURATION[OPTIMIZE_CHUNKS] 179 s 4 s
Total ng build 234 s 55 s

Output files are byte-identical (same file list, same SHA-256 for every JS and CSS file).

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

A follow-up on the rolldown side could cache code in transformToRenderedModule, but this hoist removes the cost regardless of the rolldown version. The [PLUGIN_TIMINGS] warning still fires after the fix because rolldown compares plugin time against a near-zero link stage when the inputs are a few hundred pre-bundled chunks; passing checks: { pluginTimings: false } to rolldown() in the optimizer would silence it, but that is left out of this PR.

🤖 Generated with Claude Code

…chunk optimizer

When converting the chunk optimizer output into an esbuild-compatible metafile, the
rendered length of each module was read inside the loop over the module's original
inputs. The `renderedLength` property of a rolldown rendered module is a lazy getter
that transfers the full module code from native memory on every access. Since each
module in this pass is an entire esbuild output chunk, a main chunk of 17 MB with
3,588 inputs resulted in roughly 62 GB of string copies and around 150 seconds spent in
`bundleOutputToEsbuildMetafile`, while the rolldown bundling itself took 3 seconds.

Read the rendered length once per module before iterating its inputs. The generated
metafile is unchanged. On the affected application the `OPTIMIZE_CHUNKS` phase drops
from 179 seconds to 4 seconds and the total production build from 234 to 55 seconds.

Fixes angular#34044
@google-cla

google-cla Bot commented Sep 8, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@angular-robot angular-robot Bot added area: performance Issues related to performance area: @angular/build labels Sep 8, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request optimizes the chunk optimizer by caching the renderedModule.renderedLength value in a local variable before entering the loop. This avoids repeatedly accessing the lazy getter, which triggers expensive native memory transfers of the entire module code on each iteration. I have no feedback to provide as there are no review comments.

Removed comments explaining the performance impact of accessing the rendered length in the chunk optimizer.
Comment thread packages/angular/build/src/builders/application/chunk-optimizer.ts Outdated
@clydin clydin added the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Sep 8, 2026
…r.ts

Co-authored-by: Charles <19598772+clydin@users.noreply.github.com>
@alan-agius4 alan-agius4 added merge: squash commits When the PR is merged, a squash and merge should be performed and removed action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews labels Sep 8, 2026
@clydin clydin added the target: patch This PR is targeted for the next patch release label Sep 8, 2026
@alan-agius4 alan-agius4 added the action: merge The PR is ready for merge by the caretaker label Sep 8, 2026
@clydin
clydin merged commit f1afa60 into angular:main Sep 8, 2026
41 of 42 checks passed
@clydin

clydin commented Sep 8, 2026

Copy link
Copy Markdown
Member

This PR was merged into the repository. The changes were merged into the following branches:

clydin added a commit that referenced this pull request Sep 8, 2026
…chunk optimizer (#34045)

* perf(@angular/build): read rendered module length once per module in chunk optimizer

When converting the chunk optimizer output into an esbuild-compatible metafile, the
rendered length of each module was read inside the loop over the module's original
inputs. The `renderedLength` property of a rolldown rendered module is a lazy getter
that transfers the full module code from native memory on every access. Since each
module in this pass is an entire esbuild output chunk, a main chunk of 17 MB with
3,588 inputs resulted in roughly 62 GB of string copies and around 150 seconds spent in
`bundleOutputToEsbuildMetafile`, while the rolldown bundling itself took 3 seconds.

Read the rendered length once per module before iterating its inputs. The generated
metafile is unchanged. On the affected application the `OPTIMIZE_CHUNKS` phase drops
from 179 seconds to 4 seconds and the total production build from 234 to 55 seconds.

Fixes #34044

* Remove performance comments in chunk-optimizer.ts

Removed comments explaining the performance impact of accessing the rendered length in the chunk optimizer.

* Update packages/angular/build/src/builders/application/chunk-optimizer.ts

Co-authored-by: Charles <19598772+clydin@users.noreply.github.com>

---------

Co-authored-by: Charles <19598772+clydin@users.noreply.github.com>
(cherry picked from commit f1afa60)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: @angular/build area: performance Issues related to performance merge: squash commits When the PR is merged, a squash and merge should be performed target: patch This PR is targeted for the next patch release

Projects

None yet

3 participants