Skip to content

perf(compiler-cli): use null instead of {} in ngDevMode spread fallback#68828

Open
arturovt wants to merge 1 commit into
angular:mainfrom
arturovt:perf/compiler_cli_null_instead_object
Open

perf(compiler-cli): use null instead of {} in ngDevMode spread fallback#68828
arturovt wants to merge 1 commit into
angular:mainfrom
arturovt:perf/compiler_cli_null_instead_object

Conversation

@arturovt
Copy link
Copy Markdown
Contributor

The implicit signal debug name transform emits a conditional spread in the prod branch of each ngDevMode ternary:

  { ...(ngDevMode ? { debugName: "x" } : {}) }

The falsy branch was {}, which allocates a short-lived object in V8's young generation on every class instantiation. In large apps with many signal-based properties this raises the minor GC (scavenger) frequency on the startup critical path. TurboFan's escape analysis could eliminate the allocation in theory, but proving escape requires inlining the callee — not guaranteed at thousands of distinct call sites.

Change the fallback to null. ECMA-262 §13.2.5 defines {...null} as a no-op, identical in behavior to {...{}}. V8 hits a fast nullish check in the object-spread runtime and returns immediately with no heap allocation.

Only the object-spread path is affected. The array-spread fallback (...(ngDevMode ? [...] : [])) is unchanged — [...null] would throw.

@angular-robot angular-robot Bot added area: performance Issues related to performance area: compiler Issues related to `ngc`, Angular's template compiler labels May 20, 2026
@ngbot ngbot Bot added this to the Backlog milestone May 20, 2026
@JeanMeche JeanMeche requested a review from hawkgs May 20, 2026 18:22
@arturovt arturovt marked this pull request as ready for review May 20, 2026 20:18
@pullapprove pullapprove Bot requested a review from atscott May 20, 2026 20:18
The implicit signal debug name transform emits a conditional spread in
the prod branch of each ngDevMode ternary:

  { ...(ngDevMode ? { debugName: "x" } : {}) }

The falsy branch was `{}`, which allocates a short-lived object in V8's
young generation on every class instantiation. In large apps with many
signal-based properties this raises the minor GC (scavenger) frequency
on the startup critical path. TurboFan's escape analysis could eliminate
the allocation in theory, but proving escape requires inlining the
callee — not guaranteed at thousands of distinct call sites.

Change the fallback to `null`. ECMA-262 §13.2.5 defines `{...null}` as
a no-op, identical in behavior to `{...{}}`. V8 hits a fast nullish
check in the object-spread runtime and returns immediately with no heap
allocation.

Only the object-spread path is affected. The array-spread fallback
(`...(ngDevMode ? [...] : [])`) is unchanged — `[...null]` would throw.
@arturovt arturovt force-pushed the perf/compiler_cli_null_instead_object branch from 6c3f2d4 to 91a3274 Compare May 21, 2026 11:24
@JoostK
Copy link
Copy Markdown
Member

JoostK commented May 21, 2026

This should end up being optimized out entirely, so this change wouldn't actually help perf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: compiler Issues related to `ngc`, Angular's template compiler area: performance Issues related to performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants