perf(@angular/build): read rendered module length once per module in chunk optimizer - #34045
Conversation
…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
|
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. |
There was a problem hiding this comment.
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.
…r.ts Co-authored-by: Charles <19598772+clydin@users.noreply.github.com>
…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)
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
optimizeChunkson a dumped esbuild result and comparing the serialized metafile andinitialFiles). Happy to add a spec if you can point me at a suitable harness forchunk-optimizer.PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: #34044
In
bundleOutputToEsbuildMetafile,renderedModule.renderedLengthis 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 sOPTIMIZE_CHUNKSphase, while the rolldown bundling itself takes ~3 s. Rolldown also emits a misleading[PLUGIN_TIMINGS]warning about theangular-bundleplugin 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):DURATION[OPTIMIZE_CHUNKS]ng buildOutput files are byte-identical (same file list, same SHA-256 for every JS and CSS file).
Does this PR introduce a breaking change?
Other information
A follow-up on the rolldown side could cache
codeintransformToRenderedModule, 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; passingchecks: { pluginTimings: false }torolldown()in the optimizer would silence it, but that is left out of this PR.🤖 Generated with Claude Code