Skip to content

perf: replace TypedDashMap with TypedMap in CustomField#8708

Merged
graphite-app[bot] merged 1 commit intomainfrom
perf/replace-typed-dash-map-with-typed-map
Mar 15, 2026
Merged

perf: replace TypedDashMap with TypedMap in CustomField#8708
graphite-app[bot] merged 1 commit intomainfrom
perf/replace-typed-dash-map-with-typed-map

Conversation

@Boshen
Copy link
Copy Markdown
Member

@Boshen Boshen commented Mar 15, 2026

Summary

  • Replace TypedDashMap (DashMap-backed, 64 shards × 8 KB = 512 KB per instance) with TypedMap (plain HashMap-backed) in CustomField

  • CustomField is write-once-read-many: populated at creation, wrapped in Arc, then only read — concurrent write access is never needed

  • Removes dashmap feature dependency from typedmap crate

    Before (from earlier profiling):

    • DashMap shard allocations from CustomField: ~24.2 MB in 3,104 blocks
    • Total allocated: ~311 MB

    After (with TypedMap):

    • DashMap shard overhead (remaining, from other DashMaps like resolver/file emitter): 0.17 MB in ~17 blocks
    • TypedMap/CustomField allocations: 0.15 MB
    • Total allocated: 301.2 MB

    The CustomField DashMap shard overhead dropped from ~24 MB to ~0.15 MB — a ~24 MB reduction (~99.4% elimination). The remaining 0.17 MB of
    DashMap shard allocations come from other legitimate concurrent DashMaps (resolver cache, file emitter, etc.), not from CustomField.

    The total allocation reduction of ~10 MB (311 → 301 MB) is less than the full 24 MB because this is a single-threaded dhat run (fewer shards
    per DashMap than the 64-shard production scenario). The savings on a 10+ core production machine would be the full ~24 MB.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 15, 2026 12:17
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 15, 2026

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit 72ead13
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/69b6d3af68dba90008c82fb1

Copy link
Copy Markdown
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 replaces TypedDashMap (a concurrent DashMap-backed typed map) with TypedMap (a plain HashMap-backed typed map) in the CustomField type. Since CustomField follows a write-once-then-share pattern (populated at creation, wrapped in Arc, then only read), the concurrent write overhead of DashMap (64 shards × 8 KB = 512 KB per instance) is unnecessary.

Changes:

  • Replaced TypedDashMap with TypedMap in CustomField, adding DerefMut impl and a manual Debug impl
  • Updated all call sites to use let mut before inserting into CustomField (required since TypedMap::insert needs &mut self unlike DashMap)
  • Removed the dashmap feature dependency from the typedmap crate in Cargo.toml

Reviewed changes

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

Show a summary per file
File Description
crates/rolldown_plugin/src/types/custom_field.rs Core change: swap TypedDashMapTypedMap, add DerefMut, manual Debug
crates/rolldown_plugin/Cargo.toml Remove dashmap feature from typedmap dependency
crates/rolldown_binding/src/options/plugin/binding_callable_builtin_plugin.rs Add mut to CustomField creation
crates/rolldown_binding/src/options/plugin/types/binding_plugin_context_resolve_options.rs Add mut to CustomField creation
crates/rolldown_plugin_vite_import_glob/src/utils.rs Add mut and wrap in Arc after mutation
crates/rolldown/tests/rolldown/plugin/plugin_context/custom_arg_in_resolve/mod.rs Add mut to test's CustomField creation
Cargo.lock Remove dashmap transitive dependency from typedmap

You can also share your feedback on Copilot code review. Take the survey.

@Boshen Boshen assigned hyf0 and unassigned Boshen Mar 15, 2026
@Boshen Boshen requested a review from hyf0 March 15, 2026 12:19
@github-actions
Copy link
Copy Markdown
Contributor

Benchmarks Rust

  • target: main(1d3c9f5)
  • pr: perf/replace-typed-dash-map-with-typed-map(367e721)
group                                                        pr                                     target
-----                                                        --                                     ------
bundle/bundle@multi-duplicated-top-level-symbol              1.00     63.1±1.27ms        ? ?/sec    1.06     66.7±1.66ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-sourcemap    1.00     71.8±1.35ms        ? ?/sec    1.03     74.3±1.82ms        ? ?/sec
bundle/bundle@rome_ts                                        1.00    141.2±6.31ms        ? ?/sec    1.02    143.8±4.56ms        ? ?/sec
bundle/bundle@rome_ts-sourcemap                              1.00    159.7±3.55ms        ? ?/sec    1.02    163.1±4.32ms        ? ?/sec
bundle/bundle@threejs                                        1.00     59.9±1.54ms        ? ?/sec    1.01     60.8±0.89ms        ? ?/sec
bundle/bundle@threejs-sourcemap                              1.00     71.0±2.61ms        ? ?/sec    1.01     71.9±2.63ms        ? ?/sec
bundle/bundle@threejs10x                                     1.00    704.5±6.05ms        ? ?/sec    1.01    710.8±6.26ms        ? ?/sec
bundle/bundle@threejs10x-sourcemap                           1.00    810.1±6.12ms        ? ?/sec    1.01    819.4±6.36ms        ? ?/sec

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 15, 2026

Merging this PR will not alter performance

✅ 6 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing perf/replace-typed-dash-map-with-typed-map (367e721) with main (f8be84a)2

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (1d3c9f5) during the generation of this report, so f8be84a was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@hyf0
Copy link
Copy Markdown
Member

hyf0 commented Mar 15, 2026

@copilot add a design note plugin-context-resolve.md, no need to add detailed content, just record content like becuase "concurrent write access is never needed" so we're using TypedMap directly, once we encounter features that required write in multiple places, we could switch back to TypedDashMap.

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 15, 2026

@hyf0 I've opened a new pull request, #8714, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Member

hyf0 commented Mar 15, 2026

Merge activity

Copy link
Copy Markdown
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 replaces TypedDashMap (backed by DashMap with 64 shards) with TypedMap (backed by a plain HashMap) in the CustomField type used for plugin context resolve options. Since CustomField is always populated before being shared via Arc and then only read, the concurrent-write overhead of DashMap is unnecessary, saving ~24 MB of shard allocation overhead in production.

Changes:

  • Swapped the inner type of CustomField from TypedDashMap to TypedMap, adding DerefMut and a manual Debug impl
  • Updated all call sites to use let mut before inserting into CustomField, then wrapping in Arc
  • Removed the dashmap feature from the typedmap dependency and added a design note explaining the rationale

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/rolldown_plugin/src/types/custom_field.rs Core change: replace TypedDashMap with TypedMap, add DerefMut, manual Debug
crates/rolldown_plugin/Cargo.toml Remove dashmap feature from typedmap dependency
crates/rolldown_binding/src/options/plugin/binding_callable_builtin_plugin.rs Add mut to CustomField creation before insert
crates/rolldown_binding/src/options/plugin/types/binding_plugin_context_resolve_options.rs Add mut to CustomField creation before insert
crates/rolldown_binding/src/options/plugin/js_plugin.rs Replace `.map(
crates/rolldown_plugin_vite_import_glob/src/utils.rs Insert into mutable CustomField before wrapping in Arc
crates/rolldown/tests/rolldown/plugin/plugin_context/custom_arg_in_resolve/mod.rs Test updated for mut pattern
meta/design/plugin-context-resolve.md Design rationale document
Cargo.lock Reflects removal of dashmap transitive dependency from typedmap

You can also share your feedback on Copilot code review. Take the survey.

## Summary

- Replace `TypedDashMap` (DashMap-backed, 64 shards × 8 KB = 512 KB per instance) with `TypedMap` (plain HashMap-backed) in `CustomField`
- `CustomField` is write-once-read-many: populated at creation, wrapped in `Arc`, then only read — concurrent write access is never needed
- Removes `dashmap` feature dependency from `typedmap` crate

  Before (from earlier profiling):
  - DashMap shard allocations from CustomField: ~24.2 MB in 3,104 blocks
  - Total allocated: ~311 MB

  After (with TypedMap):
  - DashMap shard overhead (remaining, from other DashMaps like resolver/file emitter): 0.17 MB in ~17 blocks
  - TypedMap/CustomField allocations: 0.15 MB
  - Total allocated: 301.2 MB

  The CustomField DashMap shard overhead dropped from ~24 MB to ~0.15 MB — a ~24 MB reduction (~99.4% elimination). The remaining 0.17 MB of
  DashMap shard allocations come from other legitimate concurrent DashMaps (resolver cache, file emitter, etc.), not from CustomField.

  The total allocation reduction of ~10 MB (311 → 301 MB) is less than the full 24 MB because this is a single-threaded dhat run (fewer shards
  per DashMap than the 64-shard production scenario). The savings on a 10+ core production machine would be the full ~24 MB.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@graphite-app graphite-app bot force-pushed the perf/replace-typed-dash-map-with-typed-map branch from 564a2d8 to 72ead13 Compare March 15, 2026 15:43
@graphite-app graphite-app bot merged commit 72ead13 into main Mar 15, 2026
33 checks passed
This was referenced Mar 18, 2026
shulaoda added a commit that referenced this pull request Mar 18, 2026
## [1.0.0-rc.10] - 2026-03-18

### 🚀 Features

- add indentExclusionRanges property to MagicString (#8746) by @IWANABETHATGUY
- expose `oxcRuntimePlugin` (#8654) by @sapphi-red
- rust: make bundler generic over FileSystem for in-memory benchmarks (#8652) by @Boshen

### 🐛 Bug Fixes

- rolldown_plugin_vite_dynamic_import_vars: align dynamic import fast check with Vite (#8760) by @shulaoda
- renamer: handle existing bindings in nested scopes when finding unique names (#8741) by @drewolson
- pass `yarn_pnp` option where needed (#8736) by @sapphi-red
- preserve optional chaining in namespace member expr rewrite (#8712) by @Copilot
- correct UTF-16 index handling in native MagicString (#8693) by @IWANABETHATGUY
- mark failing doctests as ignore (#8700) by @Boshen
- prevent may_partial_namespace from leaking through include_module (#8682) by @IWANABETHATGUY
- ci: bump native-build cache key to invalidate stale napi-rs artifacts (#8678) by @Boshen
- `comments.annotation: false` breaking tree-shaking (#8657) by @IWANABETHATGUY
- validate filenames for NUL bytes from chunkFileNames/entryFileNames (#8644) by @IWANABETHATGUY
- dce-only minify should not set NODE_ENV to production (#8651) by @IWANABETHATGUY

### 🚜 Refactor

- rust: remove dead `CrossModuleOptimizationConfig::side_effects_free_function_optimization` (#8673) by @Dunqing
- rust: simplify `cross_module_optimization` by removing redundant scope tracking (#8672) by @Dunqing
- simplify string repeat in guess_indentor (#8753) by @IWANABETHATGUY
- consolidate custom magic-string tests into one file (#8696) by @IWANABETHATGUY
- extract CJS bailout checks from include_symbol (#8683) by @IWANABETHATGUY
- rust: remove `BindingIdentifierExt` to use `BindingIdentifier::symbol_id()` instead (#8667) by @Dunqing
- bench: add bench_preset helper and inline presets (#8658) by @Boshen
- rust: filter external modules from entries instead of mapping bit positions (#8637) by @Dunqing

### 📚 Documentation

- clarify watch mode behavior and its limitations (#8751) by @sapphi-red
- add external link icon to GitHub button in Hero section (#8731) by @thisisnkc
- guide: clarify that `inject` option is only conceptually similar to esbuild's one (#8743) by @sapphi-red
- meta/design: add `devtools.md` (#8663) by @hyf0
- add viteplus alpha announcement banner (#8668) by @shulaoda

### ⚡ Performance

- rolldown: some minor perf optimization found by autoresearch (#8730) by @Brooooooklyn
- replace Vec allocation with lazy iterator in find_hash_placeholders (#8703) by @Boshen
- replace TypedDashMap with TypedMap in CustomField (#8708) by @Boshen
- bench: remove scan benchmark binary to halve LTO link time (#8694) by @Boshen

### 🧪 Testing

- watch: increase timeout for error output (#8766) by @sapphi-red
- vite-tests: remove JS plugin tests (#8767) by @sapphi-red
- watch: add CLI exit code test (#8752) by @sapphi-red
- normalize paths on Windows even if `resolve.symlinks` is false (#8483) by @sapphi-red

### ⚙️ Miscellaneous Tasks

- correct comment in bundle-analyzer-plugin.ts (#8770) by @origami-z
- upgrade oxc to 0.120.0 (#8764) by @Boshen
- enable all test for `reset` category in MagicString.test.ts (#8749) by @IWANABETHATGUY
- deps: update test262 submodule for tests (#8742) by @sapphi-red
- deps: update oxc apps (#8734) by @renovate[bot]
- deps: update softprops/action-gh-release action to v2.6.1 (#8724) by @renovate[bot]
- deps: update npm packages (major) (#8722) by @renovate[bot]
- deps: update github-actions (major) (#8721) by @renovate[bot]
- deps: update softprops/action-gh-release action to v2.6.0 (#8720) by @renovate[bot]
- deps: update npm packages (#8718) by @renovate[bot]
- deps: update rust crates (#8717) by @renovate[bot]
- deps: update github-actions (#8716) by @renovate[bot]
- deps: update dependency oxlint-tsgolint to v0.17.0 (#8713) by @renovate[bot]
- deps: bump cargo-shear to v1.11.2 (#8711) by @Boshen
- use org level `CODE_OF_CONDUCT.md` (#8706) by @sapphi-red
- fix cache key mismatch and remove redundant cache saves (#8695) by @Boshen
- deps: update oxc apps (#8692) by @renovate[bot]
- deps: update oxc apps (#8649) by @renovate[bot]
- should do matrix out side of reusable workflows 2 (#8691) by @hyf0
- should do matrix out side of reusable workflows (#8690) by @hyf0
- deps: update dependency rolldown-plugin-dts to v0.22.5 (#8689) by @renovate[bot]
- upgrade oxc to 0.119.0 and oxc_resolver to 11.19.1 (#8686) by @Boshen
- correct if condition of `type-check` job (#8677) by @hyf0
- Gate CI type-check job on node changes (#8669) by @Copilot
- benchmark: improve codspeed build (#8665) by @Boshen
- deps: update oxc to v0.118.0 (#8650) by @renovate[bot]
- deps: update crate-ci/typos action to v1.44.0 (#8647) by @renovate[bot]
- deps: update oxc resolver to v11.19.1 (#8646) by @renovate[bot]
- deps: update dependency rust to v1.94.0 (#8648) by @renovate[bot]
- deps: update dependency rolldown-plugin-dts to v0.22.4 (#8645) by @renovate[bot]

### ◀️ Revert

- Revert "ci: Gate CI type-check job on node changes" (#8674) by @hyf0
- "chore(deps): update dependency rust to v1.94.0 (#8648)" (#8660) by @shulaoda

### ❤️ New Contributors

* @origami-z made their first contribution in [#8770](#8770)
* @drewolson made their first contribution in [#8741](#8741)
* @thisisnkc made their first contribution in [#8731](#8731)

Co-authored-by: shulaoda <165626830+shulaoda@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants