Skip to content

turbo-tasks: task-storage memory and dispatch-path overhead reductions#93720

Draft
lukesandberg wants to merge 9 commits intocanaryfrom
05-09-turbo-tasks_replace_taskstorage_lazy_vec_with_a_16_b_lazyvec
Draft

turbo-tasks: task-storage memory and dispatch-path overhead reductions#93720
lukesandberg wants to merge 9 commits intocanaryfrom
05-09-turbo-tasks_replace_taskstorage_lazy_vec_with_a_16_b_lazyvec

Conversation

@lukesandberg
Copy link
Copy Markdown
Contributor

@lukesandberg lukesandberg commented May 9, 2026

Summary

A series of small, independent perf and memory wins for turbo-tasks-backend's task storage and dispatch path. Each commit stands alone; the benchmark numbers below cover the cumulative effect of all 7 commits.

What changed

triomphe::Arc<CachedTaskType> (commit 1)

triomphe::Arc is already a workspace dep used elsewhere (ReadRef, SharedReference). CachedTaskType never appears in a Weak<...>, so we can drop the weak count and the corresponding CAS in drop_slow. Saves one usize per allocation. Migration is via a thin CachedTaskTypeArc newtype to satisfy the bincode Encode/Decode orphan rules.

Niche-encode CellDependency (commit 2)

cell_dependencies/outdated_cell_dependencies previously stored (CellRef, Option<u64>) tuples — the Option<u64> cost a full 16 B (8 B discriminant + 8 B value, aligned), making elements 32 B. The dependent-side cell_dependents stored a parallel (CellId, Option<u64>, TaskId) tuple.

A single CellDependency enum (All(CellRef) / Hash(CellRef, u64)) now backs both sides — the dependents map reuses the same enum with CellRef.task re-pointed at the dependent task. The layout algorithm reuses the niche on ValueTypeId (NonZero<u16>) inside CellRef.cell.type_id for the variant tag. Elements drop from 32 B → 24 B, and LazyField's pinned size from 56 B → 48 B (those AutoSets were the largest variants).

Cache TURBO_TASKS and CURRENT_TASK_STATE in Resolve/Read futures (commit 3)

ResolveRawVcFuture::poll and ReadRawVcFuture::poll are on the per-poll path of every Vc await. Each poll did:

  • with_turbo_tasks(...) (a LocalKey::with on the TURBO_TASKS task-local), and
  • for RawVc::LocalOutput polls — the common case for foo(bar).await patterns — try_read_local_output did another LocalKey::with on CURRENT_TASK_STATE plus a dyn TurboTasksApi virtual dispatch.

Both futures cache the Arc<RwLock<CurrentTaskState>> lazily on first poll and reuse it. The LocalOutput path additionally bypasses the trait object via a free try_read_local_output_in_state function. A future cannot legally migrate between turbo-tasks scopes, so the cached values stay valid for the future's lifetime.

Replace TaskStorage::lazy: Vec<LazyField> with a 16 B TinyVec (commit 4)

TaskStorage::lazy only ever holds at most ~25 elements (one per declared lazy field in the schema), and growth never exceeds that. Replacing Vec<LazyField>'s 24 B (ptr, len, cap) header with (ptr, len: u8, cap: u8) + 6 B padding gives 16 B. Drops size_of::<TaskStorage>() from 136 → 128 B. Multiplied by ~6.5M tasks live during a typical Next.js build, that's roughly 50 MB of resident memory recovered. The TinyVec API is a strict subset of Vec covering only what the schema's macro emit and the storage helpers need.

Capture Arc<dyn TurboTasksApi> at RawVc future construction (commit 5)

Continuation of commit 3: instead of caching the turbo-tasks handle lazily on first poll, capture it eagerly when constructing ResolveRawVcFuture / ReadRawVcFuture. Construction always happens inside a turbo-tasks scope, so a single with_turbo_tasks(Arc::clone) at construction replaces the Option<Arc<...>> and the per-poll fast path. ReadRawVcFuture shares the Arc with its inner ResolveRawVcFuture rather than holding its own.

Match task_cache shard count to storage.map (commit 6)

Storage::new carefully computes a quadratic shard_amount (k=16) sized for num_cpus. On a 14-core box that's 4096 shards. The task_cache FxDashMap, however, fell through to dashmap's default heuristic (num_cpus * 4 ≈ 64 shards), so cache lookups contended on a fraction of the storage map's shard count. The cache is now constructed with with_capacity_and_hasher_and_shard_amount using the same value as storage.map.

Self-review polish (commit 7)

Renames LazyVecTinyVec, merges CellDependent into CellDependency, simplifies a few comments, drops a stale orphan-rule paragraph, and tightens the task_storage macro output.

Benchmark results

task_overhead benchmark (cargo bench -p turbo-tasks-backend --bench mod -- task_overhead/turbo) on an Apple M4 Pro (14 cores), --sample-size 200, run under caffeinate -dimsu nice -n -20 with Spotlight indexing paused and powermetrics co-recorded. No thermal pressure events recorded during either run. Significance: 95% CIs on the mean.

variant dur canary branch Δ significant?
turbo-uncached 1µs 9.77 µs 9.68 µs −1.0%
turbo-uncached 10µs 18.33 µs 18.26 µs −0.3%
turbo-uncached 100µs 108.58 µs 108.55 µs −0.0% noise
turbo-uncached 1000µs 1.01 ms 1.01 ms −0.1%
turbo-cached-same-keys 1µs 198.6 ns 191.9 ns −3.4%
turbo-cached-same-keys 10µs 197.2 ns 190.6 ns −3.3%
turbo-cached-same-keys 100µs 226.5 ns 208.1 ns −8.1%
turbo-cached-same-keys 1000µs 274.5 ns 267.0 ns −2.7% noise
turbo-cached-different-keys 1µs 233.8 ns 224.1 ns −4.2%
turbo-cached-different-keys 10µs 226.6 ns 225.7 ns −0.4% noise
turbo-cached-different-keys 100µs 305.3 ns 246.9 ns −19.1%
turbo-cached-different-keys 1000µs 331.4 ns 342.5 ns +3.3% noise
turbo-uncached-parallel 1µs 1.71 µs 1.76 µs +2.6% noise
turbo-uncached-parallel 10µs 1.63 µs 1.54 µs −5.8%
turbo-uncached-parallel 100µs 8.41 µs 7.88 µs −6.3%
turbo-uncached-parallel 1000µs 74.21 µs 74.53 µs +0.4% noise

The cached paths show the largest improvements — that's where every Vc await goes through the per-poll TLS/dispatch path most heavily. turbo-cached-different-keys/100 lands at −19.1% and turbo-cached-same-keys/100 at −8.1%. turbo-uncached-parallel/10 and /100 (−5.8%, −6.3%) reflect the task_cache shard fix reducing contention on the cache lookup path.

Where the workload itself dominates (turbo-uncached/100, /1000), busy_task is the entire budget and overhead is rounding error — flat is expected and correct. No significant regressions: the +% rows all have overlapping confidence intervals with their baselines.

vercel-site build results

branch

Benchmark 1: pnpm next build   --experimental-build-mode=compile
  Time (mean ± σ):     55.122 s ±  0.798 s    [User: 462.556 s, System: 64.588 s]
  Range (min … max):   54.064 s … 56.067 s    5 runs

canary

Benchmark 1: pnpm next build   --experimental-build-mode=compile
  Time (mean ± σ):     55.601 s ±  0.737 s    [User: 450.405 s, System: 67.746 s]
  Range (min … max):   54.855 s … 56.610 s    5 runs

Test plan

  • cargo test -p turbo-tasks-backend --lib — 46 passed
  • cargo test -p turbo-tasks-backend --tests — all integration tests passed
  • cargo check --workspace — clean
  • Bench comparison vs canary captured above

The `task_cache` map and the `persistent_task_type` field both stored
`Arc<CachedTaskType>`. `triomphe::Arc` is already a workspace dep used by
`ReadRef` / `SharedReference`, and `CachedTaskType` is never wrapped in a
`Weak`. triomphe saves one usize per allocation (no weak count) and avoids
the weak-count CAS in `drop_slow`.

Profiles showed `Arc<CachedTaskType>::drop_slow` taking ~1.4% of overhead
samples on the `task_overhead/turbo-uncached-parallel` benchmark. Migration
removes the weak-count CAS and shrinks the per-task allocation by 8 bytes.

