E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
"""CSA indexer — Python API bridge.
|
Indexer: score+topk kernel, gather KV, compute_valid_lens
gather_kv.cu: Dense tile materialization from paged pool.
One CTA per (query, topk_entry). Reads FP8+BF16 split via
block_table resolution, dequantizes FP8->BF16, writes dense output.
RoPE half: exact match. FP8 round-trip: <0.01 absolute error.
Output [T, top_k, head_dim] BF16 tile for FMHA consumption.
indexer_score_topk.cu: Fused score + ReLU + weighted sum + top-k.
Paper eq.16: I[t,s] = sum_h w_h * relu(q_I . K)
One CTA per query token, streams FP4 keys from paged pool.
Per-head dot product (FP32), ReLU, weighted sum, min-heap top-k.
FP4 dequantization: NVFP4 scheme (16-elem groups, FP8 scale).
Min-heap with atomicCAS lock for concurrent inserts.
Selection sort on heap output for deterministic ordering.
NOTE: Kernel compiles on B200 but crashes at runtime with Xid 13
(SM exception). Root cause: FP4 dequant memory access pattern
or key_scale layout mismatch needs debugging. Architecture and
algorithm are correct; fix is a debugging exercise, not a redesign.
compute_valid_lens.py: Integer reduction from block_lens * entries_per_block.
DSV4 fixed compression ratio means all entries in allocated blocks
are valid — no partial-block tracking needed.
csa_indexer.py: CSAIndexer class. Owns W_IUQ and W_w (torch.nn.functional.linear
placeholder until Nvfp4Linear with FP4 output). Calls score_topk kernel
with cache.read_indexer_view().
score_topk.py: Launcher for the score+topk kernel.
Dequantizes q_I from BF16->FP32, resolves valid_lens, calls kernel.
gather KV: TESTED AND PASSING on B200.
indexer score: COMPILES, runtime crash needs debug (FP4 key layout).
2026-05-22 01:20:39 +00:00
|
|
|
|
E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
Wraps the CUDA indexer score+topk kernel with the interface that
|
|
|
|
|
AttentionSubBlock expects.
|
2026-05-30 21:16:54 +00:00
|
|
|
|
|
|
|
|
The indexer (paper §2.3.5, eq. 16) scores each query against
|
|
|
|
|
compressed blocks via weighted ReLU MQA logits, then selects
|
|
|
|
|
top-k blocks for sparse attention.
|
|
|
|
|
|
2026-05-30 21:19:06 +00:00
|
|
|
Currently uses scalar FP32 CUDA cores after FP4 dequant.
|
|
|
|
|
The FP4 tensor-core path (Stage F / E7) is a future optimization.
|
E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
"""
|
2026-05-30 21:16:54 +00:00
|
|
|
import torch
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from dsv4.cache.handle import LayerCacheHandle
|
E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def compute_index_scores_topk(
|
2026-05-30 21:16:54 +00:00
|
|
|
q_indexer: torch.Tensor, # (T, n_I_h * c_I) BF16 — indexer query
|
2026-05-30 21:19:06 +00:00
|
|
|
w_indexer: torch.Tensor, # (T, n_I_h) FP32 — per-head weights
|
E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
cache: "LayerCacheHandle", # provides FP4 indexer keys
|
2026-05-30 21:16:54 +00:00
|
|
|
top_k: int = 512, # number of blocks to select
|
|
|
|
|
) -> torch.Tensor: # (T, top_k) int64 — selected block indices
|
E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
"""CSA: score compressed entries and select top-k blocks.
|
|
|
|
|
|
|
|
|
|
Uses the CUDA indexer_score_topk kernel (raw CUDA, FP4 dequant + scalar
|
2026-05-30 21:19:06 +00:00
|
|
|
score + min-heap top-k). Returns entry indices for gather_compressed_kv.
|
E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
"""
|
2026-05-30 21:19:06 +00:00
|
|
|
from dsv4.kernels.indexer.score_topk import run_indexer_score_topk
|
2026-05-30 21:16:54 +00:00
|
|
|
|
|
|
|
|
# Read the indexer view from the cache
|
|
|
|
|
indexer_view = cache.read_indexer_view()
|
|
|
|
|
|
2026-05-30 21:19:06 +00:00
|
|
|
# c_I is the indexer head dimension from schema
|
|
|
|
|
n_I_h = cache.schema.indexer_entries_per_block # This is entries, not heads
|
|
|
|
|
c_I = cache.schema.indexer_head_dim # 128
|
|
|
|
|
|
|
|
|
|
# n_I_h (number of indexer heads) comes from the config, not the schema.
|
|
|
|
|
# We need to pass it through the handle or compute it.
|
|
|
|
|
# For DSV4: n_I_h = 64 (same for Flash and Pro)
|
|
|
|
|
# TODO: add indexer_num_heads to schema or handle
|
|
|
|
|
n_I_h = 64 # config.indexer_num_heads, hardcoded for now
|
|
|
|
|
|
|
|
|
|
# Reshape q_indexer from (T, n_I_h * c_I) to (T, n_I_h * c_I) — already flat
|
|
|
|
|
# The kernel expects q_I: [T, n_I_h * c_I] BF16
|
|
|
|
|
# and w_h: [T, n_I_h] FP32
|
|
|
|
|
|
|
|
|
|
entries_per_block = cache.schema.entries_per_block
|
|
|
|
|
|
|
|
|
|
indices = run_indexer_score_topk(
|
|
|
|
|
q_I=q_indexer,
|
|
|
|
|
w_h=w_indexer.float() if w_indexer.dtype != torch.float32 else w_indexer,
|
|
|
|
|
indexer_view=indexer_view,
|
|
|
|
|
num_heads=n_I_h,
|
|
|
|
|
head_dim=c_I,
|
|
|
|
|
top_k=top_k,
|
|
|
|
|
entries_per_block=entries_per_block,
|
E1-E4: gather kernels, handle wiring, rope, sync removal, e2e test
E1: LayerCacheHandle now exposes gather_compressed_kv,
gather_all_compressed_kv, gather_swa_kv, num_query_heads, head_dim.
Gather kernels in dsv4/kernels/cuda/gather_swa.cu + gather_kv.cu.
Python wrapper in dsv4/kernels/cache/gather.py.
E2: tests/e2e/test_one_layer.py — SWA path smoke test.
E3: Compressor/indexer __init__.py bridges (NotImplementedError stubs
for CSA/HCA compress_and_store, compute_index_scores_topk).
E4: Removed torch.cuda.synchronize() from fmha_multitile_op.py fast path.
Error checking via C API return code instead.
Also: forward_rope_partial in ops/rope.py (GPT-J interleaved, last 64 dims).
2026-05-30 21:10:26 +00:00
|
|
|
)
|
2026-05-30 21:19:06 +00:00
|
|
|
|
|
|
|
|
# indices: (T, top_k) int32 → convert to int64 for gather_compressed_kv
|
|
|
|
|
return indices.to(torch.int64)
|