Skip to content

perf: avoid iterator allocations in Frustum.setFromMat4#8775

Merged
willeastcott merged 1 commit into
mainfrom
perf/frustum-skip-iterator-destructure
May 26, 2026
Merged

perf: avoid iterator allocations in Frustum.setFromMat4#8775
willeastcott merged 1 commit into
mainfrom
perf/frustum-skip-iterator-destructure

Conversation

@willeastcott
Copy link
Copy Markdown
Contributor

@willeastcott willeastcott commented May 25, 2026

Summary

Frustum.setFromMat4 was destructuring a Float32Array with const [m00, ..., m33] = matrix.data. JavaScript array destructuring uses the iterator protocol, so on every call V8 allocates one { value, done } result object per element (16 per call) plus the iterator object itself.

This function fires per-camera per-layer per-frame, so the iterator-result objects accumulated into measurable GC pressure on a representative scene workload — and even pulled additional V8 HeapNumber boxing into the Plane.normalize subtree downstream.

Replacing the destructuring with explicit indexed reads is behaviour-identical and eliminates the iterator allocations entirely.

Technical details

// before
const [
    m00, m01, m02, m03,
    m10, m11, m12, m13,
    m20, m21, m22, m23,
    m30, m31, m32, m33
] = matrix.data;

// after
const d = matrix.data;
const m00 = d[0], m01 = d[1], m02 = d[2], m03 = d[3];
const m10 = d[4], m11 = d[5], m12 = d[6], m13 = d[7];
const m20 = d[8], m21 = d[9], m22 = d[10], m23 = d[11];
const m30 = d[12], m31 = d[13], m32 = d[14], m33 = d[15];

Measured impact

Captured via V8 sampling heap profiler (node:inspector HeapProfiler.startSampling) at 128 B interval, 1800 frames of a representative scene rendered against NullGraphicsDevice:

Metric Before After Δ
Frustum.setFromMat4 subtree (selfSize incl. children) 98,160 B 36,216 B −63%
Iterator .next() attribution under this site ~22 KB 0 B −100%

A targeted microbenchmark of Frustum.setFromMat4 × 20,000 calls drops from ~56 B/iter to ~45 B/iter. The residual is V8 HeapNumber boxing inside the downstream Plane.normalize arithmetic — unrelated to this change.

Public API changes

None.

Test plan

  • Existing unit tests pass (npm test)
  • Existing examples that exercise area-light culling and shadow frustums render identically on both WebGL2 and WebGPU

`const [m00, ..., m33] = matrix.data` destructures the Float32Array via
the iterator protocol, allocating a `{ value, done }` object per element
(16 per call) plus the iterator itself. `Frustum.setFromMat4` runs per
camera per layer per frame, so this accumulates into measurable GC
pressure under V8 sampling profiling of a typical scene.

Replace the destructuring with explicit indexed reads — same behaviour,
no iterator allocation, and the subtree that Frustum.setFromMat4 attributes
to (including the downstream Plane.normalize HeapNumber boxing) shrinks
in step.

Measured impact on a typical-scene allocation profile (1800 frames,
NullGraphicsDevice, V8 sampling heap profiler @ 128 B interval):

  - Frustum.setFromMat4 subtree (selfSize incl. children): 98,160 B -> 36,216 B (-63%)
  - Iterator `.next()` attribution under this site: ~22 KB -> 0 B

A targeted microbenchmark of `Frustum.setFromMat4` x 20,000 calls drops
from ~56 B/iter to ~45 B/iter; the residual is V8 HeapNumber boxing
inside `Plane.normalize` arithmetic and is unrelated to this change.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes a hot path in the math/culling layer by removing Float32Array destructuring from Frustum.setFromMat4, avoiding per-call iterator-related allocations and reducing GC pressure during per-frame frustum updates.

Changes:

  • Replaced const [m00, ..., m33] = matrix.data destructuring with explicit indexed reads from matrix.data.
  • Introduced a local d alias for matrix.data to keep the indexed reads concise.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@willeastcott willeastcott merged commit c5f8e5c into main May 26, 2026
9 checks passed
@willeastcott willeastcott deleted the perf/frustum-skip-iterator-destructure branch May 26, 2026 07:36
@AlexAPPi
Copy link
Copy Markdown
Contributor

Down with syntactic sugar that harms performance!!!!

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

Labels

performance Relating to load times or frame rate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants