Skip to content

M7d: symmetry storage halving — ceil(L/2) stored rows, mirrored dots - #10

Merged
tap merged 2 commits into
mainfrom
claude/m7-symmetry-halving
Jul 24, 2026
Merged

M7d: symmetry storage halving — ceil(L/2) stored rows, mirrored dots#10
tap merged 2 commits into
mainfrom
claude/m7-symmetry-halving

Conversation

@tap

@tap tap commented Jul 24, 2026

Copy link
Copy Markdown
Owner

⚠️ Merge order

This PR bumps the DspTap submodule pin to the dot_row_reversed commit — merge DspTap #6 first so the pinned commit is reachable from its main.

What

Lever 3 of the M7 campaign, in two PRs per the substrate discipline (kernel in DspTap first, consumer here).

The linear-phase prototype satisfies b_p[t] = b_{L−1−p}[T−1−t] — branch p is branch L−1−p tap-reversed. So:

  • basic_phase_table now stores only ⌈L/2⌉ rows (74 of 147 down, 80 of 160 up; odd L keeps its self-symmetric middle branch as a stored row). A mirrored phase dots its partner's stored row backward via tap::dsp::dot_row_reversed — same products, same accumulation order, bit-identical outputs to a full table.
  • Quantization is canonical over the stored half, so the mirror is exact by construction, and the fixed-point exact-unity row sums carry over (reversal preserves the coefficient multiset).
  • Storage, pinned by test: economy f32 44.8 → 22.5 KiB (down), 27.5 → 13.8 KiB (up); transparent 105.7 → 53.2 / 60.0 → 30.0 KiB; Q15 halves those again.
  • The every-phase table battery now runs through the same stored_row/is_mirrored pairing the hot path uses, plus an exhaustive mirror-identity sweep (every phase × every tap).

The part the ratchet earned

First measurement: M55 up_q15 +3.3% REGRESSION. Diagnostics isolated it — a build with both arms calling the forward kernel cost the same, so the reversed kernel is free and the second inlined dot expansion in the walk's loop body is what broke Arm's unrolled codegen. Outlining the mirrored arm fixed Arm — and regressed Hexagon (down_q31 +3.3% called vs +2.7% inlined: hexagon-clang keeps both expansions tight and pays for the call instead). So the mirrored-arm out-lining is gated per target (TAP_RATIO_MIRRORED_DOT_ATTR), the same measured-per-target pattern as the tap::dsp kernel gates.

Final result: zero baseline changes on all three targets — the halving is compute-free within the ±3% gate. Worst residual: Hexagon down_q31 +2.7% (rides as slack); Arm came out slightly ahead (M33 Q31 −2.5%, M55 up_q15 −1.1%).

Verification

  • 58/58 host (gcc and clang -Werror); scipy vectors, pull-parity, custom-taps impulse all bit-exact.
  • M55 and M33 bare-metal suites green — the M33 leg is the on-target bit-exactness proof of DspTap's SMLALDX swapped-lane pairing (the impulse-reproduces-table sweep hits every mirrored phase with the intrinsic active).
  • Hexagon 56/56 under emulation; bluetooth_bridge identical; clang-tidy clean.
  • PLAN §4 storage table + §7 lever entry updated.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ldeq57sBySx2nFTQsGcQB6


Generated by Claude Code

claude added 2 commits July 24, 2026 15:04
Lever 3 of the optimization campaign (PLAN section 7), the RatioTap half
of the two-PR lever (DspTap grew dot_row_reversed first; this bumps the
pin to c2bfbdb and consumes it).

The linear-phase prototype gives b_p[t] = b_{L-1-p}[T-1-t]: branch p is
branch L-1-p tap-reversed. The table now stores only ceil(L/2) rows (74
of 147 down, 80 of 160 up) and a mirrored phase dots its partner's
stored row backward via tap::dsp::dot_row_reversed — same products, same
accumulation order, bit-identical outputs. Quantization is canonical
over the stored half, so the mirror is exact by construction and the
fixed-point exact-unity row sums carry over (reversal preserves the
multiset). Storage, pinned by test: economy f32 44.8 -> 22.5 KiB (down),
27.5 -> 13.8 KiB (up); transparent 105.7 -> 53.2 / 60.0 -> 30.0 KiB;
Q15 halves those again.

Compute cost: ZERO baseline changes on all three targets — with one
codegen subtlety the ratchet caught and the diagnostics isolated:
inlining both dot arms in the walk broke Arm's unrolled forward codegen
(M55 up_q15 +3.3%, reversed kernel itself measured free), while
outlining the mirrored arm broke Hexagon's (down_q31 +3.3% called, +2.7%
inlined). The mirrored-arm out-lining is therefore gated per target
(TAP_RATIO_MIRRORED_DOT_ATTR), the same measured-per-target pattern as
the tap::dsp kernel gates. Worst residual rides inside the two-sided
gate (Hexagon down_q31 +2.7%); Arm came out slightly ahead (M33 Q31
-2.5%, M55 up_q15 -1.1%).

Verification: 58/58 host (gcc + clang -Werror); M55 and M33 bare-metal
suites green — the M33 leg is the on-target bit-exactness proof of the
SMLALDX swapped-lane pairing (impulse-reproduces-table sweeps every
mirrored phase with the intrinsic active); Hexagon 56/56; every-phase
table battery extended with the exhaustive mirror-identity sweep;
bluetooth_bridge identical; clang-tidy clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ldeq57sBySx2nFTQsGcQB6
The DspTap merge rewrote the branch commit (c2bfbdb -> dfbe18d, identical
tree); the pin must be reachable from dsptap main or recursive submodule
checkouts break once the branch ref is pruned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ldeq57sBySx2nFTQsGcQB6
@tap
tap merged commit e56f704 into main Jul 24, 2026
20 checks passed
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.

2 participants