Skip to content

turbopack: reduce EffectStateStorage memory by storing u128 content hashes#92902

Merged
sokra merged 1 commit intocanaryfrom
sokra/effect-storage-memory
Apr 17, 2026
Merged

turbopack: reduce EffectStateStorage memory by storing u128 content hashes#92902
sokra merged 1 commit intocanaryfrom
sokra/effect-storage-memory

Conversation

@sokra
Copy link
Copy Markdown
Member

@sokra sokra commented Apr 16, 2026

What?

In turbo-tasks-fs, the WriteEffect and WriteLinkEffect structs now store a precomputed u128 xxh3 content hash instead of returning the full ReadRef<PersistedFileContent> / ReadRef<LinkContent> from their Effect::value() implementations.

The associated type Effect::Value is changed from ReadRef<PersistedFileContent> / ReadRef<LinkContent> to u128 for both effect types.

LinkContent gains a DeterministicHash derive to support hashing via hash_xxh3_hash128.

Why?

EffectStateStorage stores the last-applied value for each effect key in last_applied: Mutex<Option<Box<dyn Any + Send + Sync>>> to enable a double-checked deduplication fast path (skip re-writing a file if content hasn't changed). In long-running sessions this map grows continuously and holds onto a lot of memory because it stores full file content (ReadRef<PersistedFileContent>) or full link content (ReadRef<LinkContent>) for every file ever written.

Storing a u128 hash (16 bytes) instead of the full content significantly reduces memory usage while preserving the deduplication semantics (hash equality implies content equality with negligible collision probability).

How?

  • Added content_hash: u128 field to WriteEffect and WriteLinkEffect, computed at construction time using hash_xxh3_hash128 (xxh3 128-bit non-cryptographic hash, already used elsewhere in turbopack for content addressing).
  • Changed Effect::Value = u128 for both effect types; value() returns &self.content_hash.
  • The EffectStateStorage now stores Box<u128> (24 bytes on the heap) per entry instead of Box<ReadRef<...>> which held a reference-counted pointer into potentially large file content.
  • No changes to EffectStateStorage, Effects::apply(), or DynEffect vtable — the existing Any/DynPartialEq machinery works transparently with u128.

…l content

Reduces memory usage in EffectStateStorage by storing a xxh3 128-bit hash
of the effect value instead of the full ReadRef<PersistedFileContent> or
ReadRef<LinkContent>. The hash is precomputed at effect construction time.

Co-Authored-By: Claude <noreply@anthropic.com>
@nextjs-bot nextjs-bot added created-by: Turbopack team PRs by the Turbopack team. Turbopack Related to Turbopack with Next.js. labels Apr 16, 2026
@sokra sokra requested a review from lukesandberg April 16, 2026 16:16
Comment thread turbopack/crates/turbo-tasks-fs/src/lib.rs
Comment thread turbopack/crates/turbo-tasks-fs/src/lib.rs
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 16, 2026

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing sokra/effect-storage-memory (06af703) with canary (4dd1fcb)

Open in CodSpeed

Footnotes

  1. 3 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.

@sokra sokra marked this pull request as ready for review April 16, 2026 18:26
@nextjs-bot
Copy link
Copy Markdown
Collaborator

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 456ms 456ms ███▁▁
Cold (Ready in log) 443ms 442ms ▅▆█▇▃
Cold (First Request) 1.954s 1.967s ▅█▅▆▂
Warm (Listen) 456ms 456ms ▁███▅
Warm (Ready in log) 440ms 440ms ▇▇█▇▄
Warm (First Request) 1.969s 1.962s ▅███▂
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 14.613s 14.622s ▆▆▇█▅
Cached Build 14.720s 14.748s ▇▇▆█▄
node_modules Size 494 MB 494 MB █████
📦 Bundle Sizes

Bundle Sizes

📦 Webpack

Client

Main Bundles
Canary PR Change
2637-HASH.js gzip 4.63 kB N/A -
7724.HASH.js gzip 169 B N/A -
8274-HASH.js gzip 61.4 kB N/A -
8817-HASH.js gzip 5.59 kB N/A -
c3500254-HASH.js gzip 62.8 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB
main-app-HASH.js gzip 254 B 255 B
main-HASH.js gzip 39.4 kB 39.4 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
5887-HASH.js gzip N/A 5.61 kB -
6522-HASH.js gzip N/A 60.8 kB -
6779-HASH.js gzip N/A 4.63 kB -
8854.HASH.js gzip N/A 169 B -
eab920f9-HASH.js gzip N/A 62.8 kB -
Total 236 kB 235 kB ✅ -643 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 193 B 193 B
_error-HASH.js gzip 182 B 182 B
css-HASH.js gzip 333 B 334 B
dynamic-HASH.js gzip 1.81 kB 1.8 kB
edge-ssr-HASH.js gzip 255 B 255 B
head-HASH.js gzip 353 B 349 B 🟢 4 B (-1%)
hooks-HASH.js gzip 384 B 382 B
image-HASH.js gzip 581 B 581 B
index-HASH.js gzip 260 B 259 B
link-HASH.js gzip 2.51 kB 2.51 kB
routerDirect..HASH.js gzip 316 B 318 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 313 B 314 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.98 kB 7.97 kB ✅ -10 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 126 kB 126 kB
page.js gzip 273 kB 273 kB
Total 399 kB 399 kB ✅ -370 B
Middleware
Canary PR Change
middleware-b..fest.js gzip 617 B 618 B
middleware-r..fest.js gzip 156 B 156 B
middleware.js gzip 44.2 kB 44.4 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 45.8 kB 46 kB ⚠️ +225 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 721 B 720 B
Total 721 B 720 B ✅ -1 B
Build Cache
Canary PR Change
0.pack gzip 4.38 MB 4.38 MB
index.pack gzip 114 kB 113 kB 🟢 1.6 kB (-1%)
index.pack.old gzip 114 kB 113 kB
Total 4.61 MB 4.61 MB ✅ -4.14 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 347 kB 347 kB
app-page-exp..prod.js gzip 192 kB 192 kB
app-page-tur...dev.js gzip 346 kB 346 kB
app-page-tur..prod.js gzip 192 kB 192 kB
app-page-tur...dev.js gzip 343 kB 343 kB
app-page-tur..prod.js gzip 190 kB 190 kB
app-page.run...dev.js gzip 343 kB 343 kB
app-page.run..prod.js gzip 190 kB 190 kB
app-route-ex...dev.js gzip 77 kB 77 kB
app-route-ex..prod.js gzip 52.5 kB 52.5 kB
app-route-tu...dev.js gzip 77.1 kB 77.1 kB
app-route-tu..prod.js gzip 52.6 kB 52.6 kB
app-route-tu...dev.js gzip 76.7 kB 76.7 kB
app-route-tu..prod.js gzip 52.3 kB 52.3 kB
app-route.ru...dev.js gzip 76.6 kB 76.6 kB
app-route.ru..prod.js gzip 52.3 kB 52.3 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 43.9 kB 43.9 kB
pages-api-tu..prod.js gzip 33.5 kB 33.5 kB
pages-api.ru...dev.js gzip 43.9 kB 43.9 kB
pages-api.ru..prod.js gzip 33.5 kB 33.5 kB
pages-turbo....dev.js gzip 53.3 kB 53.3 kB
pages-turbo...prod.js gzip 39.1 kB 39.1 kB
pages.runtim...dev.js gzip 53.3 kB 53.3 kB
pages.runtim..prod.js gzip 39.1 kB 39.1 kB
server.runti..prod.js gzip 62.9 kB 62.9 kB
Total 3.06 MB 3.06 MB ✅ -3 B
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/e9885bcabe05b408ad5c11ecfa953999cbc75aa2/next

@sokra sokra merged commit 68bac2a into canary Apr 17, 2026
330 of 342 checks passed
@sokra sokra deleted the sokra/effect-storage-memory branch April 17, 2026 07:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Turbopack team PRs by the Turbopack team. Turbopack Related to Turbopack with Next.js.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants