This document is relevant for: Trn2, Trn3
Sparse Attention Indexer MX BF16Score Kernel API Reference#
DeepSeek Sparse Attention Indexer — MX projections + BF16 score.
Supported/optimal usage: head_dim == 128; dim and q_lora_rank multiples of 512; batch_size == 1 and S a multiple of P_MAX (=128) on the validated v3_long/h64 configs; LNC sharding degree up to 2.
Background#
The sparse_attention_indexer_mx_bf16score kernel implements the DeepSeek Sparse Attention Indexer with MX projections and a BF16 score path.
API Reference#
Source code for this kernel API can be found at: sparse_attention_indexer_mx_bf16score.py
sparse_attention_indexer_mx_bf16score#
- nkilib.experimental.sparse_attention_indexer.sparse_attention_indexer_mx_bf16score(x: nl.NkiTensor, wq_b: nl.NkiTensor, wk: nl.NkiTensor, k_norm_gamma: nl.NkiTensor, k_norm_beta: nl.NkiTensor, weights_proj: nl.NkiTensor, cos: nl.NkiTensor, sin: nl.NkiTensor, k_cache: nl.NkiTensor, mask: nl.NkiTensor, n_heads: int, head_dim: int, rope_head_dim: int, index_topk: int, start_pos: int, use_hadamard: bool = False, batch_size: int = 1, wq_b_scale: Optional[nl.NkiTensor] = None, wk_scale: Optional[nl.NkiTensor] = None, k_scale_cache: Optional[nl.NkiTensor] = None, x_non_mx: Optional[nl.NkiTensor] = None, x_mx_data: Optional[nl.NkiTensor] = None, x_mx_scale: Optional[nl.NkiTensor] = None, qr_qtz_hbm: Optional[nl.NkiTensor] = None, qr_scale_hbm: Optional[nl.NkiTensor] = None, phase: str = 'all', k_seq_out_hbm: Optional[nl.NkiTensor] = None, end_pos_arg: Optional[int] = None, emit_flat_topk: bool = False, emit_tiled_topk: bool = False, compact_scales: bool = False, precision: MlaPrecision = MlaPrecision.MX, rope_layout: RopeLayout = RopeLayout.HALF_SPLIT, qr_bf16_hbm: Optional[nl.NkiTensor] = None) tuple[nl.NkiTensor, nl.NkiTensor]#
DeepSeek Sparse Attention Indexer — MX projections + BF16 score.
- Parameters:
x (
nl.NkiTensor) – [B*S, dim] hidden states.wq_b (
nl.NkiTensor) – [q_lora_rank // 4, n_heads * head_dim] fp8_e4m3fn_x4.wk (
nl.NkiTensor) – [dim // 4, head_dim] fp8_e4m3fn_x4 (contraction dim on partition).weights_proj (
nl.NkiTensor) – [n_heads, dim] per-head weights projection (always bf16). cos, sin: [S, rope_head_dim // 2] RoPE caches.k_cache (
nl.NkiTensor) – [B, head_dim, max_seq_len] bf16 (mutable).mask (
nl.NkiTensor) – [B*S, end_pos] attention mask.index_topk (
int) – number of top positions to select per query (topk k).wq_b_scale (
Optional[nl.NkiTensor]) – uint8 e8m0 weight scale; layout selected by compact_scales – [q_lora_rank // 32, n_heads * head_dim] native block-32, or [q_lora_rank // 128, ceil(n_heads * head_dim / 128)] compact block-128.wk_scale (
Optional[nl.NkiTensor]) – uint8 e8m0 weight scale; layout selected by compact_scales – [dim // 32, head_dim] native block-32, or [dim // 128, ceil(head_dim / 128)] compact block-128. k_norm_gamma, k_norm_beta: [head_dim] LayerNorm gamma/beta.k_scale_cache (
Optional[nl.NkiTensor]) – ignored (no MX scales in bf16 path).x_non_mx (
Optional[nl.NkiTensor]) – pre-cast bf16 view of x for W-projection (skips the f32->bf16 cast). x_mx_data, x_mx_scale: pre-quantized x for K-projection (skips the in-kernel swizzle + quantize_mx). qr_qtz_hbm, qr_scale_hbm: pre-quantized qr latent from an upstream QKV kernel (indexer skips its own qr transpose+norm+quantize). This is the only Q input path — qr is never quantized in-kernel.qr_qtz_hbm (
Optional[nl.NkiTensor]) – [num_s_tiles, P_MAX, q_lora_rank // (P_MAX*H_PACK), P_MAX] uint32 — pre-quantized (fp8x4-as-uint32) compressed query from the upstream QKV kernel.qr_scale_hbm (
Optional[nl.NkiTensor]) – matching uint8 block-32 e8m0 scales for qr_qtz_hbm.phase (
str) – context-parallel phased split. “all” (default): fused self-attention (queries == keys == S). “kproj”: project K for this rank’s S shard only; write seq-major [S, head_dim] to k_seq_out_hbm, return (that, None), no score. “score”: skip K-proj; score this rank’s S queries vs the pre-gathered full k_cache [B, head_dim, end_pos_arg=S_kv] over [0, S_kv).k_seq_out_hbm (
Optional[nl.NkiTensor]) – [S, head_dim] bf16 K output for phase=”kproj”.end_pos_arg (
Optional[int]) – key range for phase=”score” (defaults to start_pos + S).emit_flat_topk (
bool) – decode the snake topk to per-query positions in-kernel and return flat [B*S, index_topk] int32 (the sparse_mla_latent_attn topk_indices contract). Fuses the out-of-kernel snake->flat decode.emit_tiled_topk (
bool) – split-fix output — skip the per-query scatter and return positions in the natural partition-tiled layout [num_s_tiles, NUM_TOPK_BATCHES, P_MAX, index_topk // 16] int32, read per-query by the paired attention kernel. Mutually exclusive with emit_flat_topk; both default False (snake output, decoded by the consumer, e.g. the fused mega kernel).compact_scales (
bool) – select the wq_b_scale / wk_scale layout. False (default) is the NATIVE block-32 MX layout (one e8m0 byte per 32-K x 1-N group), already at hardware granularity. True is the DeepSeekscale_fmt=ue8m0checkpoint layout – ONE byte per 128-K x 128-N weight block – broadcast to the native granularity in-kernel, so callers can feed checkpoint scales directly instead of pre-expanding them (128x smaller scale tensors in HBM). Applies to the WEIGHT scales only; qr_scale_hbm / x_mx_scale stay native block-32 since they come from an upstream quantizer, not a checkpoint.precision (
MlaPrecision) – arithmetic format of the Q and K PROJECTIONS.MX(default) orBF16. The score matmul and the W projection are bf16 in both — the W projection was always bf16 despitew_projection_mx_batch’s name, and the score path already dropped MX (see Notes). BF16 additionally drops thedim/q_lora_rankmultiple-of-512 requirement down to 128. Required for GLM-MoE-DSA, whose checkpoints are bf16; passqr_bf16_hbminstead ofqr_qtz_hbm/qr_scale_hbm.rope_layout (
RopeLayout) – element pairing for the Q and K RoPE.HALF_SPLIT(default) is what DeepSeek-V3.2’s indexer uses;INTERLEAVEDis what GLM-MoE-DSA’s indexer uses. This is the ONE place the two models’ RoPE genuinely differs — their main-attention RoPE is identical (both interleaved). Applied to Q and K together; they are contracted against each other, so a mismatch silently corrupts the scores rather than failing.qr_bf16_hbm (
Optional[nl.NkiTensor]) – [B*S, q_lora_rank] bf16 q-normed compressed query — the BF16 Q-path input, replacing the MXqr_qtz_hbm/qr_scale_hbmpair. This is what the bf16 MLA QKV kernel exports (it has no MX-quantized qr to hand over). Already gamma-normed.
- Returns:
[B*S, end_pos] f32 — pre-topk per-position scores.
- Returns:
[num_S_tiles_total, NUM_TOPK_BATCHES, P_MAX, index_topk] uint32 — hardware-topk output.
topk_idx[s_tile, t, 16g + j%16, j//16]holds the position (0-based in[0, end_pos)) of query8t + g’s j-th largest score (ascending).num_S_tiles_total = batch_size * ceil(S / P_MAX).- Return type:
tuple[nl.NkiTensor, nl.NkiTensor]
Notes:
Q/K/W projections still use
nc_matmul_mxfor speed, but the score matmul uses bf16 nc_matmul (no Q/K quantization, no Hadamard rotation). This removes the Q swizzle + quantize_mx + Hadamard chain from the per-S-tile critical path.K cache stores bf16 instead of fp8x4 (no need for MX format if downstream consumers use this same kernel; otherwise downstream must coordinate).
Algorithm equivalent to the MX variant via Hadamard’s orthogonality:
q_H @ (k_H).T = q @ H @ H.T @ k.T = q @ k.Tso dropping Hadamard is mathematically a no-op. The MX quantization that Hadamard exists to support is also dropped on the score path. Score precision improves (bf16 score matmul vs MX-rounded score matmul).
Dimensions:
B: batch size (= batch_size).
S: query sequence length per batch.
M: B * S (total query rows, = x.shape[0]).
dim: hidden size (= x.shape[1]).
q_lora_rank: compressed-query rank (derived from qr_qtz_hbm layout).
n_heads: number of indexer heads.
head_dim: per-head dimension (== 128).
rope_head_dim: RoPE-rotated slice of head_dim.
end_pos: start_pos + S (KV positions scored per query).
P_MAX: partition tile size (= 128).
This document is relevant for: Trn2, Trn3