Skip to content

Allocation-free matrix construction + deprecate index helpers (#70) - #69

Merged
ctreffs merged 2 commits into
masterfrom
perf/allocation-free-construction
Oct 17, 2026
Merged

ctreffs merged 2 commits into
masterfrom
perf/allocation-free-construction

Conversation

@ctreffs

@ctreffs ctreffs commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Stack 4+5/5 — base: master. Also contains #70 (deprecate the public matrix index helpers), which was merged into this branch; see #70 for its description and tests.

Problem

  • Matrix{2,3,4}.init(_ column0, …) built an Array of columns and handed it to Storage(columns), so every matrix construction allocated. That includes every non-simd multiply, which ends with Mat4x4d(Vec4d(…), …).
  • init(_ values:) sliced the array into more arrays.
  • Iteration allocated too: the non-simd storages used [column0, …].flatMap { $0 }, the simd quaternions built [x, y, z, w], and elements went through the type-erased AnyIterator.

Change

  • New Storage*Protocol requirement: init(_ column0:, _ column1:, …). The simd matrix types (simd_float4x4 etc.) already have it, so their conformance needs no new code. The non-simd storages implement it, and their [Column] initializers delegate to it.
  • Matrix column, array and values initializers all go through the column initializer, with no intermediate arrays. The [Vector] and [Value] initializers keep their preconditions.
  • The non-simd storages drop their custom makeIterator. They are RandomAccessCollections and now use the default IndexingIterator<Self>.
  • simd quaternions iterate their vector with SIMDScalarIterator.
  • Matrix.elements is Array(storage), and SIMD.elements maps indices.

API note: the associated Iterator type of the non-simd storages and the simd quaternion storage changes, from IndexingIterator<[T]> to IndexingIterator<Self> / SIMDScalarIterator<SIMD4<T>>. This only breaks code that names those types explicitly.

Tests

MatrixConstructionTests checks, for all six matrix types, that the column, [column], [value] and array-literal initializers produce the same matrix in column-major layout, and that elements, Array(m) and for … in yield the same order. It also checks quaternion and SIMD element order. Both check-inlinable configurations still pass.

Benchmark

External consumer package, -c release, 5M mat4 * mat4:

before (#68) this PR
non-simd (Linux/Windows path) 156 ms 22 ms (≈7×)
simd 25.3 ms (median of 6) 25.3 ms (median of 6)

https://claude.ai/code/session_011WSUtoLptjea3s3iC5wTNC

@ctreffs
ctreffs force-pushed the perf/allocation-free-construction branch from 8f4cf0e to fad8f8c Compare October 17, 2026 11:31
@ctreffs
ctreffs force-pushed the perf/allocation-free-construction branch from fad8f8c to 5538593 Compare October 17, 2026 12:18
@ctreffs
ctreffs force-pushed the perf/allocation-free-construction branch from 5538593 to 1d23f99 Compare October 17, 2026 12:48
Base automatically changed from perf/inlinable to master October 17, 2026 13:10
@ctreffs
ctreffs force-pushed the perf/allocation-free-construction branch from 1d23f99 to 29586ba Compare October 17, 2026 13:11
@ctreffs
ctreffs force-pushed the perf/allocation-free-construction branch 2 times, most recently from 6010506 to d5eefff Compare October 17, 2026 13:54
Matrix{2,3,4}.init(_ column0, ...) built an Array of columns and passed
it to Storage(columns), so every matrix construction allocated, including
every non-SIMD matrix multiply. init(_ values:) sliced into arrays as
well, and iteration allocated through flatMap'd arrays (non-SIMD
storages), [x, y, z, w] arrays (simd quaternions) and AnyIterator
(`elements`).

- Add init(_ column0:, _ column1:, ...) as a Storage*Protocol
  requirement. The simd matrix types already provide it; the non-SIMD
  storages implement it, and their array initializers delegate to it.
- Matrix column/array/values initializers go through the column
  initializer; no intermediate arrays.
- Non-SIMD storages use the default IndexingIterator over themselves
  instead of building an array; simd quaternions iterate their vector via
  SIMDScalarIterator.
- Matrix `elements` is Array(storage); SIMD `elements` maps indices
  instead of going through AnyIterator.

Non-SIMD 4x4 multiply in an external release-mode consumer: 156 ms ->
22 ms for 5M multiplies. The SIMD path is unchanged.

Tests: MatrixConstructionTests checks that all construction paths agree
and produce column-major layout, and that matrices, quaternions and
vectors iterate in order.

Claude-Session: https://claude.ai/code/session_011WSUtoLptjea3s3iC5wTNC
@ctreffs
ctreffs force-pushed the perf/allocation-free-construction branch from d5eefff to 2ce7789 Compare October 17, 2026 14:05
@ctreffs ctreffs changed the title Construct matrices and iterate without heap allocations Allocation-free matrix construction + deprecate index helpers (#70) Oct 17, 2026
@ctreffs
ctreffs merged commit 46fa3bb into master Oct 17, 2026
15 checks passed
@ctreffs
ctreffs deleted the perf/allocation-free-construction branch October 17, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant