@@ -124,6 +124,46 @@ def test_sparse_kernels_recognize_fp8_dtypes(dtype: torch.dtype):
124124
125125
126126# Index top-k kernels.
127+ def _assert_prefill_index_scores (
128+ actual : torch .Tensor ,
129+ idx_q : torch .Tensor ,
130+ index_kv_cache : torch .Tensor ,
131+ block_table : torch .Tensor ,
132+ q_lens : torch .Tensor ,
133+ seq_lens : torch .Tensor ,
134+ prefix_lens : torch .Tensor ,
135+ block_size_q : int ,
136+ ) -> None :
137+ q_start = 0
138+ for req_id , (q_len , seq_len , prefix_len ) in enumerate (
139+ zip (q_lens .tolist (), seq_lens .tolist (), prefix_lens .tolist ())
140+ ):
141+ q = idx_q [q_start : q_start + q_len ]
142+ num_blocks = (seq_len + BLOCK_SIZE - 1 ) // BLOCK_SIZE
143+ pages = block_table [req_id , :num_blocks ]
144+ k = index_kv_cache [pages ].reshape (num_blocks * BLOCK_SIZE , - 1 )
145+ expected = torch .einsum ("qhd,kd->hqk" , q .float (), k .float ())
146+
147+ q_pos = prefix_len + torch .arange (q_len , device = idx_q .device )
148+ k_pos = torch .arange (k .shape [0 ], device = idx_q .device )
149+ expected .masked_fill_ (k_pos [None , :] > q_pos [:, None ], - float ("inf" ))
150+ expected = (
151+ expected .reshape (idx_q .shape [1 ], q_len , num_blocks , BLOCK_SIZE )
152+ .max (dim = 3 )
153+ .values
154+ )
155+
156+ for local_q in range (q_len ):
157+ q_block_end = min (q_len , (local_q // block_size_q + 1 ) * block_size_q )
158+ hi = min (seq_len , prefix_len + q_block_end )
159+ written_blocks = (hi + BLOCK_SIZE - 1 ) // BLOCK_SIZE
160+ torch .testing .assert_close (
161+ actual [:, q_start + local_q , :written_blocks ],
162+ expected [:, local_q , :written_blocks ],
163+ )
164+ q_start += q_len
165+
166+
127167def _reference_index_topk (
128168 idx_q : torch .Tensor ,
129169 index_kv_cache : torch .Tensor ,
@@ -222,14 +262,34 @@ def _reference_decode_index_score(
222262 return out
223263
224264
225- def test_prefill_index_topk_correctness ():
265+ @pytest .mark .parametrize ("long_context" , [False , True ])
266+ def test_prefill_index_topk_correctness (long_context : bool ):
267+ if current_platform .is_rocm ():
268+ from vllm .models .minimax_m3 .amd .ops .index_topk import (
269+ minimax_m3_index_score as amd_index_score ,
270+ )
271+
272+ index_score = amd_index_score
273+ else :
274+ index_score = minimax_m3_index_score
275+
276+ if long_context :
277+ if not current_platform .is_rocm ():
278+ pytest .skip ("The split-K index-score path is ROCm-specific." )
279+ from vllm .platforms .rocm import on_gfx942
280+
281+ if not on_gfx942 ():
282+ pytest .skip ("The split-K index-score path is enabled on gfx942." )
283+
226284 topk = 6
227285 init_blocks = 0
228286 local_blocks = 1
229287 num_idx_heads = 2
230288 head_dim = 16
231- q_lens = torch .tensor ((4 , 3 ), device = "cuda" , dtype = torch .int32 )
232- prefix_lens = torch .tensor ((0 , 1024 ), device = "cuda" , dtype = torch .int32 )
289+ q_lens_values = (128 , 129 ) if long_context else (4 , 3 )
290+ prefix_lens_values = (8192 , 16384 ) if long_context else (0 , 1024 )
291+ q_lens = torch .tensor (q_lens_values , device = "cuda" , dtype = torch .int32 )
292+ prefix_lens = torch .tensor (prefix_lens_values , device = "cuda" , dtype = torch .int32 )
233293 seq_lens = prefix_lens + q_lens
234294 batch = q_lens .numel ()
235295 max_seq_len = seq_lens .max ().item ()
@@ -242,13 +302,15 @@ def test_prefill_index_topk_correctness():
242302 batch , max_blocks
243303 )
244304 idx_q = torch .ones (q_lens .sum ().item (), num_idx_heads , head_dim , device = "cuda" )
245- index_kv_cache = torch .empty (num_pages , BLOCK_SIZE , head_dim , device = "cuda" )
246- for req_id in range (batch ):
247- for block_id in range (max_blocks ):
248- page = block_table [req_id , block_id ]
249- index_kv_cache [page ].fill_ (block_id + 1 )
305+ block_values = torch .empty (num_pages , device = "cuda" )
306+ block_values [block_table ] = torch .arange (
307+ 1 , max_blocks + 1 , device = "cuda" , dtype = torch .float32
308+ ).expand (batch , - 1 )
309+ index_kv_cache = (
310+ block_values [:, None , None ].expand (- 1 , BLOCK_SIZE , head_dim ).contiguous ()
311+ )
250312
251- score = minimax_m3_index_score (
313+ score = index_score (
252314 idx_q ,
253315 index_kv_cache ,
254316 block_table ,
@@ -259,6 +321,16 @@ def test_prefill_index_topk_correctness():
259321 max_seq_len = max_seq_len ,
260322 num_kv_heads = num_idx_heads ,
261323 )
324+ _assert_prefill_index_scores (
325+ score ,
326+ idx_q ,
327+ index_kv_cache ,
328+ block_table ,
329+ q_lens ,
330+ seq_lens ,
331+ prefix_lens ,
332+ block_size_q = 128 if long_context else 64 ,
333+ )
262334 actual = minimax_m3_index_topk (
263335 score ,
264336 cu_seqlens ,
0 commit comments