From 74eb6cf779026b4316a404920e162aece3a93924 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 08:27:16 +0000 Subject: [PATCH] D-RMAP-2: count the row-major B blocks in the BF16 byte total MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bf16Staged::bytes omitted b_rows, which stage() keeps for the polyfill entry, so the cost example reported 256 KiB for an object holding 384 KiB (matrix, packed blocks, row-major blocks, 128 KiB each). Adds a test pinning the total; plan §5a and the board entry corrected. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VyWZYKycy6MoYDxHRVnV5K --- .../board/entries/2026-10-20-d-rmap-2-tile-gemm-cell.md | 2 +- .claude/plans/d-rmap-2-context-tile-algebra-probe-v1.md | 2 +- crates/mork-pathmap-probe/src/tile_gemm.rs | 4 +++- crates/mork-pathmap-probe/tests/tile_gemm.rs | 9 +++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.claude/board/entries/2026-10-20-d-rmap-2-tile-gemm-cell.md b/.claude/board/entries/2026-10-20-d-rmap-2-tile-gemm-cell.md index c45e2a9eb..90c0e132b 100644 --- a/.claude/board/entries/2026-10-20-d-rmap-2-tile-gemm-cell.md +++ b/.claude/board/entries/2026-10-20-d-rmap-2-tile-gemm-cell.md @@ -5,7 +5,7 @@ ## Measured - On a 256 × 256 focus, `ndarray::simd::bf16_tile_gemm_16x16_packed` gives walk counts equal to a triple-loop oracle exactly, and `count > 0` equals Boolean composition (1 %, 10 %, 50 %, skewed hubs). 5 guards disable-verified. -- The tier that ran is the F32x16 FMA polyfill (no AMX, no `avx512_bf16` on this host): `A·A` about 19 ms. The bit arm: composition 2.6–25 µs, walk counts by popcount 165–192 µs, triangles 1.9–72 µs. Staging bits to BF16 (16 KiB to 256 KiB) costs 150–400 µs, more than the whole bit composition. +- The tier that ran is the F32x16 FMA polyfill (no AMX, no `avx512_bf16` on this host): `A·A` about 19 ms. The bit arm: composition 2.6–25 µs, walk counts by popcount 165–192 µs, triangles 1.9–72 µs. Staging bits to BF16 (16 KiB to 384 KiB) costs 150–400 µs, more than the whole bit composition. - The polyfill allocates per output element (`ISS-BF16-POLYFILL-PER-ELEMENT-ALLOC`), so this host measures allocation, not GEMM. ## Open diff --git a/.claude/plans/d-rmap-2-context-tile-algebra-probe-v1.md b/.claude/plans/d-rmap-2-context-tile-algebra-probe-v1.md index e9ac17ca0..dda50cbf6 100644 --- a/.claude/plans/d-rmap-2-context-tile-algebra-probe-v1.md +++ b/.claude/plans/d-rmap-2-context-tile-algebra-probe-v1.md @@ -108,7 +108,7 @@ Report: total bound cost, sorted-index creation/maintenance, native scan time, r | 1 % + 4 hubs | 2 706 | 20 273 | 2.6 µs | 165 µs | 5.8 µs | 150 µs | 19.7 / 19.4 ms | - **§3's exactness claim holds as measured.** BF16 walk counts equal the oracle exactly (`assert_eq!`, not a tolerance), and `count > 0` equals Boolean composition, on all four maps. -- **On this host the bit arm wins every cell**, including walk counts. Popcount of `row_i(A) ∧ row_j(Aᵀ)` gives the same counts as the GEMM in about 170–190 µs, against about 19 ms. Staging alone (bits to dense BF16, 16 KiB to 256 KiB) costs more than the whole bit composition. +- **On this host the bit arm wins every cell**, including walk counts. Popcount of `row_i(A) ∧ row_j(Aᵀ)` gives the same counts as the GEMM in about 170–190 µs, against about 19 ms. Staging alone (bits to dense BF16, 16 KiB to 384 KiB: the BF16 matrix, packed B blocks and row-major B blocks, 128 KiB each) costs more than the whole bit composition. - **The 19 ms is the polyfill's shape, not GEMM's.** `ndarray::simd_ops::bf16_tile_gemm_16x16` allocates two decode buffers per call, then one strided B-column buffer and one horizontal reduce per output element: about 257 heap allocations per 16 × 16 call, 65 000 per 256 × 256 product. The packed entry on this tier also unpacks and allocates per call. Recorded as `ISS-BF16-POLYFILL-PER-ELEMENT-ALLOC`; the row-major entry is no faster, so the unpack is not the cost. - **Two conclusions, kept apart.** (1) *Measured:* on this host's F32x16 polyfill tier, the bit arm wins every cell, and BF16 is exact. (2) *Not measured:* nothing here says anything about BF16 or AMX tile hardware performance. The polyfill's cost is dominated by allocation, so it is not even a lower bound on what `TDPBF16PS` or `VDPBF16PS` would cost. No layout decision may cite (1) as evidence about (2). - **Not measured:** AMX and `VDPBF16PS` hosts; dense relations above one focus (blocked 256 × 256 tiles); weighted or evidence-bearing relations, which §3 excludes anyway. diff --git a/crates/mork-pathmap-probe/src/tile_gemm.rs b/crates/mork-pathmap-probe/src/tile_gemm.rs index 63535f4bf..3de4ee91e 100644 --- a/crates/mork-pathmap-probe/src/tile_gemm.rs +++ b/crates/mork-pathmap-probe/src/tile_gemm.rs @@ -183,7 +183,8 @@ impl Bf16Staged { } } - /// Bytes held: the BF16 matrix and the packed blocks. + /// Bytes held: the BF16 matrix, the packed blocks and the row-major + /// blocks kept for the polyfill entry. pub fn bytes(&self) -> usize { self.a.len() * 2 + self @@ -191,6 +192,7 @@ impl Bf16Staged { .iter() .map(|b| b.data().len() * 2) .sum::() + + self.b_rows.iter().map(|b| b.len() * 2).sum::() } /// Walk counts through the row-major polyfill entry (no pack, no unpack). diff --git a/crates/mork-pathmap-probe/tests/tile_gemm.rs b/crates/mork-pathmap-probe/tests/tile_gemm.rs index 6e874d3ad..785c28c10 100644 --- a/crates/mork-pathmap-probe/tests/tile_gemm.rs +++ b/crates/mork-pathmap-probe/tests/tile_gemm.rs @@ -85,3 +85,12 @@ fn composition_respects_direction() { assert!(!c.get(2, 0)); assert_eq!(c.edges(), 1); } + +/// The reported BF16 size counts every buffer the staged object keeps: the +/// 256 × 256 matrix, the packed B blocks and the row-major B blocks, each +/// 256 × 256 BF16 values. +#[test] +fn bf16_bytes_count_every_retained_buffer() { + let staged = Bf16Staged::stage(&BitMat::from_edges([(0, 1), (1, 2)])); + assert_eq!(staged.bytes(), 3 * 256 * 256 * 2); +}