@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 598190aac3
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 5, 17:15 UTC

[fix] Remove trtllm ragged mla prefills (#36540)

Signed-off-by: Olya Kozlova <okozlova@nvidia.com>

Olya Kozlova committed Mar 31, 2026 at 21:30 UTC 598190aac38a42d8c51ea46a3061e46d9078b3a5
8 files changed +185 -35
csrc/attention/merge_attn_states.cu
+48 -19
@@ -3,6 +3,7 @@
3 #include <ATen/cuda/CUDAContext.h>
4 #include <c10/cuda/CUDAGuard.h>
5 #include <algorithm>
6 +#include <limits>
7
8 #include "attention_dtypes.h"
9 #include "attention_utils.cuh"
@@ -17,7 +18,7 @@ __global__ void merge_attn_states_kernel(
18 const float* prefix_lse, const scalar_t* suffix_output,
19 const float* suffix_lse, const uint num_tokens, const uint num_heads,
20 const uint head_size, const uint prefix_head_stride,
20 - const uint output_head_stride) {
21 + const uint output_head_stride, const uint prefix_num_tokens) {
22 using pack_128b_t = uint4;
23 const uint pack_size = 16 / sizeof(scalar_t);
24 const uint threads_per_head = head_size / pack_size;
@@ -43,6 +44,22 @@ __global__ void merge_attn_states_kernel(
44 const scalar_t* suffix_head_ptr = suffix_output + src_head_offset;
45 scalar_t* output_head_ptr = output + dst_head_offset;
46
47 + // If token_idx >= prefix_num_tokens, just copy from suffix
48 + if (token_idx >= prefix_num_tokens) {
49 + if (pack_offset < head_size) {
50 + pack_128b_t s_out_pack = reinterpret_cast<const pack_128b_t*>(
51 + suffix_head_ptr)[pack_offset / pack_size];
52 + reinterpret_cast<pack_128b_t*>(output_head_ptr)[pack_offset / pack_size] =
53 + s_out_pack;
54 + }
55 + if (output_lse != nullptr && pack_idx == 0) {
56 + float s_lse = suffix_lse[head_idx * num_tokens + token_idx];
57 + output_lse[head_idx * num_tokens + token_idx] = s_lse;
58 + }
59 + return;
60 + }
61 +
62 + // For tokens within prefix range, merge prefix and suffix
63 float p_lse = prefix_lse[head_idx * num_tokens + token_idx];
64 float s_lse = suffix_lse[head_idx * num_tokens + token_idx];
65 p_lse = std::isinf(p_lse) ? -std::numeric_limits<float>::infinity() : p_lse;
@@ -143,7 +160,8 @@ __global__ void merge_attn_states_kernel(
160 reinterpret_cast<float*>(prefix_lse.data_ptr()), \
161 reinterpret_cast<scalar_t*>(suffix_output.data_ptr()), \
162 reinterpret_cast<float*>(suffix_lse.data_ptr()), num_tokens, \
146 - num_heads, head_size, prefix_head_stride, output_head_stride); \
163 + num_heads, head_size, prefix_head_stride, output_head_stride, \
164 + prefix_num_tokens); \
165 }
166
167 /*@brief Merges the attention states from prefix and suffix
@@ -157,14 +175,18 @@ __global__ void merge_attn_states_kernel(
175 * @param suffix_output [n,h,d] The suffix attention states.
176 * @param suffix_lse [h,n] The log-sum-exp values for the suffix attention
177 * states.
178 + * @param prefill_tokens_with_context Number of prefill tokens with context
179 + * For the first p tokens (0 <= token_idx < prefill_tokens_with_context), output
180 + * is computed by merging prefix_output and suffix_output. For remaining tokens
181 + * (prefill_tokens_with_context <= token_idx < n), output is copied directly
182 + * from suffix_output.
183 */
184 template <typename scalar_t>
162 -void merge_attn_states_launcher(torch::Tensor& output,
163 - std::optional<torch::Tensor> output_lse,
164 - const torch::Tensor& prefix_output,
165 - const torch::Tensor& prefix_lse,
166 - const torch::Tensor& suffix_output,
167 - const torch::Tensor& suffix_lse) {
185 +void merge_attn_states_launcher(
186 + torch::Tensor& output, std::optional<torch::Tensor> output_lse,
187 + const torch::Tensor& prefix_output, const torch::Tensor& prefix_lse,
188 + const torch::Tensor& suffix_output, const torch::Tensor& suffix_lse,
189 + const std::optional<int64_t> prefill_tokens_with_context) {
190 constexpr uint NUM_THREADS = 128;
191 const uint num_tokens = output.size(0);
192 const uint num_heads = output.size(1);
@@ -174,6 +196,14 @@ void merge_attn_states_launcher(torch::Tensor& output,
196 const uint pack_size = 16 / sizeof(scalar_t);
197 TORCH_CHECK(head_size % pack_size == 0,
198 "headsize must be multiple of pack_size:", pack_size);
199 +
200 + const uint prefix_num_tokens =
201 + prefill_tokens_with_context.has_value()
202 + ? static_cast<uint>(prefill_tokens_with_context.value())
203 + : num_tokens;
204 + TORCH_CHECK(prefix_num_tokens <= num_tokens,
205 + "prefix_num_tokens must be <= num_tokens");
206 +
207 float* output_lse_ptr = nullptr;
208 if (output_lse.has_value()) {
209 output_lse_ptr = output_lse.value().data_ptr<float>();
@@ -192,18 +222,17 @@ void merge_attn_states_launcher(torch::Tensor& output,
222 LAUNCH_MERGE_ATTN_STATES(scalar_t, NUM_THREADS);
223 }
224
195 -#define CALL_MERGE_ATTN_STATES_LAUNCHER(scalar_t) \
196 - { \
197 - merge_attn_states_launcher<scalar_t>(output, output_lse, prefix_output, \
198 - prefix_lse, suffix_output, \
199 - suffix_lse); \
225 +#define CALL_MERGE_ATTN_STATES_LAUNCHER(scalar_t) \
226 + { \
227 + merge_attn_states_launcher<scalar_t>( \
228 + output, output_lse, prefix_output, prefix_lse, suffix_output, \
229 + suffix_lse, prefill_tokens_with_context); \
230 }
231
202 -void merge_attn_states(torch::Tensor& output,
203 - std::optional<torch::Tensor> output_lse,
204 - const torch::Tensor& prefix_output,
205 - const torch::Tensor& prefix_lse,
206 - const torch::Tensor& suffix_output,
207 - const torch::Tensor& suffix_lse) {
232 +void merge_attn_states(
233 + torch::Tensor& output, std::optional<torch::Tensor> output_lse,
234 + const torch::Tensor& prefix_output, const torch::Tensor& prefix_lse,
235 + const torch::Tensor& suffix_output, const torch::Tensor& suffix_lse,
236 + std::optional<int64_t> prefill_tokens_with_context = std::nullopt) {
237 DISPATCH_BY_SCALAR_DTYPE(output.dtype(), CALL_MERGE_ATTN_STATES_LAUNCHER);
238 }
csrc/ops.h
+5 -6
@@ -53,12 +53,11 @@ void paged_attention_v2(
53 const int64_t blocksparse_vert_stride, const int64_t blocksparse_block_size,
54 const int64_t blocksparse_head_sliding_step);
55
56 -void merge_attn_states(torch::Tensor& output,
57 - std::optional<torch::Tensor> output_lse,
58 - const torch::Tensor& prefix_output,
59 - const torch::Tensor& prefix_lse,
60 - const torch::Tensor& suffix_output,
61 - const torch::Tensor& suffix_lse);
56 +void merge_attn_states(
57 + torch::Tensor& output, std::optional<torch::Tensor> output_lse,
58 + const torch::Tensor& prefix_output, const torch::Tensor& prefix_lse,
59 + const torch::Tensor& suffix_output, const torch::Tensor& suffix_lse,
60 + const std::optional<int64_t> prefill_tokens_with_context);
61 #ifndef USE_ROCM
62 void convert_vertical_slash_indexes(
63 torch::Tensor& block_count, // [BATCH, N_HEADS, NUM_ROWS]
csrc/torch_bindings.cpp
+2 -1
@@ -73,7 +73,8 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
73 " Tensor prefix_output,"
74 " Tensor prefix_lse,"
75 " Tensor suffix_output,"
76 - " Tensor suffix_lse) -> ()");
76 + " Tensor suffix_lse,"
77 + " int!? prefill_tokens_with_context) -> ()");
78 ops.impl("merge_attn_states", torch::kCUDA, &merge_attn_states);
79 #ifndef USE_ROCM
80 ops.def(
tests/kernels/attention/test_merge_attn_states.py
+26 -2
@@ -20,7 +20,11 @@ def merge_attn_states_torch(
20 suffix_output: torch.Tensor, # [NUM_TOKENS, NUM_HEADS, HEAD_SIZE]
21 suffix_lse: torch.Tensor, # [NUM_HEADS, NUM_TOKENS]
22 output_lse: torch.Tensor | None = None, # [NUM_HEADS, NUM_TOKENS]
23 + prefill_tokens_with_context: int | None = None,
24 ):
25 + # Apply prefill_tokens_with_context mask if needed
26 + if prefill_tokens_with_context is None:
27 + prefill_tokens_with_context = output.shape[0]
28 p_lse = prefix_lse
29 s_lse = suffix_lse
30 # inf -> -inf
@@ -28,6 +32,9 @@ def merge_attn_states_torch(
32 s_lse[s_lse == torch.inf] = -torch.inf
33 # max_lse [NUM_HEADS, NUM_TOKENS]
34 max_lse = torch.maximum(p_lse, s_lse)
35 +
36 + mask = torch.ones((prefix_lse.shape[1], 1, 1), device=p_lse.device)
37 + mask[prefill_tokens_with_context:].fill_(0)
38 p_lse = p_lse - max_lse
39 s_lse = s_lse - max_lse
40 p_lse_exp = torch.exp(p_lse)
@@ -35,11 +42,16 @@ def merge_attn_states_torch(
42 out_se = p_lse_exp + s_lse_exp
43 if output_lse is not None:
44 output_lse = torch.log(out_se) + max_lse
45 + output_lse[prefill_tokens_with_context:] = suffix_lse[
46 + prefill_tokens_with_context:
47 + ]
48 p_scale = p_lse_exp / out_se # [NUM_HEADS, NUM_TOKENS]
49 s_scale = s_lse_exp / out_se # [NUM_HEADS, NUM_TOKENS]
50 p_scale = torch.transpose(p_scale, 0, 1).unsqueeze(2) # [NUM_TOKENS, NUM_HEADS, 1]
51 s_scale = torch.transpose(s_scale, 0, 1).unsqueeze(2) # [NUM_TOKENS, NUM_HEADS, 1]
42 - output = prefix_output * p_scale + suffix_output * s_scale
52 + output.copy_(
53 + prefix_output * p_scale * mask + suffix_output * (s_scale * mask + (1 - mask))
54 + )
55 return output, output_lse
56
57
@@ -90,13 +102,18 @@ def generate_markdown_table():
102 )
103
104
105 +@pytest.mark.parametrize("prefill_tokens_with_context", [None, 128])
106 @pytest.mark.parametrize("num_tokens", NUM_BATCH_TOKENS)
107 @pytest.mark.parametrize("num_query_heads", NUM_QUERY_HEADS)
108 @pytest.mark.parametrize("head_size", HEAD_SIZES)
109 @pytest.mark.parametrize("output_dtype", DTYPES)
110 @torch.inference_mode()
111 def test_merge_attn_states(
99 - num_tokens: int, num_query_heads: int, head_size: int, output_dtype: torch.dtype
112 + prefill_tokens_with_context: int | None,
113 + num_tokens: int,
114 + num_query_heads: int,
115 + head_size: int,
116 + output_dtype: torch.dtype,
117 ):
118 if not current_platform.is_cuda():
119 pytest.skip(
@@ -111,6 +128,7 @@ def test_merge_attn_states(
128 print(
129 f"\nNUM_TOKENS:{NUM_TOKENS}, NUM_HEADS:{NUM_HEADS}, "
130 f"HEAD_SIZE:{HEAD_SIZE}, DTYPE: {output_dtype}, "
131 + f"prefill_tokens_with_context: {prefill_tokens_with_context}, "
132 f"Device: {current_platform.get_device_name()}"
133 )
134
@@ -164,6 +182,7 @@ def test_merge_attn_states(
182 suffix_output,
183 suffix_lse_torch,
184 output_lse_torch,
185 + prefill_tokens_with_context,
186 )
187 torch.accelerator.synchronize()
188
@@ -176,6 +195,7 @@ def test_merge_attn_states(
195 suffix_output,
196 suffix_lse_torch,
197 output_lse_torch,
198 + prefill_tokens_with_context,
199 )
200 end.record()
201 torch.accelerator.synchronize()
@@ -199,6 +219,7 @@ def test_merge_attn_states(
219 suffix_output,
220 suffix_lse,
221 output_lse_ref_triton,
222 + prefill_tokens_with_context,
223 )
224 torch.accelerator.synchronize()
225
@@ -211,6 +232,7 @@ def test_merge_attn_states(
232 suffix_output,
233 suffix_lse,
234 output_lse_ref_triton,
235 + prefill_tokens_with_context,
236 )
237 end.record()
238 torch.accelerator.synchronize()
@@ -231,6 +253,7 @@ def test_merge_attn_states(
253 suffix_output,
254 suffix_lse,
255 output_lse_cuda,
256 + prefill_tokens_with_context,
257 )
258 torch.accelerator.synchronize()
259
@@ -243,6 +266,7 @@ def test_merge_attn_states(
266 suffix_output,
267 suffix_lse,
268 output_lse_cuda,
269 + prefill_tokens_with_context,
270 )
271 end.record()
272 torch.accelerator.synchronize()
vllm/_custom_ops.py
+8 -1
@@ -264,9 +264,16 @@ def merge_attn_states(
264 suffix_output: torch.Tensor,
265 suffix_lse: torch.Tensor,
266 output_lse: torch.Tensor | None = None,
267 + prefill_tokens_with_context: int | None = None,
268 ) -> None:
269 torch.ops._C.merge_attn_states(
269 - output, output_lse, prefix_output, prefix_lse, suffix_output, suffix_lse
270 + output,
271 + output_lse,
272 + prefix_output,
273 + prefix_lse,
274 + suffix_output,
275 + suffix_lse,
276 + prefill_tokens_with_context,
277 )
278
279
vllm/model_executor/layers/attention/mla_attention.py
+14 -2
@@ -1181,6 +1181,7 @@ class MLACommonPrefillMetadata:
1181 padded_local_cu_seq_lens: torch.Tensor | None = None
1182 cu_seq_lens_lst: list[list[int]] | None = None
1183 chunk_size: int | None = None
1184 + prefill_tokens_with_context: int | None = None
1185
1186 block_table: torch.Tensor
1187 query_start_loc: torch.Tensor
@@ -1743,6 +1744,9 @@ class MLACommonMetadataBuilder(AttentionMetadataBuilder[M]):
1744 prefill_query_start_loc = (
1745 query_start_loc[reqs_start:] - query_start_loc[reqs_start]
1746 )
1747 + prefill_query_start_loc_cpu = (
1748 + query_start_loc_cpu[reqs_start:] - query_start_loc_cpu[reqs_start]
1749 + )
1750
1751 chunked_context_metadata = None
1752 if max_context_len_cpu > 0:
@@ -1864,6 +1868,11 @@ class MLACommonMetadataBuilder(AttentionMetadataBuilder[M]):
1868 if self._use_cudnn_prefill
1869 else MLACommonPrefillMetadata.ChunkedContextMetadata
1870 )
1871 + prefill_tokens_with_context = None
1872 + if num_prefills_with_context_cpu > 0:
1873 + prefill_tokens_with_context = prefill_query_start_loc_cpu[
1874 + num_prefills_with_context_cpu
1875 + ].item()
1876 if self.dcp_world_size > 1:
1877 chunked_context_metadata = chunked_context_metadata_cls(
1878 cu_seq_lens=cu_seq_lens_cpu.to(device, non_blocking=True),
@@ -1883,6 +1892,7 @@ class MLACommonMetadataBuilder(AttentionMetadataBuilder[M]):
1892 ),
1893 cu_seq_lens_lst=cu_seq_lens_cpu.tolist(),
1894 chunk_size=padded_local_max_context_chunk_across_ranks,
1895 + prefill_tokens_with_context=prefill_tokens_with_context,
1896 )
1897 else:
1898 chunked_context_metadata = chunked_context_metadata_cls(
@@ -1896,6 +1906,7 @@ class MLACommonMetadataBuilder(AttentionMetadataBuilder[M]):
1906 ),
1907 chunk_total_token=chunk_total_token,
1908 workspace=self.chunked_prefill_workspace,
1909 + prefill_tokens_with_context=prefill_tokens_with_context,
1910 )
1911
1912 if self._use_cudnn_prefill:
@@ -2382,14 +2393,13 @@ class MLACommonImpl(MLAAttentionImpl[M], Generic[M]):
2393 assert prefill.chunked_context.seq_lens[chunk_idx] is not None
2394 assert prefill.workspace_buffer is not None
2395
2385 - out = torch.zeros(
2396 + out = torch.empty(
2397 q.shape[0],
2398 q.shape[1],
2399 v.shape[2],
2400 device=q.device,
2401 dtype=prefill.output_dtype,
2402 )
2392 - prefill.workspace_buffer.fill_(0)
2403
2404 attn_out, lse = trtllm_ragged_attention_deepseek(
2405 query=q,
@@ -2691,6 +2701,7 @@ class MLACommonImpl(MLAAttentionImpl[M], Generic[M]):
2701 )
2702
2703 if has_context:
2704 + assert prefill_metadata.chunked_context is not None
2705 suffix_output, suffix_lse = output_prefill
2706 if self.dcp_world_size > 1:
2707 context_output, context_lse = (
@@ -2719,6 +2730,7 @@ class MLACommonImpl(MLAAttentionImpl[M], Generic[M]):
2730 prefix_lse=context_lse,
2731 suffix_output=suffix_output,
2732 suffix_lse=suffix_lse,
2733 + prefill_tokens_with_context=prefill_metadata.chunked_context.prefill_tokens_with_context,
2734 )
2735 else:
2736 output_prefill = output_prefill[..., : v.shape[-1]].flatten(start_dim=-2)
vllm/v1/attention/ops/merge_attn_states.py
+43 -2
@@ -13,7 +13,36 @@ def merge_attn_states(
13 suffix_output: torch.Tensor,
14 suffix_lse: torch.Tensor,
15 output_lse: torch.Tensor | None = None,
16 + prefill_tokens_with_context: int | None = None,
17 ) -> None:
18 + """Merge partial attention outputs from prefix (KV cache) and suffix
19 + (new tokens) into a single output tensor using the log-sum-exp (LSE)
20 + rescaling method described in section 2.2 of
21 + https://www.arxiv.org/pdf/2501.01005.
22 +
23 + For tokens that have prefix context (token index < prefill_tokens_with_context),
24 + the prefix and suffix partial outputs are combined as a weighted sum.
25 + For tokens without prefix context, the suffix output is copied directly.
26 +
27 + Args:
28 + output: Output tensor of shape [NUM_TOKENS, NUM_HEADS, HEAD_SIZE].
29 + prefix_output: Partial attention output over the prefix (KV cache),
30 + shape [NUM_TOKENS, NUM_HEADS, HEAD_SIZE].
31 + prefix_lse: Log-sum-exp values for the prefix attention,
32 + shape [NUM_HEADS, NUM_TOKENS].
33 + suffix_output: Partial attention output over the suffix (new KV),
34 + shape [NUM_TOKENS, NUM_HEADS, HEAD_SIZE].
35 + suffix_lse: Log-sum-exp values for the suffix attention,
36 + shape [NUM_HEADS, NUM_TOKENS].
37 + output_lse: Optional tensor to store the merged LSE values,
38 + shape [NUM_HEADS, NUM_TOKENS]. If None, LSE is not written out.
39 + prefill_tokens_with_context: Number of prefill tokens that have
40 + prefix context and therefore require merging. Tokens at indices
41 + >= this value are decode or context-free prefill tokens whose
42 + output is taken directly from suffix_output. If None, all tokens
43 + are treated as having context.
44 + """
45 +
46 # NOTE(DefTruth): Currently, custom merge_attn_states CUDA kernel
47 # does not support FP8 dtype, fallback to use Triton kernel.
48 def supported_dtypes(o: torch.Tensor) -> bool:
@@ -37,11 +66,23 @@ def merge_attn_states(
66 from vllm._custom_ops import merge_attn_states
67
68 return merge_attn_states(
40 - output, prefix_output, prefix_lse, suffix_output, suffix_lse, output_lse
69 + output,
70 + prefix_output,
71 + prefix_lse,
72 + suffix_output,
73 + suffix_lse,
74 + output_lse,
75 + prefill_tokens_with_context,
76 )
77 else:
78 from vllm.v1.attention.ops.triton_merge_attn_states import merge_attn_states
79
80 return merge_attn_states(
46 - output, prefix_output, prefix_lse, suffix_output, suffix_lse, output_lse
81 + output,
82 + prefix_output,
83 + prefix_lse,
84 + suffix_output,
85 + suffix_lse,
86 + output_lse,
87 + prefill_tokens_with_context,
88 )
vllm/v1/attention/ops/triton_merge_attn_states.py
+39 -2
@@ -15,6 +15,7 @@ def merge_attn_states(
15 suffix_output: torch.Tensor,
16 suffix_lse: torch.Tensor,
17 output_lse: torch.Tensor | None = None,
18 + prefill_tokens_with_context: int | None = None,
19 ) -> None:
20 num_tokens = output.shape[0]
21 num_query_heads = output.shape[1]
@@ -25,6 +26,11 @@ def merge_attn_states(
26 # backend.
27 prefix_head_stride = prefix_output.stride(1)
28 output_head_stride = output.stride(1)
29 +
30 + # If prefill_tokens_with_context is None, all tokens should use prefix context
31 + if prefill_tokens_with_context is None:
32 + prefill_tokens_with_context = num_tokens
33 +
34 # TODO(woosuk): Use CUDA kernel instead of Triton to minimize CPU overhead.
35 merge_attn_states_kernel[(num_tokens, num_query_heads)](
36 output,
@@ -38,6 +44,7 @@ def merge_attn_states(
44 head_size,
45 padded_head_size,
46 output_lse is not None,
47 + prefill_tokens_with_context,
48 )
49
50
@@ -54,12 +61,44 @@ def merge_attn_states_kernel(
61 HEAD_SIZE: tl.constexpr,
62 PADDED_HEAD_SIZE: tl.constexpr,
63 OUTPUT_LSE: tl.constexpr,
64 + prefill_tokens_with_context: tl.constexpr,
65 ):
66 token_idx = tl.program_id(0)
67 num_tokens = tl.num_programs(0)
68 head_idx = tl.program_id(1)
69 num_heads = tl.num_programs(1)
70
71 + prefix_mask = token_idx < prefill_tokens_with_context
72 +
73 + head_arange = tl.arange(0, PADDED_HEAD_SIZE)
74 + head_mask = head_arange < HEAD_SIZE
75 +
76 + # For tokens without context (token_idx >= prefill_tokens_with_context),
77 + # directly copy from suffix_output
78 + if not prefix_mask:
79 + s_lse = tl.load(suffix_lse + head_idx * num_tokens + token_idx)
80 + if OUTPUT_LSE:
81 + tl.store(output_lse + head_idx * num_tokens + token_idx, s_lse)
82 +
83 + s_out = tl.load(
84 + suffix_output
85 + + token_idx * num_heads * prefix_head_stride
86 + + head_idx * prefix_head_stride
87 + + head_arange,
88 + mask=head_mask,
89 + )
90 + tl.store(
91 + output
92 + + token_idx * num_heads * output_head_stride
93 + + head_idx * output_head_stride
94 + + head_arange,
95 + s_out,
96 + mask=head_mask,
97 + )
98 + return
99 +
100 + # For tokens with context (token_idx < prefill_tokens_with_context),
101 + # perform normal merge operation
102 p_lse = tl.load(prefix_lse + head_idx * num_tokens + token_idx)
103 s_lse = tl.load(suffix_lse + head_idx * num_tokens + token_idx)
104
@@ -83,8 +122,6 @@ def merge_attn_states_kernel(
122 out_lse = tl.log(out_se) + max_lse
123 tl.store(output_lse + head_idx * num_tokens + token_idx, out_lse)
124
86 - head_arange = tl.arange(0, PADDED_HEAD_SIZE)
87 - head_mask = head_arange < HEAD_SIZE
125 p_out = tl.load(
126 prefix_output
127 + token_idx * num_heads * prefix_head_stride