Skip to content

Conversation

@marcins
Copy link
Contributor

@marcins marcins commented Oct 27, 2025

Motivation

Looking at some product SSR bundling builds shows that a significant amount of time is spent in bundleGraph.isAssetReferenced during packaging. This is inefficient as there are overlapping repeated traversals for different assets during the build. In particular this results in a lot of GC, and clear-ing of the visited BitSet used in the graph traversal.

This isn't true across all products and all build types though, it seems to be endemic to some builds only. It's still slightly faster for the builds where this wasn't really an issue just not to the same extent (e.g. below - Confluence is 18%±5% faster, Jira is 4%±2% faster)

Also worth noting that for local development the impact will only be seen for SSR, as the ScopeHoistingPackager is not used for FE builds in dev.

Benchmark results:

Confluence SSR:

Command Mean [s] Min [s] Max [s] Relative
ENABLE_OPTIMISATION=false 90.782 ± 0.593 90.433 91.468 1.26 ± 0.01
ENABLE_OPTIMISATION=true 72.168 ± 0.586 71.493 72.547 1.00

Confluence FE:

Command Mean [s] Min [s] Max [s] Relative
ENABLE_OPTIMISATION=false 203.843 ± 6.274 200.159 211.087 1.24 ± 0.04
ENABLE_OPTIMISATION=true 163.853 ± 2.859 161.229 166.900 1.00

Jira SSR:

Command Mean [s] Min [s] Max [s] Relative
ENABLE_OPTIMISATION=false 219.319 ± 4.621 214.291 223.381 1.04 ± 0.02
ENABLE_OPTIMISATION=true 211.732 ± 2.117 209.288 212.963 1.00

Jira FE:

Command Mean [s] Min [s] Max [s] Relative
ENABLE_OPTIMISATION=false 699.723 ± 3.190 697.485 703.375 1.14 ± 0.11
ENABLE_OPTIMISATION=true 611.203 ± 58.262 571.685 678.113 1.00

Changes

Instead of traversing per asset on demand, we take a multi phase approach:

  • if we have already precomputed data for this asset/bundle then use that data
  • run just some "fast checks" - these aren't cached because caching them is more expensive then doing them
  • only if the "fast check" is inconclusive, do we proceed to the traversal based check.

This is behind a new precomputeReferencedAssets feature flag.

This approach is faster for all builds that were tested.

Checklist

  • Existing or new tests cover this change
  • Dist diffing
  • Perf benchmarking
  • There is a changeset for this change, or one is not required
  • Added documentation for any new features to the docs/ folder

@changeset-bot
Copy link

changeset-bot bot commented Oct 27, 2025

🦋 Changeset detected

Latest commit: 33cd346

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

