Skip to content

Comments

fix: optimize chunk merging for shared entry points#7194

Merged
graphite-app[bot] merged 1 commit intomainfrom
11-20-feat_4904
Dec 1, 2025
Merged

fix: optimize chunk merging for shared entry points#7194
graphite-app[bot] merged 1 commit intomainfrom
11-20-feat_4904

Conversation

@IWANABETHATGUY
Copy link
Member

@IWANABETHATGUY IWANABETHATGUY commented Nov 22, 2025

related to #4904

Copy link
Member Author

IWANABETHATGUY commented Nov 22, 2025


How to use the Graphite Merge Queue

Add the label graphite: merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@netlify
Copy link

netlify bot commented Nov 22, 2025

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit b158b29
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/69218cfb554f0b0008b18f8d

@netlify
Copy link

netlify bot commented Nov 22, 2025

Deploy Preview for rolldown-rs failed.

Name Link
🔨 Latest commit 5208010
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/692d04c210607d0008f303b0

@github-actions
Copy link
Contributor

github-actions bot commented Nov 22, 2025

Benchmarks Rust

group                                                        pr                                     target
-----                                                        --                                     ------
bundle/bundle@multi-duplicated-top-level-symbol              1.03     67.9±2.37ms        ? ?/sec    1.00     65.7±1.56ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-sourcemap    1.03     74.8±1.48ms        ? ?/sec    1.00     72.9±1.28ms        ? ?/sec
bundle/bundle@rome_ts                                        1.01    109.6±2.04ms        ? ?/sec    1.00    108.9±1.51ms        ? ?/sec
bundle/bundle@rome_ts-sourcemap                              1.00    123.0±3.90ms        ? ?/sec    1.00    122.6±1.85ms        ? ?/sec
bundle/bundle@threejs                                        1.00     40.2±2.71ms        ? ?/sec    1.01     40.7±2.34ms        ? ?/sec
bundle/bundle@threejs-sourcemap                              1.00     43.1±0.57ms        ? ?/sec    1.01     43.6±0.53ms        ? ?/sec
bundle/bundle@threejs10x                                     1.00    399.2±5.42ms        ? ?/sec    1.00    399.7±6.00ms        ? ?/sec
bundle/bundle@threejs10x-sourcemap                           1.00    457.8±4.49ms        ? ?/sec    1.00    459.3±4.35ms        ? ?/sec
scan/scan@rome_ts                                            1.03     87.0±1.73ms        ? ?/sec    1.00     84.7±1.45ms        ? ?/sec
scan/scan@threejs                                            1.02     28.9±0.43ms        ? ?/sec    1.00     28.3±0.38ms        ? ?/sec
scan/scan@threejs10x                                         1.01    302.0±5.88ms        ? ?/sec    1.00    298.3±5.84ms        ? ?/sec

@IWANABETHATGUY IWANABETHATGUY force-pushed the 11-20-feat_4904 branch 6 times, most recently from abc708f to 6b35b86 Compare November 28, 2025 13:42
@IWANABETHATGUY IWANABETHATGUY changed the base branch from main to graphite-base/7194 November 28, 2025 16:46
@IWANABETHATGUY IWANABETHATGUY changed the base branch from graphite-base/7194 to 11-28-refactor_use_iterative_method_to_merge_chunks November 28, 2025 16:46
@graphite-app graphite-app bot changed the base branch from 11-28-refactor_use_iterative_method_to_merge_chunks to graphite-base/7194 November 29, 2025 04:43
@IWANABETHATGUY IWANABETHATGUY changed the base branch from graphite-base/7194 to main November 29, 2025 12:00
@IWANABETHATGUY IWANABETHATGUY force-pushed the 11-20-feat_4904 branch 3 times, most recently from 89df5c1 to 9e07bb6 Compare November 30, 2025 08:11
@IWANABETHATGUY IWANABETHATGUY changed the title feat: 4904 fix: optimize chunk merging for shared entry points Nov 30, 2025
@IWANABETHATGUY IWANABETHATGUY marked this pull request as ready for review November 30, 2025 08:14
Copilot AI review requested due to automatic review settings November 30, 2025 08:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes chunk generation by merging shared chunks into entry chunks when one entry imports another, reducing the total number of output chunks. The optimization applies to deduplicated entry points (multiple entries with the same source file) and scenarios where entry modules have import relationships.

Key changes:

  • Modified chunk merging logic to detect when one user-defined entry imports all other entries sharing common modules
  • Changed imports_from_other_chunks from Vec to FxIndexMap for efficient deduplication and lookup
  • Moved entry_module_to_entry_chunk assignment earlier in the flow to support the new merging logic

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
crates/rolldown/src/stages/generate_stage/code_splitting.rs Added new logic to merge shared chunks into entry chunks when one entry imports all others; moved entry_module_to_entry_chunk assignment
crates/rolldown_common/src/chunk/mod.rs Changed imports_from_other_chunks from Vec to FxIndexMap for O(1) lookups and deduplication
crates/rolldown/src/stages/generate_stage/compute_cross_chunk_links.rs Updated to merge pre-existing imports_from_other_chunks entries with newly computed ones
packages/rolldown/tests/fixtures/input/deduplicate/named-entry/_config.ts Updated test expectation: 2 chunks instead of 3 (shared chunk merged into entry)
packages/rolldown/tests/fixtures/input/deduplicate/emit-file2/_config.ts Updated test expectation: 2 chunks instead of 3
packages/rolldown/tests/fixtures/plugin/context/emit-chunk/_config.ts Updated hash in snapshot due to changed chunk content
crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap Updated hashes for multiple test cases reflecting optimized chunk merging
crates/rolldown/tests/rolldown/semantic/export_star_from_external_as_shared_entries_cjs/artifacts.snap Demonstrates chunk merging: entry2 now requires entry.js instead of separate main.js
crates/rolldown/tests/rolldown/semantic/export_star_from_external_as_shared_entries/artifacts.snap ESM equivalent of above optimization
crates/rolldown/tests/rolldown/misc/preserve_entry_signature/issue-4880/artifacts.snap Shows removal of shared foo22.js chunk, merged into foo2.js
crates/rolldown/tests/rolldown/misc/cjs_entry_as_dependency/artifacts.snap Demonstrates merging of main22.js into main2.js
crates/rolldown/tests/rolldown/misc/assign_chunk_name_order/artifacts.snap Shows removal of intermediate shared file2.js chunk
crates/rolldown/tests/rolldown/issues/5011/artifacts.snap Demonstrates removal of after-preload-dynamic2.js shared chunk
crates/rolldown/tests/rolldown/function/format/cjs/share_chunk_without_symbol/artifacts.snap Shows merging of main.js into entry1.js for CJS format
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/ensure_lazy_module_eval/artifacts.snap Demonstrates removal of dynamic2.js shared chunk
crates/rolldown/tests/rolldown/code_splitting/ensure_side_effect_executed/artifacts.snap Shows merging of entry.js into entry_js.js
crates/rolldown/tests/rolldown/cjs_compat/multiple_circle_cjs_entries/artifacts.snap Demonstrates handling of circular CJS entries with chunk merging
crates/rolldown/tests/esbuild/default/duplicate_entry_point/artifacts.snap Shows removal of entry3.js shared chunk for duplicate entry points

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

crates/rolldown/src/stages/generate_stage/code_splitting.rs:959

  • The variable name ret is generic and doesn't convey its purpose. Consider renaming it to something more descriptive like all_dynamic_entries_reachable to clarify what the boolean represents.
    let ret = dynamic_entry.iter().all(|idx| {
      entry_chunk_reference
        .get(&chunk_idx)
        .map(|reached_dynamic_chunk| reached_dynamic_chunk.contains(idx))
        .unwrap_or(false)
    });
    ret.then_some(chunk_idx)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@hyf0 hyf0 assigned sapphi-red and unassigned IWANABETHATGUY Nov 30, 2025
Copy link
Member

@hyf0 hyf0 left a comment

Choose a reason for hiding this comment

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

I go through all snapshot changes and they're looking fine, but still want some input from @sapphi-red .

@graphite-app
Copy link
Contributor

graphite-app bot commented Dec 1, 2025

Merge activity

graphite-app bot pushed a commit that referenced this pull request Dec 1, 2025
@sapphi-red
Copy link
Member

Let me check the behavior, in case.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@graphite-app graphite-app bot merged commit 5208010 into main Dec 1, 2025
30 of 34 checks passed
@graphite-app graphite-app bot deleted the 11-20-feat_4904 branch December 1, 2025 03:15
This was referenced Dec 3, 2025
shulaoda added a commit that referenced this pull request Dec 3, 2025
## [1.0.0-beta.53] - 2025-12-03

💥 Breaking Changes
- Drop `i686-pc-windows-msvc` target support

🚀 Chunk Merging Optimization
- Rolldown now automatically merges shared chunks when entries import each other (when `preserveEntrySignature` is not `strict`)
```shell
Before:
entry.js   → imports → shared.js (common chunk)
entry2.js  → imports → shared.js
Output: 3 chunks (entry.js, entry2.js, shared.js)

After:
entry.js   → contains shared code
entry2.js  → imports → entry.js
Output: 2 chunks (entry.js, entry2.js)
```

### 💥 BREAKING CHANGES

- drop `i686-pc-windows-msvc` target support (#7230) by @sapphi-red

### 🚀 Features

- rolldown_plugin_vite_manifest: pass normalized options to `isLegacy` callback (#7321) by @shulaoda
- plugin/vite-resolve: add `disableCache` option (#6763) by @sapphi-red
- rolldown: export `createTokioRuntime` for tsdown (#7264) by @shulaoda
- rolldown_plugin_vite_html: sync `moduleSideEffects` for already loaded modules (#7254) by @shulaoda
- rolldown_plugin_vite_html: load module scripts with side effects to prevent tree-shaking (#7244) by @shulaoda
- rolldown_plugin_vite_css_post: implement `cssScopeTo` for scoped CSS tree-shaking (#7240) by @shulaoda

### 🐛 Bug Fixes

- export default class decl __name runtime insertion (#7316) by @IWANABETHATGUY
- chunk side effects calculation (#7273) by @IWANABETHATGUY
- node: `output.generateCode.preset: 'es2015'` should set `output.generateCode.symbols: true` by default (#7314) by @sapphi-red
- skip name helper for classes with static name property (#7312) by @IWANABETHATGUY
- preserve chunk imports relationship after chunk merging (#7303) by @shulaoda
- dev: make `register_modules` async (#7289) by @hyf0
- preserve computed property in object destructuring (#7288) by @IWANABETHATGUY
- support dynamic imports with shared dependencies (#7261) by @IWANABETHATGUY
- call `defer_sync_scan_data` in non-incremental build mode (#7255) by @shulaoda
- optimize chunk merging for shared entry points (#7194) by @IWANABETHATGUY
- add indentation for UMD format output (#7263) by @IWANABETHATGUY
- rolldown_plugin_vite_css_post: pass options to `isLegacy` callback for proper legacy detection (#7260) by @shulaoda
- rolldown_plugin_vite_css_post: also detect `?inline=true` query for inlined CSS (#7245) by @shulaoda
- rolldown_plugin_vite_css_post: distinguish empty CSS from no CSS (#7241) by @shulaoda
- add Windows support for t-run command (#7242) by @IWANABETHATGUY
- cjs: prevent duplicate require declarations for external modules with preserveModules (#7234) by @logaretm
- rolldown_plugin_vite_resolve: resolve from root for virtual modules (#7236) by @sapphi-red
- include entry level external modules in chunk exports (#7218) by @IWANABETHATGUY

### 🚜 Refactor

- dev: make `removeClient` async (#7313) by @hyf0
- move chunk merging code out of code_splitting.rs (#7285) by @IWANABETHATGUY
- extract common function util for chunk merging (#7271) by @IWANABETHATGUY
- use iterative method to merge chunks (#7256) by @IWANABETHATGUY
- use concat_string! instead of string replace for generating chunk level exports (#7247) by @IWANABETHATGUY

### 📚 Documentation

- add warning to experimental.resolveNewUrlToAsset about JS/TS files (#7300) by @Copilot
- add sequential hook execution difference in plugin-api.md (#7308) by @Copilot
- add migration example from onwarn to onLog (#7299) by @Copilot
- add migration example for manualChunks to advancedChunks (#7298) by @Copilot
- deps: bump vitepress to fix build (#7307) by @sapphi-red
- examples & text for experimental.resolveNewUrlToAsset (#7259) by @TheAlexLichter

### ⚡ Performance

- rolldown_plugin_vite_css_post: lazily load `cssScopeTo` from JS module options (#7253) by @shulaoda
- rolldown_plugin_vite_css_post: avoid unnecessary string clones in `resolve_asset_urls_in_css` (#7250) by @shulaoda

### 🧪 Testing

- generate relative path like name in advanced chunks (#7267) by @IWANABETHATGUY
- add test case for preserveEntrySignatures with re-exports (#7279) by @IWANABETHATGUY
- add test262 integration tests (#7196) by @sapphi-red

### ⚙️ Miscellaneous Tasks

- deps: update dependency rolldown-plugin-dts to v0.18.1 (#7304) by @renovate[bot]
- enable tracing feature for napi (#7322) by @sapphi-red
- deps: update napi (#7320) by @renovate[bot]
- deps: update oxc (#7318) by @renovate[bot]
- deps: update napi (#7317) by @renovate[bot]
- deps: update oxc to v0.100.0 (#7301) by @renovate[bot]
- deps: downgrade pnpm to 10.23.0 to fix Netlify build (#7306) by @shulaoda
- add `trustPolicyExclude` for chokidar and semver (#7302) by @sapphi-red
- update pnpm lockfile (#7291) by @IWANABETHATGUY
- deps: update npm packages (#7272) by @renovate[bot]
- deps: update rust crates (#7270) by @renovate[bot]
- deps: update oxc (#7262) by @renovate[bot]
- deps: update github-actions (#7269) by @renovate[bot]
- deps: update dependency dprint-typescript to v0.95.13 (#7268) by @renovate[bot]
- deps: update `html5gum` to 0.8.1 (#7265) by @shulaoda
- rolldown: remove unused `getModuleOptions` from `PluginContext` (#7266) by @shulaoda
- remove unnecessary justfile ignore (#7243) by @IWANABETHATGUY
- deps: update oxc apps (#7238) by @renovate[bot]
- add `nul` to workaround https://github.com/anthropics/claude-c… (#7237) by @IWANABETHATGUY
- deps: update dependency valibot to v1.2.0 [security] (#7231) by @renovate[bot]
- deps: update crate-ci/typos action to v1.40.0 (#7232) by @renovate[bot]

### ❤️ New Contributors

* @logaretm made their first contribution in [#7234](#7234)

Co-authored-by: shulaoda <[email protected]>
@silvenon
Copy link

silvenon commented Jan 8, 2026

Pardon my lack of performance knowledge, but when looking at the "After" below doesn't this cause index.js to import all of lib.js just to get the one shared piece, whereas having the shared chunk would result in downloading only the much smaller shared script, the amount that's actually being used?

G7wE8WgXUAAUnBr-1

@IWANABETHATGUY
Copy link
Member Author

Pardon my lack of performance knowledge, but when looking at the "After" below doesn't this cause index.js to import all of lib.js just to get the one shared piece, whereas having the shared chunk would result in downloading only the much smaller shared script, the amount that's actually being used?

G7wE8WgXUAAUnBr-1

That only happens when the lib.js is referenced with a single entry. https://repl.rolldown.rs/#eNqFUUtqwzAQvcowG7cQXHfr0DMUutZGtUfBwRkZaZwPwneP5E/ikEA2Qo/R+8xTQINlwIZrOufi052xvOMNVgkeOusEgh7AOHuALP9qm/987zPFdB5npudKGssg5OXjE4JiAEfSOwa9VTxEKcJSXE/DBp1t29qeOK8sm2a3cn4xecggl44gwN/87LdLph6WYAs9A8WKo4YXmJTKJ85PChmTpWPeoiaj+3ahYEw67bmkm9GUaOZMJjrKfRdFsX3Yk1jcZcW/4be9uudmR/JYbRyuy41wXW/0PaasOn0FDlfLrKVl

If it is shared with more than one entry, the optimization would bail out.

@silvenon
Copy link

silvenon commented Jan 8, 2026

Ah, I see now (had to fill in the configuration), that's pretty cool! Thank you for the clarification.

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.

4 participants