Wraps `triomphe::Arc<CachedTaskType>` in a newtype `CachedTaskTypeArc` so
we can implement the foreign `bincode::Encode` / `Decode` traits — the
orphan rule blocks an `impl<Context> Decode<Context> for triomphe::Arc<...>`
because `Context` is not covered by a local type. Also forwards `Hash`,
`Eq`, `Borrow<CachedTaskType>`, `Display`, `Debug`, `Deref`, and `Clone`.
…ypes

The `cell_dependencies` set stored `(CellRef, Option<u64>)` tuples and
`cell_dependents` stored `(CellId, Option<u64>, TaskId)` tuples. The
`Option<u64>` cost a full 16 B (8 B discriminant + 8 B value, aligned),
making each element 24 B. Going through `AutoSet` brought the largest
`LazyField` variants to 48 B + 8 B discriminant = the enum's pinned size
of 56 B.

Replace the tuples with explicit enums `CellDependency` (`All(CellRef)` /
`Hash(CellRef, u64)`) and `CellDependent` (`All(CellId, TaskId)` /
`Hash(CellId, TaskId, u64)`). The layout algorithm reuses the niche on
`ValueTypeId` (`NonZero<u16>`) inside `CellRef.cell.type_id` for the
variant tag, so the elements drop to 24 B and the corresponding
`AutoSet`s to 40 B.

Net effect: `LazyField` shrinks from 56 B to 48 B, and `TaskStorage` from
344 B to 312 B (saves 32 B per task at `LAZY_INLINE_CAPACITY = 4`).

Updates the backend's wire format. Persistent caches are auto-invalidated
by the existing git-version-keyed cache directory scheme.
… futures

`ResolveRawVcFuture::poll` and `ReadRawVcFuture::poll` are on the per-poll
hot path of every Vc await. Each poll did:

- `with_turbo_tasks(...)` (a `LocalKey::with` on the `TURBO_TASKS`
  task-local) on every poll, and
- for `RawVc::LocalOutput` polls (the common case for `foo(bar).await`
  patterns): `try_read_local_output` which itself does a
  `CURRENT_TASK_STATE.with(...)` on every call.

Add lazy-initialized `tt: Option<Arc<dyn TurboTasksApi>>` and
`cts: Option<Arc<RwLock<CurrentTaskState>>>` fields on both futures.
Initialize on first poll via `with_turbo_tasks` / `current_task_state`
respectively, then reuse on every subsequent poll. The cached values are
valid for the lifetime of the future because a future cannot legally
migrate between turbo-tasks scopes.

The `LocalOutput` resolution path now bypasses both the `LocalKey::with`
on `CURRENT_TASK_STATE` and the `dyn TurboTasksApi` virtual dispatch in
`try_read_local_output`. A new free function
`try_read_local_output_in_state` is the underlying implementation; the
trait method on `TurboTasksApi` delegates to it.

Measured cumulative impact (across this and the preceding three commits)
on `task_overhead/turbo`:

- turbo-uncached-parallel/1µs:        -25%
- turbo-uncached-parallel/10µs:       -26%
- turbo-cached-different-keys/1µs:    -37%
- turbo-cached-different-keys/10µs:   -29%
- turbo-cached-different-keys/1000µs: -32%
- turbo-cached-same-keys/1µs:         -10%

Higher-duration variants (100µs and 1ms) where `busy_task` dominates show
no significant change, as expected.
`TaskStorage::lazy` only ever holds at most ~25 elements (one per declared
lazy field in the schema), and growth never exceeds that, so a `Vec`'s
24 B `(ptr, len, cap)` header is wasteful. Replace it with a hand-rolled
`LazyVec<T>`: `(ptr, len: u8, cap: u8)` plus 6 B padding = 16 B total.

`size_of::<TaskStorage>()` drops from 136 B to 128 B. The savings
multiplied by several million `TaskStorage`s live during a Next.js build
recovers dozens of MB of resident memory.

The API is intentionally a strict subset of `Vec` covering only what the
task-storage callers and the `#[task_storage]` macro emit need: `len`,
`is_empty`, `iter`, `iter_mut`, `push`, `swap_remove`, `last_mut`,
`Index/IndexMut`, `Extend`, `IntoIterator`, `reserve`, `Default`, `Debug`,
and `ShrinkToFit`. Capacity overflow above 255 panics, which we have plenty
of headroom against.
Copy link
Copy Markdown
Contributor Author

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

@github-actions github-actions Bot added created-by: Turbopack team PRs by the Turbopack team. Turbopack Related to Turbopack with Next.js. labels May 9, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

Tests Passed

Commit: dbced22

Comment thread turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs Outdated
@lukesandberg lukesandberg changed the title turbo-tasks: migrate CachedTaskType reference-counting to triomphe::Arc turbo-tasks: task-storage memory and dispatch-path overhead reductions May 9, 2026
Comment thread turbopack/crates/turbo-tasks-backend/src/data.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/data.rs Outdated
Comment thread turbopack/crates/turbo-tasks-macros/src/derive/task_storage_macro.rs Outdated
Comment thread turbopack/crates/turbo-tasks/src/backend.rs
Comment thread turbopack/crates/turbo-tasks/src/backend.rs
Comment thread turbopack/crates/turbo-tasks/src/backend.rs Outdated
Comment thread turbopack/crates/turbo-tasks/src/lazy_vec.rs Outdated
Comment thread turbopack/crates/turbo-tasks/src/raw_vc.rs
- Merge `CellDependent` into `CellDependency` — `CellDependent::All(CellId,
  TaskId)` is `CellRef { task: TaskId, cell: CellId }` with field order
  swapped. Both `cell_dependencies` and `cell_dependents` now store
  `CellDependency`; the `CellRef.task` field's meaning differs by direction
  (dependee vs. dependent) but the bits are identical. Drops the
  `CellDependent` enum and 4 accessor methods.

- Rename `LazyVec` to `TinyVec`. Move the file accordingly.

- `CachedTaskTypeArc`: trim the orphan-rule paragraph and `derive(Hash)`
  instead of the manual impl.

- Add `print_schema_sizes` test and update `test_schema_size` with the
  per-field breakdown and slack analysis (TaskStorage = 128 B; naive sum
  134 B; layout packs −6 B).

- Simplify the macro's `lazy` field comment.

- `raw_vc.rs`: add a TODO acknowledging the `cts` cache should be
  unnecessary if `try_read_local_output` returned a future itself.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 10, 2026

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

Metric Canary PR Change Trend
Cold (Listen) 814ms 811ms ███▁█
Cold (Ready in log) 800ms 802ms ▇▇▇▁█
Cold (First Request) 1.292s 1.290s ▅▆▃▂▅
Warm (Listen) 812ms 813ms ▃█▁█▅
Warm (Ready in log) 803ms 799ms ▃▂▃█▇
Warm (First Request) 627ms 629ms ▄▃▃██
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 811ms 810ms █████
Cold (Ready in log) 777ms 781ms █▇▇▆▇
Cold (First Request) 3.151s 3.185s █▆▄▄▅
Warm (Listen) 810ms 810ms █████
Warm (Ready in log) 781ms 780ms █▇▆▆▆
Warm (First Request) 3.178s 3.194s █▄▄▃▃

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 4.973s 4.884s ▅█▄▅▄
Cached Build 4.984s 4.864s ▄█▅▃▆
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 23.586s 23.737s ██▅▂▆
Cached Build 23.520s 23.818s █▆▄▁▆
node_modules Size 505 MB 505 MB ▁▇▇▇█
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
0-uf63qh441ym.js gzip 65.5 kB N/A -
02_eu35th8xp1.js gzip 161 B N/A -
04hm05ar7kldw.js gzip 5.73 kB N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0dvitrl5zg37g.js gzip 8.82 kB N/A -
0jnyo43x8vdm1.js gzip 167 B N/A -
0nqh1n0j6mmm9.js gzip 49.5 kB N/A -
0sf7ysou-72zd.js gzip 8.71 kB N/A -
0yh-uay5ufmjc.js gzip 70.8 kB N/A -
157abun3hwc_s.js gzip 10.3 kB N/A -
17rz06bb4qf0q.js gzip 156 B N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1gegnjx8b94t8.js gzip 156 B N/A -
1jj68jv9537mc.js gzip 13.8 kB N/A -
1jpaub6y8xlfr.js gzip 2.3 kB N/A -
1k4uph64t2cnr.js gzip 154 B N/A -
1mqf_oa8flo6h.js gzip 155 B N/A -
1ot0mvscrc_uf.js gzip 233 B N/A -
1ua4pgvcras7v.js gzip 157 B N/A -
1uxu6kee53sh_.js gzip 160 B N/A -
1yrhtrozledc8.js gzip 157 B N/A -
2_m3xv2uq3sjc.js gzip 1.46 kB N/A -
2-25idef4_4k4.js gzip 154 B N/A -
24y34mwgrkqp4.js gzip 8.78 kB N/A -
2c-fd4y1zozz8.js gzip 8.79 kB N/A -
2d7416h_xd36x.js gzip 8.71 kB N/A -
2extn3odmmem_.js gzip 12.9 kB N/A -
2fyhyy7niw9r6.js gzip 7.61 kB N/A -
2jp7mexj-ffhj.js gzip 155 B N/A -
2kjre_jvvxym-.js gzip 155 B N/A -
2lyuhit6rn8fy.js gzip 9.44 kB N/A -
2q0gr8wfr3jwl.js gzip 8.77 kB N/A -
2t9e75oz6r0zp.js gzip 8.76 kB N/A -
2uku_olcn15b7.js gzip 8.79 kB N/A -
30r8mm-46bdqy.js gzip 220 B 220 B
3cgkrmk_s_wpq.js gzip 156 B N/A -
3inab2jybr4k9.js gzip 450 B N/A -
3jkm5tdjvaf_q.js gzip 13.1 kB N/A -
3mt67agm5wp40.js gzip 10.6 kB N/A -
3saabek4kohwi.js gzip 10 kB N/A -
4189xmby9yu1p.js gzip 13.6 kB N/A -
turbopack-0e..eglm.js gzip 4.2 kB N/A -
turbopack-0g..c3gx.js gzip 4.2 kB N/A -
turbopack-0t..khtt.js gzip 4.2 kB N/A -
turbopack-12..ub3i.js gzip 4.21 kB N/A -
turbopack-13..n0-u.js gzip 4.2 kB N/A -
turbopack-1c..yqmk.js gzip 4.2 kB N/A -
turbopack-1k..3xbn.js gzip 4.2 kB N/A -
turbopack-1k..kwc3.js gzip 4.2 kB N/A -
turbopack-1o..ge4m.js gzip 4.18 kB N/A -
turbopack-1x..r-ki.js gzip 4.2 kB N/A -
turbopack-3_..svfh.js gzip 4.2 kB N/A -
turbopack-39..jio1.js gzip 4.2 kB N/A -
turbopack-3v..vys8.js gzip 4.2 kB N/A -
turbopack-3v..vyke.js gzip 4.2 kB N/A -
0_i7nqgx23st7.js gzip N/A 10 kB -
05e40c15cx1dd.js gzip N/A 7.61 kB -
06puhytyxk31p.js gzip N/A 8.82 kB -
0fca20ykv8h6s.js gzip N/A 155 B -
0m34gln_kt4fg.js gzip N/A 5.73 kB -
1_ekw0s2no-u6.js gzip N/A 153 B -
1b3r4o2n_avk6.js gzip N/A 155 B -
1bxqv8k7jvem-.js gzip N/A 159 B -
1g3q1ww01thnl.js gzip N/A 2.3 kB -
1hraqxuiymq6v.js gzip N/A 8.79 kB -
1ijznya56sya4.js gzip N/A 153 B -
1l9un1sl77287.js gzip N/A 1.46 kB -
1n-atmavce2tf.js gzip N/A 158 B -
1n4b65wcm1qu2.js gzip N/A 157 B -
1v4dom7n1w8nm.js gzip N/A 156 B -
21-eavqb1k_36.js gzip N/A 13.9 kB -
2147zgtf14z-q.js gzip N/A 234 B -
23bz3xsg-5-1s.js gzip N/A 8.71 kB -
27441mytv7pbm.js gzip N/A 9.43 kB -
2cjkwjgm1zcfs.js gzip N/A 8.71 kB -
2d58c6s3nmhua.js gzip N/A 156 B -
2scd8zaoyb8md.js gzip N/A 8.79 kB -
2st_qs6p_9us0.js gzip N/A 13.1 kB -
2zo2exm1d8qj1.js gzip N/A 13.6 kB -
3-sxo0xac1vlg.js gzip N/A 149 B -
31d304nyh0qnr.js gzip N/A 49.5 kB -
32fzho7lv-v6e.js gzip N/A 153 B -
352tr8j0win8e.js gzip N/A 168 B -
3hn75zuxly9az.js gzip N/A 10.3 kB -
3hqh7m128tvsn.js gzip N/A 8.77 kB -
3hqti_t-zy1x4.js gzip N/A 449 B -
3jz6mmnclh250.js gzip N/A 70.8 kB -
3mnawenie1flm.js gzip N/A 8.76 kB -
3r2a97foojskq.js gzip N/A 155 B -
3ubsozlu6zs38.js gzip N/A 10.6 kB -
41mf-x3mmsxae.js gzip N/A 12.9 kB -
42thqoo1bcm89.js gzip N/A 65.6 kB -
43iwfqjnx1cy_.js gzip N/A 8.78 kB -
turbopack-01..3l6a.js gzip N/A 4.2 kB -
turbopack-0d..yy8u.js gzip N/A 4.2 kB -
turbopack-0g..0hjy.js gzip N/A 4.2 kB -
turbopack-0v..4p4w.js gzip N/A 4.2 kB -
turbopack-0w..ixcj.js gzip N/A 4.2 kB -
turbopack-1g..6nwq.js gzip N/A 4.2 kB -
turbopack-1s..6f82.js gzip N/A 4.2 kB -
turbopack-1x..l758.js gzip N/A 4.18 kB -
turbopack-26..jr44.js gzip N/A 4.2 kB -
turbopack-2f..a86d.js gzip N/A 4.2 kB -
turbopack-2v..9fvd.js gzip N/A 4.2 kB -
turbopack-2x.._yqu.js gzip N/A 4.2 kB -
turbopack-31..b94z.js gzip N/A 4.21 kB -
turbopack-3k..lomm.js gzip N/A 4.2 kB -
Total 468 kB 468 kB ⚠️ +8 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 717 B 716 B
Total 717 B 716 B ✅ -1 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 433 B 432 B
Total 433 B 432 B ✅ -1 B

📦 Webpack

Client

Main Bundles
Canary PR Change
2258-HASH.js gzip 61.1 kB N/A -
2266-HASH.js gzip 4.69 kB N/A -
3317.HASH.js gzip 169 B N/A -
4866-HASH.js gzip 5.64 kB N/A -
9e302639-HASH.js gzip 62.7 kB N/A -
framework-HASH.js gzip 59.5 kB 59.5 kB
main-app-HASH.js gzip 255 B 254 B
main-HASH.js gzip 39.9 kB 39.9 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
175fd0fd-HASH.js gzip N/A 62.7 kB -
2596-HASH.js gzip N/A 5.63 kB -
34-HASH.js gzip N/A 61 kB -
5691.HASH.js gzip N/A 169 B -
9156-HASH.js gzip N/A 4.68 kB -
Total 236 kB 236 kB ✅ -82 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 181 B 182 B
css-HASH.js gzip 334 B 332 B
dynamic-HASH.js gzip 1.79 kB 1.81 kB
edge-ssr-HASH.js gzip 255 B 255 B
head-HASH.js gzip 351 B 348 B
hooks-HASH.js gzip 385 B 384 B
image-HASH.js gzip 580 B 580 B
index-HASH.js gzip 257 B 259 B
link-HASH.js gzip 2.51 kB 2.52 kB
routerDirect..HASH.js gzip 318 B 319 B
script-HASH.js gzip 387 B 386 B
withRouter-HASH.js gzip 316 B 316 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.97 kB 7.99 kB ⚠️ +19 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 126 kB 126 kB
page.js gzip 275 kB 270 kB 🟢 5.28 kB (-2%)
Total 401 kB 396 kB ✅ -5.49 kB
Middleware
Canary PR Change
middleware-b..fest.js gzip 619 B 614 B
middleware-r..fest.js gzip 155 B 155 B
middleware.js gzip 44.4 kB 44.9 kB 🔴 +461 B (+1%)
edge-runtime..pack.js gzip 842 B 842 B
Total 46.1 kB 46.5 kB ⚠️ +456 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 719 B 717 B
Total 719 B 717 B ✅ -2 B
Build Cache
Canary PR Change
0.pack gzip 4.45 MB 4.45 MB
index.pack gzip 115 kB 115 kB
index.pack.old gzip 114 kB 113 kB 🟢 1.35 kB (-1%)
Total 4.68 MB 4.67 MB ✅ -4 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 350 kB 350 kB
app-page-exp..prod.js gzip 194 kB 194 kB
app-page-tur...dev.js gzip 349 kB 349 kB
app-page-tur..prod.js gzip 194 kB 194 kB
app-page-tur...dev.js gzip 346 kB 346 kB
app-page-tur..prod.js gzip 192 kB 192 kB
app-page.run...dev.js gzip 346 kB 346 kB
app-page.run..prod.js gzip 192 kB 192 kB
app-route-ex...dev.js gzip 77.5 kB 77.5 kB
app-route-ex..prod.js gzip 52.9 kB 52.9 kB
app-route-tu...dev.js gzip 77.6 kB 77.6 kB
app-route-tu..prod.js gzip 52.9 kB 52.9 kB
app-route-tu...dev.js gzip 77.2 kB 77.2 kB
app-route-tu..prod.js gzip 52.7 kB 52.7 kB
app-route.ru...dev.js gzip 77.1 kB 77.1 kB
app-route.ru..prod.js gzip 52.7 kB 52.7 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 44.3 kB 44.3 kB
pages-api-tu..prod.js gzip 33.8 kB 33.8 kB
pages-api.ru...dev.js gzip 44.3 kB 44.3 kB
pages-api.ru..prod.js gzip 33.7 kB 33.7 kB
pages-turbo....dev.js gzip 53.7 kB 53.7 kB
pages-turbo...prod.js gzip 39.4 kB 39.4 kB
pages.runtim...dev.js gzip 53.6 kB 53.6 kB
pages.runtim..prod.js gzip 39.3 kB 39.3 kB
server.runti..prod.js gzip 63.2 kB 63.2 kB
use-cache-pr...dev.js gzip 69.7 kB 69.7 kB
use-cache-pr...dev.js gzip 69.7 kB 69.7 kB
use-cache-pr...dev.js gzip 68 kB 68 kB
use-cache-pr...dev.js gzip 68 kB 68 kB
Total 3.36 MB 3.36 MB
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/dbced223a31f247c5c55dbbedd10e80fcd325326/next

Commit: dbced22

…ead/Resolve futures

`ResolveRawVcFuture::tt` was an `Option<Arc<dyn TurboTasksApi>>` populated on
first poll via `with_turbo_tasks(Arc::clone)`. Since the future is always
constructed inside a turbo-tasks scope (entry points are `RawVc::resolve`
and `RawVc::into_read`), there's no reason to defer — capture eagerly in the
constructor and drop the `Option`. Removes one branch from the per-poll hot
path.

`ReadRawVcFuture` previously had its own `Option<Arc<dyn TurboTasksApi>>`,
adding a second `LocalKey::with` plus a redundant `Arc::clone`. Drop that
field entirely; phase 2 reuses the inner `ResolveRawVcFuture`'s `tt` via
`&self.resolve.tt`.

Also derive `PartialEq` + `Eq` on `CachedTaskTypeArc` instead of hand-rolling
the `ptr_eq` short-circuit. The cache map's bucket lookup uses `eq_components`
directly, so the short-circuit was unreachable.
`task_cache: FxDashMap::default()` falls through to dashmap's default shard
amount of `num_cpus * 4` (e.g. 64 shards on a 14-core machine), while
`storage.map` uses our `compute_shard_amount` heuristic which is quadratic
in worker count for a target ~3% collision probability (e.g. 4096 shards
on the same machine).

The 64× mismatch made `task_cache` lookups self-contend on every cache hit
even when `storage.map` accesses were uncontended. Profiles attributed
~10% of overhead samples to `dashmap::lock_exclusive_slow` on
`task_cache`'s shards, which is implausible for a properly sharded map at
this thread count.

Use `with_capacity_and_hasher_and_shard_amount` on `task_cache` with the
same `shard_amount` we already pass to `Storage::new`.
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.

1 participant