This PR includes changesets to release 108 packages
Name Type
@atlaspack/types-internal Minor
@atlaspack/feature-flags Minor
@atlaspack/packager-js Minor
@atlaspack/core Minor
@atlaspack/bundler-default Patch
atlaspack Patch
@atlaspack/codeframe Patch
@atlaspack/fs Patch
@atlaspack/graph Patch
@atlaspack/logger Patch
@atlaspack/plugin Patch
@atlaspack/profiler Patch
@atlaspack/types Patch
@atlaspack/utils Patch
@atlaspack/workers Patch
@atlaspack/packager-css Patch
@atlaspack/reporter-bundle-buddy Patch
@atlaspack/resolver-tesseract Patch
@atlaspack/runtime-js Patch
@atlaspack/transformer-less Patch
@atlaspack/transformer-postcss Patch
@atlaspack/transformer-tokens Patch
@atlaspack/transformer-xml Patch
@atlaspack/ts-utils Patch
@atlaspack/bundler-experimental Patch
@atlaspack/cache Patch
@atlaspack/cli Patch
@atlaspack/inspector Patch
@atlaspack/optimizer-inline-requires Patch
@atlaspack/packager-html Patch
@atlaspack/reporter-cli Patch
@atlaspack/reporter-conditional-manifest Patch
@atlaspack/runtime-react-refresh Patch
@atlaspack/transformer-compiled-css-in-js Patch
@atlaspack/transformer-css Patch
@atlaspack/transformer-html Patch
@atlaspack/transformer-js Major
@atlaspack/transformer-react-refresh-wrap Patch
@atlaspack/node-resolver-core Patch
@atlaspack/config-default Major
@atlaspack/config-webextension Major
@atlaspack/register Patch
@atlaspack/test-utils Patch
@atlaspack/bundle-stats Patch
@atlaspack/query Patch
@atlaspack/optimizer-image Major
@atlaspack/reporter-bundle-stats Patch
@atlaspack/transformer-image Major
@atlaspack/inspector-frontend Patch
@atlaspack/package-manager Patch
@atlaspack/link Patch
@atlaspack/watcher-watchman-js Patch
@atlaspack/bundler-library Patch
@atlaspack/compressor-brotli Patch
@atlaspack/compressor-gzip Patch
@atlaspack/compressor-raw Patch
@atlaspack/namer-default Patch
@atlaspack/optimizer-blob-url Patch
@atlaspack/optimizer-css Patch
@atlaspack/optimizer-cssnano Patch
@atlaspack/optimizer-data-url Patch
@atlaspack/optimizer-htmlnano Patch
@atlaspack/optimizer-svgo Patch
@atlaspack/optimizer-swc Patch
@atlaspack/optimizer-terser Patch
@atlaspack/packager-raw-url Patch
@atlaspack/packager-raw Patch
@atlaspack/packager-svg Patch
@atlaspack/packager-ts Patch
@atlaspack/packager-wasm Patch
@atlaspack/packager-webextension Patch
@atlaspack/packager-xml Patch
@atlaspack/reporter-build-metrics Patch
@atlaspack/reporter-bundle-analyzer Patch
@atlaspack/reporter-dev-server-sw Patch
@atlaspack/reporter-dev-server Patch
@atlaspack/reporter-json Patch
@atlaspack/reporter-lsp Patch
@atlaspack/reporter-sourcemap-visualiser Patch
@atlaspack/reporter-tracer Patch
@atlaspack/resolver-default Patch
@atlaspack/resolver-glob Patch
@atlaspack/runtime-browser-hmr Patch
@atlaspack/runtime-service-worker Patch
@atlaspack/runtime-webextension Patch
@atlaspack/transformer-babel Patch
@atlaspack/transformer-glsl Patch
@atlaspack/transformer-graphql Patch
@atlaspack/transformer-inline-string Patch
@atlaspack/transformer-inline Patch
@atlaspack/transformer-json Patch
@atlaspack/transformer-jsonld Patch
@atlaspack/transformer-mdx Patch
@atlaspack/transformer-posthtml Patch
@atlaspack/transformer-pug Patch
@atlaspack/transformer-raw Patch
@atlaspack/transformer-sass Patch
@atlaspack/transformer-svg-react Patch
@atlaspack/transformer-svg Patch
@atlaspack/transformer-toml Patch
@atlaspack/transformer-typescript-tsc Patch
@atlaspack/transformer-typescript-types Patch
@atlaspack/transformer-webextension Patch
@atlaspack/transformer-webmanifest Patch
@atlaspack/transformer-worklet Patch
@atlaspack/transformer-yaml Patch
@atlaspack/validator-eslint Patch
@atlaspack/validator-typescript 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
Copy link
Contributor

github-actions bot commented Oct 27, 2025

📊 Type Coverage Report

Coverage Comparison

Metric Baseline Current Change
Coverage Percentage 92.02% 92.03% 📈 +0.01%
Correctly Typed 200,922 201,267 +345
Total Expressions 218,323 218,675 +352
Untyped Expressions 17,401 17,408 +7

Files with Most Type Issues (Top 15)

File Issues Affected Lines
packages/core/integration-tests/test/javascript.ts 983 648
packages/core/integration-tests/test/cache.ts 885 626
packages/core/integration-tests/test/scope-hoisting.ts 623 490
packages/utils/node-resolver-core/test/resolver.ts 476 177
packages/core/integration-tests/test/html.ts 468 294
packages/core/integration-tests/test/sourcemaps.ts 356 176
packages/core/test-utils/src/utils.ts 330 205
packages/core/integration-tests/test/incremental-bundling.ts 298 206
packages/core/core/src/dumpGraphToGraphViz.ts 251 108
packages/core/integration-tests/test/output-formats.ts 227 161
packages/transformers/webextension/src/WebExtensionTransformer.ts 210 80
packages/core/integration-tests/test/react-refresh.ts 197 71
packages/core/integration-tests/test/css-modules.ts 191 107
packages/core/core/src/requests/TargetRequest.ts 190 133
packages/core/integration-tests/test/babel.ts 187 115

This report was generated by the Type Coverage GitHub Action

@marcins marcins force-pushed the optimise-isAssetReferenced-calls branch from 8b00720 to 793231e Compare October 27, 2025 05:25
@marcins marcins marked this pull request as ready for review October 27, 2025 06:23
@marcins marcins requested a review from a team as a code owner October 27, 2025 06:23
@marcins marcins changed the title Optimise is asset referenced calls Optimise isAssetReferenced Oct 27, 2025
@marcins marcins changed the title Optimise isAssetReferenced Optimise isAssetReferenced in packager Oct 27, 2025
Implements a three-tier optimization strategy for isAssetReferenced calls:

1. **Cache-first lookup** (~0.001ms) - Check expensive computation cache first
2. **Fast checks fallback** (~0.01ms) - Only when cache misses, try quick checks
3. **Expensive computation** (~20-2000ms) - Only when both above fail, with caching

**Key Changes:**
- Add `isAssetReferencedFastCheck()` method to BundleGraph for fast-only checks
- Update ScopeHoistingPackager to use cache-first strategy
- Separate fast operations from expensive ones for optimal performance

