Skip to content

frost(sdpa): THD zero-host-read extents break non-packed views; unit decode is extent-sensitive (regression from #606, also in #608) #613

Description

@vedaanta

Summary

#606's zero-host-read THD execute derives the packed token extents from a host-side buffer heuristic (t_q = min(q_buf.numel() // q_ts, o_buf.numel() // o_ts), same for KV). Two independent problems follow, one breaking non-packed views, one showing the kernel is extent-sensitive in a way no host heuristic can satisfy. Found while restacking the torch-ops work (#517/#554) on #608; SM100 (merged) and #608's SM120 path (buf.numel() // ts as well) both carry it.

Bug 1 — numel() // token_stride under-claims non-packed views

A K/V slice of a kv-interleaved [T, 2, H, D] buffer (the torch.nn.attention.varlen fused-projection layout, #526's headline case) holds T tokens but only T*H*D of the record's elements, so the derived extent halves. Same for any declared-stride gap view. Result: TMA extents cut off half the tokens — silently wrong O.

Repro (fails on develop, FROST route; backend route passes):

cd test/python && CUDNN_FRONTEND_ENABLE_FROST_ENGINES=1 pytest test_mhas_v2.py::test_repro --repro \
"{'data_type': 'torch.float16', 'diag_align': 'cudnn.diagonal_alignment.TOP_LEFT', 'batches': 2, 'h_q': 8, 'h_k': 8, 'h_v': 8, 'd_qk': 128, 'd_v': 128, 's_q': 256, 's_kv': 256, 'seq_len_q': [192, 256], 'seq_len_kv': [192, 256], 'is_ragged': True, 'is_padding': True, 'is_infer': True, 'with_ragged_token_gap': True, 'rng_geom_seed': 1, 'rng_data_seed': 2}"

→ 41.1% O mismatches served by frost:sdpa_fwd_prefill_sm100_d128. (The always-on stride fuzzing from #516 catches this — the ragged sweeps are partially red on develop depending on the seed's gap draws and buffer binding style.)

A storage-derived exact-addressable capacity ((storage_elems_from_offset - row_footprint) // ts + 1) fixes every view case (verified: repro 41% → 0, kv-interleave probes bit-exact) — but breaks base-buffer bindings, because of bug 2.

Bug 2 — the kernel's unit decode is extent-sensitive; "over-claim is safe" does not hold

Forcing t_q for a failing sweep config (seq_len_q=[17,174,665,911], real total 1767, harness buffer capacity 1792, CGA_TILE_M=256):

forced t_q result
1767 (exact) pass
1792 (= 7×256 boundary) pass
1793 fail, ~50% O mismatches
1794 fail
1856 (64-aligned!) fail

Empirical law: corruption whenever ceil(t_q / CGA_TILE_M) > ceil(real_total / CGA_TILE_M) — the extent feeds the unit/tile decode (or something equivalent), so an over-claimed extent that crosses a tile boundary shifts real tiles onto wrong (batch, row) coordinates. The in-tree suites stay green only because the harness allocates packed_token_capacity (64-rounded) buffers, and a 64-rounding can never cross a 256 boundary.

Why no host-side formula can fix this

The adapter must choose t_q ∈ [real_total, real_total rounded up to the SAME tile count] — but the real total is device-only by #552's own design. numel//ts breaks views; exact-addressable capacity breaks any binding with slack past a tile boundary. The fix has to be kernel-side: make the unit decode (and anything else consuming the token extent) read the device metadata (cu[n_batch]) instead of the descriptor extent, so the extent can be a plain safe upper bound. Alternatively the envelope/decode contract needs to tolerate extra dead tile rows.

Affected

Happy to pair on this — I have the probes, the forced-t_q knob diff, and the storage-capacity patch staged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions