Commit 55c27ec
[Feature] Migrate KDA Triton Op to Ascend NPU (vllm-project#9035)
### What this PR does / why we need it?
This PR migrates the KDA (Kimi Delta Attention) Triton operators from
vLLM's upstream FLA (flash-linear-attention) GPU implementation to
Ascend NPU. Previously, KDA had no NPU-compatible Triton implementation.
### Does this PR introduce _any_ user-facing change?
No new user-facing configuration or public API is introduced. This PR
only adds the underlying Ascend NPU Triton kernels for KDA, without
changing existing behavior.
### Implementation overview
#### File responsibilities
| File | Responsibility | Main caller |
| --- | --- | --- |
| `kda.py` | Public operator entry points and orchestration for
`chunk_kda`, `fused_recurrent_kda`, `fused_kda_gate`, and gated RMSNorm;
also contains the chunk KKT, `W/U`, and output kernels. | Model
integration / tests |
| `fused_recurrent_kda.py` | Single-kernel token-by-token KDA recurrence
used by decode, including optional Q/K L2 normalization and paged
in-place state updates. | `fused_recurrent_kda_fwd` in `kda.py` |
| `cumsum.py` | Computes the per-chunk cumulative vector gate `G`. |
`chunk_kda_fwd` in `kda.py` |
| `solve_tril.py` | Computes the blockwise inverse `M = (I + L)^{-1}`
for 16/32/64-token lower-triangular blocks. The current KDA path uses a
chunk size of 64. | `chunk_kda_fwd` in `kda.py` |
| `chunk_delta_h.py` | Propagates the recurrent state across chunks and
computes the corrected value block `V'`. | `chunk_kda_fwd` in `kda.py` |
| `l2norm.py` | Optional standalone Q/K L2 normalization for the chunk
path. | `chunk_kda` in `kda.py` |
| `utils.py` | Cached variable-length metadata (`chunk_indices` and
`chunk_offsets`) plus contiguous-input/device guards. | `kda.py`,
`cumsum.py`, `solve_tril.py`, `chunk_delta_h.py` |
#### Kernel call graph
##### Prefill / chunked path
<img width="959" height="955" alt="img"
src="https://github.com/user-attachments/assets/69718f6e-20d9-4363-ba56-3183ffd1c7e2"
/>
##### Decode / recurrent path
<img width="955" height="955" alt="img_1"
src="https://github.com/user-attachments/assets/360ac9b6-963c-4eec-b996-693040ff089a"
/>
`fused_kda_gate` and `rms_norm_gated` are independent helper entry
points in `kda.py`; they are not called from the two attention paths
above. `fused_kda_gate` launches `kda_gate_fwd_kernel`, while
`rms_norm_gated` selects one of the two `layer_norm_gated_fwd_kernel`
variants according to the feature dimension.
#### Recurrent KDA definition
For readability, let the mathematical state be $S_t \in \mathbb{R}^{K
\times V}$. The Triton kernels physically store its transpose, $S_t^\top
\in \mathbb{R}^{V \times K}$, to make the value dimension contiguous.
With $s = K^{-1/2}$ and optional
$$
q_t \leftarrow \frac{q_t}{\sqrt{\lVert q_t \rVert_2^2 + \epsilon}},
\qquad
k_t \leftarrow \frac{k_t}{\sqrt{\lVert k_t \rVert_2^2 + \epsilon}},
$$
the decode kernel implements the following recurrence in one launch:
$$
\widetilde{S}_t = \mathrm{Diag}(\exp(g_t)) S_{t-1},
$$
$$
\delta_t = \beta_t \odot \left(v_t - \widetilde{S}_t^\top k_t\right),
$$
$$
S_t = \widetilde{S}_t + k_t \delta_t^\top,
\qquad
o_t = s S_t^\top q_t.
$$
Here, $g_t \in \mathbb{R}^{K}$ is a key-dimension decay gate. The
recurrent kernel accepts either a scalar $\beta_t$ or a value-dimension
vector $\beta_t \in \mathbb{R}^{V}$. The `ssm_state_indices` path maps
each sequence/token to its paged state slot and writes the updated state
in place.
The standalone gate helper produces the negative decay gate used above:
$$
g = -\exp(A)\,\mathrm{softplus}_{\beta}(g_{\mathrm{raw}} + b),
$$
where the bias is optional and the softplus kernel switches to its
linear approximation above `threshold`.
#### Chunked formulation
The prefill path computes the same recurrence in 64-token chunks. For
local token indices $0 \le j \le i < C$, define the cumulative vector
gate
$$
G_i = \sum_{r=0}^{i} g_r,
\qquad
E_{ij} = \exp(G_i - G_j).
$$
The implementation converts $G$ to base-2 exponent space,
$$
\widehat{G} = G\log_2(e),
\qquad
\mathrm{exp2}(\widehat{G}_i-\widehat{G}_j)=\exp(G_i-G_j),
$$
so the Triton kernels can use `exp2` without changing the mathematical
decay.
The two KKT kernels construct a strictly lower-triangular correction
matrix $L$ and a causal query-key matrix $P$ (`Aqk` in code):
$$
L_{ij} =
\begin{cases}
\beta_i\left\langle k_i,\;k_j\odot E_{ij}\right\rangle, & j < i,\\
0, & j \ge i,
\end{cases}
$$
$$
P_{ij} =
\begin{cases}
s\left\langle q_i,\;k_j\odot E_{ij}\right\rangle, & j \le i,\\
0, & j > i.
\end{cases}
$$
`solve_tril_kda` then computes
$$
M=(I+L)^{-1}.
$$
For a chunk matrix $K_c \in \mathbb{R}^{C\times K}$, values $V_c \in
\mathbb{R}^{C\times V}$, scalar-per-token $\beta$, and the state $S_c$
at the start of the chunk, `recompute_w_u_fwd_kernel` and the
chunk-state kernel compute
$$
U = M\,\mathrm{Diag}(\beta)V_c,
$$
$$
W = M\left[\mathrm{Diag}(\beta)\left(K_c\odot\exp(G)\right)\right],
$$
$$
K_c^{\star}=K_c\odot\exp(G_{C-1}-G),
\qquad
V' = U-WS_c,
$$
$$
S_{c+1}=\mathrm{Diag}(\exp(G_{C-1}))S_c+\left(K_c^{\star}\right)^\top
V'.
$$
Finally, `chunk_gla_fwd_kernel_o` combines the contribution from the
state before the chunk with the causal contribution produced inside the
chunk:
$$
O_c=s\left(Q_c\odot\exp(G)\right)S_c+PV'.
$$
This decomposition keeps the sequential recurrence only at chunk
boundaries; all token interactions within a chunk are expressed as tiled
matrix operations suitable for Ascend NPU execution. `cu_seqlens`,
`chunk_indices`, and `chunk_offsets` preserve the same flow for
flattened variable-length batches without allowing a chunk to cross a
sequence boundary.
#### Precision and layout notes
- The default chunk size is 64 tokens. Pairwise matrices are built from
16-token sub-blocks, and the triangular inverse is assembled from 16x16
blocks into the final 64x64 inverse.
- Cumulative gates, $L$, and `Aqk` are produced in fp32. The solved
inverse is converted to the input dtype for the following tiled matrix
multiplications.
- Chunk-boundary state is accumulated and returned in fp32. In
particular, the precision-sensitive $WS_c$ residual uses fp32 operands
and `input_precision="ieee"` before subtracting it from $U$.
- The physical state layout is `[V, K]`, while the equations use `[K,
V]`. This avoids an extra transpose in the value-tiled state update and
output kernels.
### How was this patch tested?
Added e2e nightly precision tests under
`tests/e2e/nightly/single_node/ops/singlecard_ops/triton/`. Each test
compares the Triton kernel against a naive float32 recurrent KDA
reference ported from FLA's `naive.py`, with an RMSE-ratio tolerance of
`0.005` for both output `o` and final state `ht`.
- **`test_chunk_kda_npu.py`** – Tests `chunk_kda` prefill/chunked
execution across variable-length layouts using `cu_seqlens`, including:
- Single- and multi-sequence inputs
- Unaligned sequence lengths
- Long sequences up to 8192 tokens
- `H ∈ {32, 64}` and `D = 128`
- fp16 and bf16
- **`test_fused_recurrent_kda_npu.py`** – Tests `fused_recurrent_kda`
decode execution, including:
- Variable-length recurrent mode
- Single-token decode and short multi-token sequences
- fp16 and bf16
- In-place state updates with `ssm_state_indices`
- Paged-state usage with `N ∈ {1, 4, 16}`
Run with:
```bash
pytest -sv \
tests/e2e/nightly/single_node/ops/singlecard_ops/triton/test_chunk_kda_npu.py
pytest -sv \
tests/e2e/nightly/single_node/ops/singlecard_ops/triton/test_fused_recurrent_kda_npu.py
```
- vLLM version: v0.23.0
- vLLM main:
vllm-project/vllm@967c5c3
---------
Signed-off-by: fangyongfu <fangyongfu321@163.com>
Signed-off-by: Chenxi Qian <chenxi.qian.cq@outlook.com>
Signed-off-by: yongfuFang <fangyongfu321@163.com>
Co-authored-by: Chenxi Qian <chenxi.qian.cq@outlook.com>1 parent 0233ddf commit 55c27ec
10 files changed
Lines changed: 3221 additions & 0 deletions
File tree
- tests/e2e/nightly/single_node/ops/singlecard_ops/triton
- vllm_ascend/ops/triton/kda
Lines changed: 162 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
0 commit comments