**Technical Details:**
- Only expensive bundle traversals get cached results
- Cache hit rates: Jira ~10%, Confluence ~95-100%
- Reduces cache overhead for operations that are cheaper to compute than cache

This resolves performance regressions in Jira while maintaining Confluence
optimization benefits through adaptive caching strategy.
@marcins marcins force-pushed the optimise-isAssetReferenced-calls branch from fe4be40 to 33cd346 Compare November 13, 2025 03:21
@github-actions
Copy link
Contributor

📊 Benchmark Results

🎉 Performance improvements detected!

📊 Benchmark Results

Overall Performance

Test Duration JS Memory Peak Native Memory Peak vs Baseline Status
Three.js Real Repository (JS) 16.55s 1.76GB 1.87GB -0.84% duration, -20.72% JS memory 🟢 Improvement
Three.js Real Repository (Native) 22.55s 2.97GB 3.37GB -21.09% duration, -20.20% JS memory 🟢 Improvement

🔍 Detailed Phase Analysis

Three.js Real Repository (JS)

Phase Duration (avg) Duration (p95) Memory Peak (avg) Memory Peak (p95)
resolving 8.87s 9.11s 1.14GB 1.27GB
transforming 8.87s 9.11s 1.15GB 1.28GB
bundling 8.65s 8.91s 1.15GB 1.28GB
bundled 8.26s 8.58s 1.23GB 1.37GB
packaging 6.10s 6.36s 1.35GB 1.50GB
optimizing 5.94s 6.21s 1.76GB 1.87GB

Three.js Real Repository (Native)

Phase Duration (avg) Duration (p95) Memory Peak (avg) Memory Peak (p95)
bundling 14.17s 18.55s 2.11GB 2.50GB
bundled 13.82s 18.19s 2.28GB 2.66GB
packaging 7.19s 8.02s 2.48GB 2.91GB
optimizing 7.06s 7.90s 2.97GB 3.37GB

💾 Unified Memory Analysis

Three.js Real Repository (JS) Memory Statistics

Memory Type Metric Min Mean Median P95 P99 Max Std Dev
JavaScript RSS 1.18GB 1.55GB 1.60GB 1.87GB 1.87GB 1.87GB 247.41MB
Heap Used 79.80MB 84.42MB 81.07MB 92.82MB 92.82MB 92.82MB 5.65MB
Heap Total 95.60MB 125.93MB 138.08MB 175.85MB 175.85MB 175.85MB 28.45MB
External 38.94MB 106.04MB 173.09MB 173.09MB 173.09MB 173.09MB 67.05MB
Native (Rust) Physical Memory 1.20GB 1.42GB 1.43GB 1.71GB 1.84GB 1.87GB 160.81MB
Virtual Memory 30.02GB 30.75GB 30.75GB 31.01GB 31.17GB 31.26GB 206.76MB

Sample Counts: JS: 6, Native: 271

Three.js Real Repository (Native) Memory Statistics

Memory Type Metric Min Mean Median P95 P99 Max Std Dev
JavaScript RSS 1.98GB 2.68GB 2.81GB 3.37GB 3.37GB 3.37GB 455.48MB
Heap Used 76.85MB 77.17MB 77.18MB 77.61MB 77.61MB 77.61MB 0.25MB
Heap Total 86.95MB 94.49MB 90.20MB 110.45MB 110.45MB 110.45MB 8.96MB
External 179.53MB 182.44MB 185.32MB 185.34MB 185.34MB 185.34MB 2.89MB
Native (Rust) Physical Memory 2.27GB 2.73GB 2.77GB 3.13GB 3.29GB 3.37GB 268.22MB
Virtual Memory 30.59GB 33.27GB 33.36GB 33.68GB 33.86GB 33.92GB 542.55MB

Sample Counts: JS: 6, Native: 517

🖥️ Environment

  • Node.js: v22.17.0
  • Platform: linux (x64)
  • CPU: AMD EPYC 7763 64-Core Processor
  • Total Memory: 15.62GB
  • Git SHA: 33cd346
  • Branch: optimise-isAssetReferenced-calls
  • Timestamp: 2025-11-13T03:43:33.423Z

asset.meta.shouldWrap ||
this.bundle.env.sourceType === 'script' ||
this.bundleGraph.isAssetReferenced(this.bundle, asset) ||
this.isAssetReferencedInBundle(this.bundle, asset) ||
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Passing this.bundle is a bit odd. I'd just reference it directly via this in isAssetReferencedInBundle

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahh I see the bundle changes in some cases. Ignore me 😅

Copy link
Contributor

@mattcompiles mattcompiles left a comment

Choose a reason for hiding this comment

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

Nice one mate

@marcins marcins enabled auto-merge (squash) November 13, 2025 05:30
@marcins marcins merged commit 8eb84ee into main Nov 13, 2025
45 of 46 checks passed
@marcins marcins deleted the optimise-isAssetReferenced-calls branch November 13, 2025 06:00
@atlaspack-ci atlaspack-ci bot mentioned this pull request Nov 13, 2025
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