This document is relevant for: Trn2, Trn3
Attention Block TKG Kernel API Reference#
[Experimental] Implements a fully fused attention block optimized for Token Generation (autoregressive decoding), keeping all intermediate tensors in SBUF to minimize HBM traffic.
The kernel supports:
Fused multi-stage computation: pre-normalization, QKV projection, RoPE, post-normalization, attention, KV cache update, and output projection
Multiple KV cache layouts: flat (transposed/non-transposed) and block-based
Grouped-Query Attention (GQA) with configurable Q/KV head ratios
Optional RMSNorm at multiple stages (pre-projection, post-projection per-head)
Optional Rotary Position Embedding (RoPE) with configurable layouts
Flexible quantization support (FP8, FP16, BF16)
FP8 KV cache quantization support
Configurable softmax scaling factor
Batch processing with per-batch cache indexing
Single program multiple data (SPMD) sharding for distributed computation
Background#
The attention_block_tkg kernel combines multiple stages of transformer attention computation into a single fused operation that minimizes data movement between HBM and on-chip memory (SBUF).
Fused Operations:
The kernel fuses the following stages in SBUF to avoid HBM round-trips:
Pre-normalization: Optional RMSNorm on input hidden states
QKV Projection: Linear projection to Query, Key, Value tensors
RoPE: Optional Rotary Position Embedding on Q and K
Post-normalization: Optional per-head RMSNorm on Q and K
Attention Computation: Scaled dot-product attention with KV cache
KV Cache Update: Write new K/V tokens to cache
Output Projection: Linear projection of attention output
Performance Benefits:
By keeping intermediate tensors in SBUF throughout the computation, this kernel achieves:
Reduced HBM bandwidth consumption
Lower latency for token generation
Better hardware utilization through operation fusion
API Reference#
Source code for this kernel API can be found at: attention_block_tkg.py
attention_block_tkg#
- nkilib.core.attention_block_tkg.attention_block_tkg.attention_block_tkg(X: nl.NkiTensor, X_hidden_dim_actual: Optional[int], rmsnorm_X_enabled: bool, rmsnorm_X_eps: Optional[float], rmsnorm_X_gamma: Optional[nl.NkiTensor], W_qkv: nl.NkiTensor, bias_qkv: Optional[nl.NkiTensor], quantization_type_qkv: QuantizationType, weight_dequant_scale_qkv: Optional[nl.NkiTensor], input_dequant_scale_qkv: Optional[nl.NkiTensor], rmsnorm_QK_pre_rope_enabled: bool, rmsnorm_QK_pre_rope_eps: float, rmsnorm_QK_pre_rope_W_Q: Optional[nl.NkiTensor], rmsnorm_QK_pre_rope_W_K: Optional[nl.NkiTensor], cos: Optional[nl.NkiTensor], sin: Optional[nl.NkiTensor], rope_contiguous_layout: bool, rmsnorm_QK_post_rope_enabled: bool, rmsnorm_QK_post_rope_eps: float, rmsnorm_QK_post_rope_W_Q: Optional[nl.NkiTensor], rmsnorm_QK_post_rope_W_K: Optional[nl.NkiTensor], K_cache_transposed: bool, active_blocks_table: Optional[nl.NkiTensor], K_cache: nl.NkiTensor, V_cache: nl.NkiTensor, attention_mask: Optional[nl.NkiTensor], sink: Optional[nl.NkiTensor], update_cache: bool, kv_cache_update_idx: Optional[nl.NkiTensor], W_out: Optional[nl.NkiTensor], bias_out: Optional[nl.NkiTensor], quantization_type_out: QuantizationType, weight_dequant_scale_out: Optional[nl.NkiTensor], input_dequant_scale_out: Optional[nl.NkiTensor], transposed_out: bool, out_in_sb: bool, transposed_in: bool = False, softmax_scale: Optional[float] = None, enable_fa_s_prior_tiling: bool = True, fp8_packed: bool = False, k_scale: Optional[nl.NkiTensor] = None, v_scale: Optional[nl.NkiTensor] = None, sbm: Optional[SbufManager] = None, skip_attention: bool = False, is_h_transposed_by_4: bool = False, KVDP: int = 1, KVDP_replica_group: Optional[ReplicaGroup] = None, KVDP_collective_mode: Optional[KVDPCollectiveMode] = None, KVDP_rank: Optional[nl.NkiTensor] = None, pos_ids: Optional[nl.NkiTensor] = None, swa_start_pos_ids: Optional[nl.NkiTensor] = None, S_ctx: Optional[int] = None, max_context_len: Optional[nl.NkiTensor] = None, dtype_mode: DtypeMode = DtypeMode.NON_OCP, CP: int = 1, CP_replica_group: Optional[ReplicaGroup] = None, CP_collective_mode: Optional[CPCollectiveMode] = None, seq_packed_attention_mask: Optional[nl.NkiTensor] = None, seq_packed_active_blocks_table: Optional[nl.NkiTensor] = None, seq_packed_q_index_table: Optional[nl.NkiTensor] = None, seq_packed_seq_id_table: Optional[nl.NkiTensor] = None, seq_packed_accumulator_partial_route_table: Optional[nl.NkiTensor] = None, seq_packed_accumulator_group_state_route_table: Optional[nl.NkiTensor] = None)#
Fused Attention Block for Token Generation (TKG).
Performs end-to-end attention block computation optimized for autoregressive decoding: X → [RMSNorm] → QKV Projection → [RMSNorm Q/K] → [RoPE] → [RMSNorm Q/K] → Attention → KV Cache Update → [Output Projection] → Output
All intermediate tensors remain in SBUF to minimize HBM traffic.
- Parameters:
X (
nl.NkiTensor) – Input hidden statesX_hidden_dim_actual (
Optional[int]) – Actual hidden dim if X is paddedrmsnorm_X_enabled (
bool) – Apply RMSNorm to X before QKV projectionrmsnorm_X_eps (
Optional[float]) – RMSNorm epsilon (default 1e-3)rmsnorm_X_gamma (
Optional[nl.NkiTensor]) – [1, H] @ HBM, RMSNorm weightsW_qkv (
nl.NkiTensor) – [H, d_head*(q_heads + 2*kv_heads)] @ HBM, QKV projection weights. Packs Q (q_heads heads), K (kv_heads heads), V (kv_heads heads) per token.bias_qkv (
Optional[nl.NkiTensor]) – [1, d_head*(q_heads + 2*kv_heads)] @ HBM, QKV bias.quantization_type_qkv (
QuantizationType) – Type of quantization for QKV projection (NONE, STATIC, ROW).weight_dequant_scale_qkv (
Optional[nl.NkiTensor]) – Weight dequantization scale for QKV projection.input_dequant_scale_qkv (
Optional[nl.NkiTensor]) – Input dequantization scale for QKV projection.rmsnorm_QK_pre_rope_enabled (
bool) – Apply RMSNorm to Q/K before RoPErmsnorm_QK_pre_rope_eps (
float) – Pre-RoPE RMSNorm epsilonrmsnorm_QK_pre_rope_W_Q (
Optional[nl.NkiTensor]) – [1, d_head] @ HBM, Pre-RoPE Q gamma weightsrmsnorm_QK_pre_rope_W_K (
Optional[nl.NkiTensor]) – [1, d_head] @ HBM, Pre-RoPE K gamma weightscos (
Optional[nl.NkiTensor]) – [d_head//2, B, S_tkg] @ HBM, RoPE cosine embeddings (None = skip RoPE)sin (
Optional[nl.NkiTensor]) – [d_head//2, B, S_tkg] @ HBM, RoPE sine embeddings (None = skip RoPE)rope_contiguous_layout (
bool) – True for contiguous halves, False for interleavedrmsnorm_QK_post_rope_enabled (
bool) – Apply RMSNorm to Q/K after RoPErmsnorm_QK_post_rope_eps (
float) – Post-RoPE RMSNorm epsilonrmsnorm_QK_post_rope_W_Q (
Optional[nl.NkiTensor]) – [1, d_head] @ HBM, Post-RoPE Q weightsrmsnorm_QK_post_rope_W_K (
Optional[nl.NkiTensor]) – [1, d_head] @ HBM, Post-RoPE K weightsK_cache_transposed (
bool) – Whether K cache is stored transposed in HBM. If True: K cache is [B, d_head, S_max_ctx]. If False: K cache is [B, S_max_ctx, d_head]. Must be False for block KV cache.active_blocks_table (
Optional[nl.NkiTensor]) – @ HBM, Block indices for block KV cache. kv_heads == 1: [B, num_blocks]. kv_heads > 1: [B, kv_heads, num_blocks] (per-head block tables; each (batch, head) pair owns its own slot range so writes never collide). Block indices must usenum_blocks * kv_headsaddressing.K_cache (
nl.NkiTensor) – Key cache @ HBM. Flat KV: [B, kv_heads, d_head, S_max_ctx] if K_cache_transposed else [B, kv_heads, S_max_ctx, d_head]. Block KV: kv_heads == 1: [num_blocks, block_len, d_head] or [num_blocks, 1, block_len, d_head]. kv_heads > 1: [num_blocks, kv_heads, block_len, d_head]. Block KV with fp8_packed: kv_heads == 1: [num_blocks, block_len // 2, d_head, 2] or [num_blocks, 1, block_len // 2, d_head, 2]. kv_heads > 1: [num_blocks, kv_heads, block_len // 2, d_head, 2].V_cache (
nl.NkiTensor) – Value cache @ HBM. Flat KV: [B, kv_heads, S_max_ctx, d_head]. Block KV: kv_heads == 1: [num_blocks, block_len, d_head] or [num_blocks, 1, block_len, d_head]. kv_heads > 1: [num_blocks, kv_heads, block_len, d_head].attention_mask (
Optional[nl.NkiTensor]) – Attention mask @ HBM. Required for ordinary attention. Sequence-packed attention usesseq_packed_attention_maskinstead, so this may be None. When pos_ids is provided: [S_tkg, B, q_heads_attn, S_tkg], active-only portion of the mask (the prior mask is generated in-kernel; layout is the same for swap and default). When pos_ids is None (full pre-generated mask): the layout MUST match the QK-swap decision. Determine it with is_qk_swapped(…) first (see attention_tkg / attention_tkg_utils for the full mask-shape guidance), then supply the matching layout: - default (K-stationary): [S_ctx, B, q_heads_attn, S_tkg] - QK-swap (Q-stationary): [B, q_heads_attn, S_tkg, S_ctx] gen_mask_tkg_hbm produces the correct layout when given the same transposed_out flag.sink (
Optional[nl.NkiTensor]) – [q_heads_attn, 1] @ HBM, one value per attention (Q) head.q_heads_attn = q_heads * KVDP(=kv_heads * q_per_group).update_cache (
bool) – Update KV cache with new tokenskv_cache_update_idx (
Optional[nl.NkiTensor]) – Cache write positions (uint32_max = skip). Flat KV: [B, 1] — the shared start position; consecutive tokens are assumed. Block KV: per-token physical slots. kv_heads == 1: [B, S_tkg]. kv_heads > 1: [B, kv_heads, S_tkg].W_out (
Optional[nl.NkiTensor]) – [q_heads*d_head, H] @ HBM, Output projection weights. When using FP8 KV cache (k_scale/v_scale provided), the attention output has magnitude scaled by v_scale. The caller must absorb v_scale into W_out or its dequant scale to compensate:W_out = W_out_original / v_scale(for NONE quantization) orweight_dequant_scale_out = scale / v_scale(for ROW/STATIC).bias_out (
Optional[nl.NkiTensor]) – [1, H] @ HBM, Output projection biasquantization_type_out (
QuantizationType) – Type of quantization for output projection (NONE, STATIC, ROW).weight_dequant_scale_out (
Optional[nl.NkiTensor]) – Weight dequantization scale for output projection.input_dequant_scale_out (
Optional[nl.NkiTensor]) – Input dequantization scale for output projection.transposed_out (
bool) – Transpose output layout (requires W_out)out_in_sb (
bool) – Return output in SBUF instead of HBMtransposed_in (
bool) – When True, X is in transposed HBM layout [H0=pmax, n_prgs, H1_shard, BxS] from a preceding layer’s transposed_out. (Note that using [H0, n_prgs, BxS, H1_shard] instead would require an extra tensor_copy upstream so all-in-all it doesn’t save us from doing the equivalent in attention.) Requires B*S_tkg <= pmax (small batch path). Default: False.softmax_scale (
Optional[float]) – Scaling factor for attention scores. If None, defaults to (1/√D) / k_scale. When using FP8 KV cache (k_scale/v_scale provided) and softmax_scale is None, the kernel automatically divides by k_scale to dequantize the KV cache values in the QK matmul, effectively settingsoftmax_scale = (1/√D) / k_scale. When softmax_scale is explicitly provided, the caller is responsible for incorporating k_scale (e.g.,softmax_scale = scaling / k_scale).enable_fa_s_prior_tiling (
bool) – bool: Whether to enable flash attention (FA) for attention computation. When enabled, the attention computation is tiled along the context (s_prior) to reduce peak memory usage.fp8_packed (
bool) – When True with block KV, K_cache is fp8 [num_blocks, block_len // 2, d_head, 2] where the last dimension packs two consecutive sequence positions. Enables DMA transpose for FP8 block KV, avoiding the slower PE transpose fallback.k_scale (
Optional[nl.NkiTensor]) – Scale for K quantization to FP8. Shape (PMAX, 1) or (1, 1) @ HBM. Must contain a single scalar value (replicated or scalar). When provided with v_scale, enables FP8 KV cache quantization. Supported dtypes: float32, float16, bfloat16.v_scale (
Optional[nl.NkiTensor]) – Scale for V quantization to FP8. Shape (PMAX, 1) or (1, 1) @ HBM. Must contain a single scalar value (replicated or scalar). When provided with k_scale, enables FP8 KV cache quantization. Supported dtypes: float32, float16, bfloat16.sbm (
Optional[SbufManager]) – SBUF memory manager (otherwise auto-allocated)skip_attention (
bool) – Skip attention computation (for testing)is_h_transposed_by_4 (
bool) – Whether input X and RMSNorm gamma have been pre-shuffled along the H dimension for MXFP quantization. Required to be True when quantization_type_qkv is MX, STATIC_MX, or ROW_MX. The pre-shuffle reorders [B, S, H//512, 128, 4] → [B, S, 4, H//512, 128] so the data matches the layout expected by the hardware quantize_mx instruction. Default: False.KVDP (
int) – KV cache data parallelism degree - number of ranks that shard the KV cache across the batch dimension (1 = disabled). Each rank processes B/KVDP batches.KVDP_replica_group (
Optional[ReplicaGroup]) – Replica group for collective opsKVDP_rank (
Optional[nl.NkiTensor]) – [1] @ HBM, uint32. This rank’s position within its KVDP replica group (0 to KVDP-1). Used for batch/head slicing. Required when KVDP > 1.pos_ids (
Optional[nl.NkiTensor]) – [B, S_tkg] @ HBM, Absolute sequence position of each active token. When provided, the kernel generates the prior causal mask on-chip (attending to KV positions < pos_id) instead of loading a full pre-generated mask from HBM. In this mode, attention_mask carries only the active portion.swa_start_pos_ids (
Optional[nl.NkiTensor]) – [B, S_tkg] @ HBM, Per-query SWA window start positions. When provided together with pos_ids, generates a banded sliding window attention mask where each token attends to positions in [start_pos, pos_id). When None, standard causal attention mask is generated (token attends to all positions < pos_id).S_ctx (
Optional[int]) – Explicit context length for flat KV with pos_ids. Required when pos_ids is provided and block KV is not used, since S_ctx cannot be derived from the attention mask. Must be None otherwise.max_context_len (
Optional[nl.NkiTensor]) – [1] @ HBM, int32 scalar for dynamic FA early exit. When provided, the FA loop exits early after processing ceil(max_context_len / tile_size) tiles. Requires block KV cache and pos_ids.dtype_mode (
DtypeMode) – Quantization dtype policy forwarded to all FP8-aware subkernels and to the FP8 KV cache whenK_cache/V_cacheare allocated with the opaque"float8e4"sentinel. Concrete dtypes are honored as-is. -DtypeMode.NON_OCP(default):nl.float8_e4m3(max=240). -DtypeMode.OCP:nl.float8_e4m3fn(max=448). TRN3 only. -DtypeMode.AUTO:nl.float8_e4m3fnon TRN3, elsenl.float8_e4m3.CP (
int) – Context parallelism degree - number of ranks that shard the KV cache along the sequence (context) dimension (1 = disabled). Each rank holds an S_ctx/CP slice of the prior KV cache and runs local flash attention; the distributed softmax results are combined across ranks by the CP collectives.CP_replica_group (
Optional[ReplicaGroup]) – Replica group for CP collective ops. Required when CP > 1.CP_collective_mode (
Optional[CPCollectiveMode]) – Collective mode for CP output redistribution (ALL_TO_ALL or REDUCE_SCATTER). Defaults to ALL_TO_ALL when None. KV Data Parallelism (KVDP > 1): KV-DP partitions the KV cache across ranks along the batch dimension. Each rank holds B/KVDP batches of the KV cache. Before attention: all_gather Q heads, slice Q/K/V batch. After attention: all_gather output batch, slice heads. When KV data parallelism is enabled, input/output shapes change: - B_attn = B / KVDP (batches per rank for attention) - q_heads_attn = q_heads * KVDP (query heads per rank after gather) Input shape changes: - K_cache, V_cache: [B_attn, …] instead of [B, …] - attention_mask: [S_ctx, B_attn, q_heads_attn, S_tkg] - pos_ids: [B_attn, S_tkg] instead of [B, S_tkg] - swa_start_pos_ids: [B_attn, S_tkg] instead of [B, S_tkg] - kv_cache_update_idx: [B_attn, 1] (caller must slice per rank) Output shape changes (when update_cache=False): - K_out: kv_heads == 1: [d_head, B_attn, S_tkg] kv_heads > 1: [d_head, B_attn, kv_heads, S_tkg] - V_out: [B_attn, kv_heads, S_tkg, d_head] Context Parallelism (CP > 1): CP partitions the prior KV cache across ranks along the sequence (context) dimension. Each rank holds an S_ctx/CP slice and runs local flash attention, returning unnormalized output plus per-rank softmax stats (running max/sum). Before attention: all_gather Q heads (q_heads -> q_heads*CP, no K/V slicing since each rank owns its context shard). Afterseq_packed_attention_mask (
Optional[nl.NkiTensor]) – Packed schedule-row mask. When provided, the active-block, Q-index, and sequence-ID tables are required and the kernel dispatches to attention_tkg_seq_packed. The two accumulator route tables are an optional all-or-none pair. This is the only attention mask consumed on the packed path. See attention_tkg_sequence_packing_design_spec.md in core/attention for the schedule contract.seq_packed_active_blocks_table (
Optional[nl.NkiTensor]) – Packed physical block routes.seq_packed_q_index_table (
Optional[nl.NkiTensor]) – Packed Q tensor-indirection routes.seq_packed_seq_id_table (
Optional[nl.NkiTensor]) – Packed absolute sequence IDs.seq_packed_accumulator_partial_route_table (
Optional[nl.NkiTensor]) – Tensor Indirection routes that scatter each row’s slot partials into sequence groups. Must be supplied with the group-state route.seq_packed_accumulator_group_state_route_table (
Optional[nl.NkiTensor]) – Tensor Indirection routes that gather and scatter each group’s persistent sequence state. Must be supplied with the partial route.
- Returns:
Output tensor with shape depending on projection and output location: - Without projection (W_out=None): - out_in_sb=False: [B, q_heads, d_head, S_tkg] @ HBM - out_in_sb=True: [d_head, B*q_heads*S_tkg] @ SBUF - With projection (W_out provided): - transposed_out=False, out_in_sb=False: [B*S_tkg, H] @ HBM - transposed_out=False, out_in_sb=True: [B*S_tkg, H//lnc] @ SBUF - transposed_out=True, out_in_sb=False: [128, lnc, H//lnc//128, B*S_tkg] @ HBM - transposed_out=True, out_in_sb=True: [128, H//lnc//128, B*S_tkg] @ SBUF
- Returns:
If update_cache=True: Updated K cache (shape matches K_cache input) - If update_cache=False: New K tokens @ HBM with shape: kv_heads == 1: [d_head, B_attn, S_tkg] kv_heads > 1: [d_head, B_attn, kv_heads, S_tkg]
- Returns:
If update_cache=True: Updated V cache (shape matches V_cache input) - If update_cache=False: New V tokens [B_attn, kv_heads, S_tkg, d_head] @ HBM
- Return type:
nl.NkiTensor
Dimensions:
B: batch size
S_tkg: number of new tokens to generate
S_ctx: KV cache sequence length in current bucket
S_max_ctx: maximum KV cache capacity of current bucket
H: hidden dimension
d_head: head dimension (must be even)
q_heads: number of query heads
kv_heads: 1 (GQA with single KV head)
Supported Data Types:
Supports nl.float16 and nl.bfloat16
Constraints:
Requires NeuronCore v3+
d_head must be even
H must be multiple of 128
Requires
batch * sequence_tkg * q_heads <= pmax (=128)
Implementation Details#
Computation Flow:
The kernel executes the following stages in sequence:
Input Pre-normalization (optional):
Apply RMSNorm to input hidden states:
X_norm = RMSNorm(X, rmsnorm_pre_W, rmsnorm_pre_eps)Computed in FP32, result cast back to input dtype
QKV Projection:
Compute
QKV = X_norm @ W_qkv.Tusing matrix multiplicationResult shape:
[B, S_tkg, (q_heads + 2) * d_head]Supports FP8 quantization with dequantization scales
Q/K Processing (per head group):
Extract Q heads:
Q = QKV[:, :, :q_heads * d_head]Extract K head:
K = QKV[:, :, q_heads * d_head : (q_heads + 1) * d_head]Apply RoPE if enabled:
Q, K = RoPE(Q, K, cos, sin, position_ids)Apply per-head RMSNorm if enabled:
Q = RMSNorm(Q, rmsnorm_post_W_Q),K = RMSNorm(K, rmsnorm_post_W_K)
V Processing:
Extract V head:
V = QKV[:, :, (q_heads + 1) * d_head :]
KV Cache Update:
Write new K/V tokens to cache at positions specified by
kv_cache_update_idxSupports multiple cache layouts (flat, transposed, block-based)
Uses indirect addressing for efficient batch processing
Attention Computation:
Compute scaled dot-product attention:
Attn = softmax(Q @ K_cache.T / scale) @ V_cacheApply causal masking based on
S_ctx(context lengths)Use FP32 accumulation if
mixed_precision=TrueSupports Grouped-Query Attention by replicating KV heads
Output Projection:
Reshape attention output:
Attn_flat = Attn.reshape([B, S_tkg, q_heads * d_head])Compute
out = Attn_flat @ W_o.TSupports FP8 quantization with dequantization scales
Memory Management:
The kernel uses a custom SBUF memory manager (SbufManager) to efficiently allocate and reuse on-chip memory:
Stack-based allocation for temporary tensors
Automatic memory reuse after tensor lifetime ends
Minimizes SBUF fragmentation
Parallelization:
The kernel supports data parallelism across multiple Neuron Cores:
Batch dimension (
B) can be sharded across coresEach core processes a subset of batch elements independently
KV cache updates use per-core indexing
Cache Layout Support:
Flat Cache (
is_block_kv=False):K cache:
[B, S_max_ctx, d_head]or[B, d_head, S_max_ctx](transposed)V cache:
[B, S_max_ctx, d_head]Direct indexing by batch and sequence position
Block Cache (
is_block_kv=True):K/V cache:
[num_blocks, block_len, d_head]Indirect indexing via block slot mapping
Efficient for variable-length sequences
Quantization Support:
FP8 weights: Provide
qkv_scaleando_scalefor dequantizationMixed precision: FP32 accumulation with FP16/BF16 inputs
Automatic dtype handling throughout the pipeline
Key Implementation Notes:
Grouped-Query Attention: The kernel processes Q heads in groups, where each group shares a single K/V head. This reduces KV cache memory by a factor of
q_heads / kv_heads.RoPE Application: Rotary embeddings are applied using position indices derived from
S_ctx(current context length). Supports both contiguous and interleaved layouts.Causal Masking: Attention scores are masked such that token at position
ican only attend to positions0toiin the context. Implemented by adding-infto masked positions before softmax.Cache Update Optimization:
For
S_tkg=1: Uses batched vector DMA withvector_offsetfor all batches in one operationFor
S_tkg>1: Uses per-batch scalar DMA withscalar_offsetBlock cache uses indirect addressing via block slot indices
Memory Efficiency: All intermediate tensors (QKV, Q, K, V, attention scores, attention output) remain in SBUF. Only input
X, weights, caches, and final outputoutreside in HBM.
This document is relevant for: Trn2, Trn3