Skip to content

Commit 5f2c7fc

Browse files
committed
PackedBf16B: explicit little-endian byte contract (as_le_bytes / from_le_bytes)
The persistence/mailbox face of the packed tile buffer, per the lance-graph SoaEnvelope discipline (envelope bytes are LE from creation to tombstone): - as_le_bytes(): zero-cost &[u16] -> &[u8] reinterpret; LE by construction since this module is cfg(x86_64) and x86_64 is LE-only. - from_le_bytes(): endian-correct rebuild via u16::from_le_bytes (compiles to a plain copy on LE targets). - Contract test asserts byte 2i == low byte of lane i (true LE, not just native) and that a GEMM over the roundtripped buffer stays bit-exact. First brick of the SoA-Morton batch-writer / write-hiding design; the writer itself lands lance-graph-side. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E7wPMi796LPvp4A6JubdWH
1 parent 8fe0d2d commit 5f2c7fc

2 files changed

Lines changed: 72 additions & 0 deletions

File tree

.claude/blackboard.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ through VDPBF16PS instead of decode+FMA (bit-exact within the integer
3939
boundary; BF16-precision-class accumulation-order differences on general
4040
floats, same as any tier change).
4141

42+
[ADDED, same day] **LE byte contract on `PackedBf16B`** (operator "Go" —
43+
first brick of the SoA-Morton batch-writer / write-hiding design):
44+
`as_le_bytes()` (zero-cost reinterpret; LE by construction — the module
45+
is x86_64-only) + `from_le_bytes()` (endian-correct anywhere, plain copy
46+
on LE). This is the persistence/mailbox face per lance-graph's
47+
SoaEnvelope discipline (envelope bytes LE from creation to tombstone).
48+
Test `le_byte_view_roundtrips_and_is_truly_le` asserts byte 2i = low
49+
byte of lane i AND that a GEMM over the roundtripped buffer stays
50+
bit-exact. 9/9 lib tests green. Next bricks (lance-graph side): batch
51+
writer flushing tile buffers as envelope tenants; write-hiding = stage
52+
morsel N+1's VNNI writes while morsel N's tiles compute.
53+
4254
## 2026-07-02 — 1BRC-on-substrate probe (`examples/onebrc_cascade_probe.rs`)
4355

4456
1BRC workload (min/mean/max per station) restated on the substrate, as a

src/hpc/bf16_tile_gemm.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,42 @@ impl PackedBf16B {
153153
pub fn data_mut(&mut self) -> &mut [u16] {
154154
&mut self.data
155155
}
156+
157+
// ── Little-endian byte contract ─────────────────────────────────────
158+
//
159+
// The persistence/mailbox face of the buffer (the lance-graph
160+
// SoaEnvelope discipline: envelope bytes are LE from creation to
161+
// tombstone). This module is `cfg(target_arch = "x86_64")`, an LE-only
162+
// ISA, so the native `u16` lanes ARE the LE bytes — the view below is
163+
// a zero-cost reinterpret, and the contract costs nothing to state
164+
// explicitly. The read side decodes with `u16::from_le_bytes`, which
165+
// is endian-correct anywhere and compiles to a plain copy here.
166+
167+
/// The buffer as **little-endian** bytes — `2·k·16` bytes, each bf16
168+
/// lane low-byte-first. Zero-copy on this (LE-only) architecture. This
169+
/// is the face a batch writer hands to columnar storage; pair with
170+
/// [`Self::from_le_bytes`] to round-trip.
171+
pub fn as_le_bytes(&self) -> &[u8] {
172+
// SAFETY: `&[u16]` → `&[u8]` reinterpret is always valid (alignment
173+
// requirement shrinks from 2 to 1; length doubles within the same
174+
// allocation). Byte order is LE by construction: x86_64 is
175+
// little-endian and this module is x86_64-only.
176+
unsafe { core::slice::from_raw_parts(self.data.as_ptr() as *const u8, self.data.len() * 2) }
177+
}
178+
179+
/// Rebuild from **little-endian** bytes (the inverse of
180+
/// [`Self::as_le_bytes`]). `bytes.len()` must be `2·k·16` and `k` a
181+
/// multiple of 32. Endian-correct on any architecture; compiles to a
182+
/// straight copy on LE targets.
183+
pub fn from_le_bytes(bytes: &[u8], k: usize) -> Self {
184+
assert_eq!(k % 32, 0, "K must be multiple of 32");
185+
assert_eq!(bytes.len(), 2 * k * 16, "expected 2·K·16 LE bytes");
186+
let data = bytes
187+
.chunks_exact(2)
188+
.map(|p| u16::from_le_bytes([p[0], p[1]]))
189+
.collect();
190+
PackedBf16B { data, k }
191+
}
156192
}
157193

158194
/// `C[16, 16] += A[16, K] × B[K, 16]` with B pre-packed — the hot-loop
@@ -450,6 +486,30 @@ mod tests {
450486
assert_eq!(c_packed, c_ref, "packed tier [{}] not exact", bf16_tile_gemm_tier());
451487
}
452488

489+
#[test]
490+
fn le_byte_view_roundtrips_and_is_truly_le() {
491+
let k = 64;
492+
let (_, b, _) = integer_case(k);
493+
let packed = PackedBf16B::pack(&b, k);
494+
495+
// Contract: byte 2i is the LOW byte of lane i (little-endian),
496+
// independent of how the platform happens to lay out u16.
497+
let bytes = packed.as_le_bytes();
498+
assert_eq!(bytes.len(), 2 * k * 16);
499+
for (i, &lane) in packed.data().iter().enumerate() {
500+
assert_eq!([bytes[2 * i], bytes[2 * i + 1]], lane.to_le_bytes(), "lane {i} not LE in byte view");
501+
}
502+
503+
// Round-trip: bytes → PackedBf16B → identical lanes AND identical
504+
// GEMM result (the envelope face and the compute face agree).
505+
let rebuilt = PackedBf16B::from_le_bytes(bytes, k);
506+
assert_eq!(rebuilt.data(), packed.data());
507+
let (a, _, c_ref) = integer_case(k);
508+
let mut c = vec![0.0f32; 256];
509+
bf16_tile_gemm_16x16_packed(&a, &rebuilt, &mut c);
510+
assert_eq!(c, c_ref, "GEMM over LE-roundtripped buffer not exact");
511+
}
512+
453513
#[test]
454514
fn packed_entry_accumulates() {
455515
// C += A·B semantics: pre-existing C values must survive.

0 commit comments

Comments
 (0)