Skip to content

Point mass matrix without skew's zero products; box hulls and the block hash - #108

Merged
nicolas-maman merged 2 commits into
mainfrom
perf/point-mass-matrix
Oct 5, 2026
Merged

nicolas-maman merged 2 commits into
mainfrom
perf/point-mass-matrix

Conversation

@nicolas-maman

@nicolas-maman nicolas-maman commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

point_mass_matrix formed skew(r) i skew(r) through two full 3x3 products per body. GCC called it instead of inlining it, and the sampling profile (scripts/profile.sh) put 16.5% of the joint grid in it. It now leaves out the products with skew's zeros: about a quarter of the multiplies, and two matrices passed by pointer instead of copied.

Results are unchanged

Each surviving term is the one mul_mm forms, added in the same order, and adding an exact zero product to a finite sum changes nothing. So the matrix is the same to the bit. A new check in tests/test_joint_solver.ae compares it with the full product over 2,000 random anchors, masses and inertias, at three scales. Perturbing one term makes the check fail on all 2,000.

Measured

Joint grid alone, interleaved against main, 4 rounds, one thread:

stage main this branch Box3D e77352c
solve 3.87-4.23 ms 3.09-3.38 3.33
relax 3.87-4.22 3.01-3.22 3.22
step 13.6-14.5 11.5-12.7 10.4-10.6

Same height sum. The joint solve and relax are now level with Box3D main's. The motor, revolute and weld joints use the same matrix. scripts/test.sh passes 28/28 locally on Aether 0.779.0.

Also in this PR: box hulls and the block hash

  • Box hulls 4.6x faster. The box's topology came from helpers that held their table as a local array, so every call copied the whole table onto the stack, 96 calls per box. It's now written out as literals in the builder.
  • Block hash in four lanes. hash_bytes ran every word through one serial chain of multiplies; four lanes now overlap. Mesh and height field builds hash their blocks too.
  • Aliasing bug fixed. hash_bytes now reads words through memcpy (native.load_word). Reading through an integer pointer broke C's aliasing rule for blocks written as doubles and structs. Once the faster hash was inlined, GCC moved the reads ahead of the writes, and the compound benchmark's material table found 253 materials instead of 2. Main has the same latent fault; it stays hidden only because the hash isn't inlined there. tests/test_compound.ae gains a two-material, 2,000-child check on the benchmark's scene. In the test program GCC inlines differently, so that check did not fail without the fix; the benchmark is what showed the bug.
main this branch Box3D
2,000 box hulls 2.88-3.01 ms 0.61-0.64 0.17
10 median mesh builds 234-260 207-224 134
10 height field builds 172-181 152-161 133

Holding the tables as local arrays in the builder slowed the compound benchmark's rays by about 6%, through GCC's inlining of its main. Written out as literals, the rays stay at main's 26.5-27.1 ms. Hashes are only compared within a run (dedup and equality), so changing the hash function changes no stored data's meaning.

point_mass_matrix formed skew(r) i skew(r) through two full 3x3
products per body, and GCC called it rather than inlining it: a sixth of
the joint grid's samples. It now leaves out the products with skew's
zeros. Each surviving term is the one mul_mm forms, added in the same
order, and adding an exact zero product changes nothing, so the matrix
is the same to the bit; a new test checks that over 2,000 random
anchors, masses and inertias.

Joint grid, interleaved against main, 4 rounds: solve 3.87-4.23 to
3.09-3.38 ms, relax 3.87-4.22 to 3.01-3.22, step 13.6-14.5 to
11.5-12.7, the same height sum. The solve and relax are now level with
Box3D main's (3.33 and 3.22). The motor, revolute and weld joints use the same
matrix.
The box hull's topology came from helpers holding their table as a local
array, so every call copied the whole table onto the stack (96 calls a
box). It is now written out in the builder. hash_bytes ran every word
through one chain of multiplies; it now runs four lanes, so the chains
overlap. 2,000 box hulls: 2.9 ms to 0.62 (Box3D 0.17). Mesh and height
field builds, which hash their blocks too: median mesh 234-260 to
207-224 ms, height field 172-181 to 152-161.

hash_bytes reads its words through memcpy (native.load_word): read
through an integer pointer, as before, they break C's aliasing rule
for blocks written as doubles and structs. Once the hash was inlined,
GCC moved the reads ahead of the writes, and the compound benchmark's
material table found 253 materials instead of 2. Main's hash has the
same latent fault, unseen only because it was not inlined.

Writing the topology as tables in the builder instead slowed the
compound benchmark's rays by ~6% through GCC's inlining of its main;
written out as literals, the rays are where they were.
@nicolas-maman nicolas-maman changed the title Point mass matrix without skew's zero products Point mass matrix without skew's zero products; box hulls and the block hash Oct 5, 2026
@nicolas-maman
nicolas-maman merged commit 3547f2b into main Oct 5, 2026
1 check passed
@nicolas-maman
nicolas-maman deleted the perf/point-mass-matrix branch October 6, 2026 23:09
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