next.js
2d2f501f - Store RouteTree slots in a Map to keep slot access monomorphic (#96168)

Commit
29 days ago
Store RouteTree slots in a Map to keep slot access monomorphic (#96168) ### Root cause Parallel route slot names are app-defined, so a `RouteTree.slots` stored as a plain object has an unbounded set of hidden classes: every distinct combination of slot names (`{children}`, `{children, side}`, ...) produces a different shape. Keyed access over these objects makes the inline cache megamorphic. This was the single `ic-megamorphic` finding reported by `pnpm bench:deopt --scenario segment-cache`, at the keyed store in `convertTreePrefetchToRouteTree` (`cache.ts:1531`, keys `children`, `side`). This PR converts `RouteTree.slots` from `{ [parallelRouteKey: string]: RouteTree }` to `Map<string, RouteTree>`. All reads, writes, and iterations over RouteTree slots now go through monomorphic `Map` method calls, and iteration order is preserved (Map iterates in insertion order, matching plain-object string-key order). ### Scope This was an approved design decision: `RouteTree` is an in-memory-only structure, so only it changes. `CacheNode.slots` (spread into JSX props) and the wire formats (`TreePrefetch.slots`, `FlightRouterState[1]`) remain plain objects; the existing conversion functions bridge them into and out of the Map. ### Before/after findings diff ```diff --- bench/deopt/artifacts/slots-map-before/findings.txt +++ bench/deopt/artifacts/slots-map-after/findings.txt @@ -high ic-megamorphic packages/next/src/client/components/segment-cache/cache.ts e keys: children, side +high ic-megamorphic packages/next/src/client/components/segment-cache/scheduler.ts e keys: children, side @@ +info deopt-dependency-change packages/next/src/client/components/segment-cache/scheduler.ts pingSharedPartOfCacheComponentsTree dependent prototype chain changed +info deopt-eager packages/next/src/client/components/segment-cache/scheduler.ts _heapIndex Insufficient type feedback for generic named access +info deopt-eager packages/next/src/client/components/segment-cache/scheduler.ts cancelPrefetchTask Insufficient type feedback for generic named access +info ic-polymorphic packages/next/src/client/components/segment-cache/cache.ts eR keys: push ``` The targeted finding at `cache.ts:1531` is cleared. The remaining megamorphic site (stable across two runs) is `scheduler.ts:1068:23` in `pingSharedPartOfCacheComponentsTree` — the keyed load `oldTreeChildren[parallelRouteKey]`, where `oldTreeChildren` is `FlightRouterState[1]`, i.e. the wire-format plain object that is intentionally out of scope for this change. That access existed before this PR; previously the `for...in` loop key qualified the sibling load for V8's fast enum-cache path, and with Map iteration keys it now surfaces as a generic keyed load. Eliminating it would require changing the `FlightRouterState` children format, which is a separate decision. The new info-level lines are one-time warm-up/prototype-registration deopts from the introduced `Map` usage, not steady-state IC misses. ### Tests - `test/e2e/app-dir/segment-cache/basic` (start-turbo): 10 passed, 1 skipped - `test/e2e/app-dir/segment-cache/optimistic-routing-parallel-slot-catchall-regression` (start-turbo): 1 passed - `test/e2e/app-dir/segment-cache/cached-navigations` (start-turbo): 16 passed - `pnpm --filter=next types` passes (also re-run after rebasing onto canary)
Author
Parents
Loading