[Kimi-K3][Kernel] Optimize the low-M fused latent MoE tail (#54168)

Signed-off-by: Yongye Zhu <zyy1102000@gmail.com> Co-authored-by: OpenAI Codex <codex@openai.com>

Yongye Zhu committed Aug 28, 2026 at 10:45 UTC d9dabfa351acdb4ceaad18c9677bc7a08b6c04ea
6 files changed +653 -194
tests/models/kimi_k3/test_latent_moe_tail.py
+107 -2
@@ -153,6 +153,57 @@ def _make_deferred_routed_output(
153 )
154
155
156 +def _make_bf16_top16_deferred_output(
157 + num_tokens: int,
158 + device: torch.device,
159 + *,
160 + drop_last_route: bool = False,
161 +) -> tuple[UnfinalizedMoEOutput, torch.Tensor]:
162 + top_k = 16
163 + num_routes = num_tokens * top_k
164 + num_permuted_rows = num_routes + 7
165 + expanded_output = torch.randn(
166 + num_routes,
167 + LATENT_SIZE,
168 + device=device,
169 + dtype=torch.bfloat16,
170 + ).mul_(0.01)
171 + expert_weights = torch.rand(
172 + num_tokens,
173 + top_k,
174 + device=device,
175 + dtype=torch.bfloat16,
176 + )
177 + expert_weights.div_(expert_weights.sum(dim=-1, keepdim=True))
178 + expanded_idx = torch.randperm(num_permuted_rows, device=device)[:num_routes]
179 + gemm2_permuted = torch.empty(
180 + num_permuted_rows,
181 + LATENT_SIZE,
182 + device=device,
183 + dtype=torch.bfloat16,
184 + )
185 + gemm2_permuted[expanded_idx] = expanded_output
186 +
187 + expanded_output = expanded_output.view(num_tokens, top_k, LATENT_SIZE)
188 + expanded_idx = expanded_idx.view(num_tokens, top_k)
189 + if drop_last_route:
190 + expanded_output[:, -1].zero_()
191 + expanded_idx[:, -1] = -1
192 + finalized = (
193 + (expanded_output.float() * expert_weights[:, :, None].float())
194 + .sum(1)
195 + .to(torch.bfloat16)
196 + )
197 + return (
198 + UnfinalizedMoEOutput(
199 + gemm2_permuted=gemm2_permuted,
200 + expert_weights=expert_weights,
201 + expanded_idx_to_permuted_idx=expanded_idx.to(torch.int32),
202 + ),
203 + finalized,
204 + )
205 +
206 +
207 @ray.remote(num_gpus=1, max_calls=1)
208 def _test_latent_moe_tail_worker(
209 monkeypatch: pytest.MonkeyPatch,
@@ -197,7 +248,8 @@ def _test_latent_moe_tail_worker(
248 )
249 cutedsl_warmup()
250
200 - for iteration, num_tokens in enumerate((1, 5, 8, 16, 5)):
251 + # M=65 crosses two 32-CTA token waves and reuses the first DSM slot.
252 + for iteration, num_tokens in enumerate((1, 5, 8, 16, 33, 65, 5)):
253 torch.manual_seed(100 * iteration + rank + 1)
254 routed_output = torch.randn(
255 num_tokens,
@@ -310,9 +362,17 @@ def _test_deferred_finalize_parity_worker(
362 rms_eps=EPS,
363 experts_per_token=TOP_K,
364 )
365 + bf16_deferred_op = KimiK3LatentMoETailOp.initialize(
366 + hidden_size=HIDDEN_SIZE,
367 + latent_size=LATENT_SIZE,
368 + dtype=torch.bfloat16,
369 + device=device,
370 + rms_eps=EPS,
371 + experts_per_token=16,
372 + )
373 cutedsl_warmup()
374
315 - for iteration, num_tokens in enumerate((1, 5, 16)):
375 + for iteration, num_tokens in enumerate((1, 5, 16, 33, 65)):
376 torch.manual_seed(2000 + 100 * iteration + rank)
377 deferred_output, finalized_output = _make_deferred_routed_output(
378 num_tokens,
@@ -340,6 +400,51 @@ def _test_deferred_finalize_parity_worker(
400
401 torch.testing.assert_close(actual, expected, atol=8e-2, rtol=3e-2)
402
403 + for iteration, num_tokens in enumerate((1, 2, 4, 5, 16)):
404 + torch.manual_seed(3000 + 100 * iteration + rank)
405 + deferred_output, finalized_output = _make_bf16_top16_deferred_output(
406 + num_tokens,
407 + device,
408 + drop_last_route=num_tokens == 5,
409 + )
410 + shared_output = torch.randn(
411 + num_tokens,
412 + HIDDEN_SIZE,
413 + device=device,
414 + dtype=torch.bfloat16,
415 + )
416 +
417 + expected = finalized_op(
418 + finalized_output,
419 + shared_output,
420 + rms_weight,
421 + up_weight,
422 + )
423 + actual = bf16_deferred_op(
424 + deferred_output,
425 + shared_output,
426 + rms_weight,
427 + up_weight,
428 + )
429 +
430 + torch.testing.assert_close(actual, expected, atol=8e-2, rtol=3e-2)
431 + if num_tokens == 4:
432 + graph = torch.cuda.CUDAGraph()
433 + with torch.cuda.graph(graph):
434 + graph_actual = bf16_deferred_op(
435 + deferred_output,
436 + shared_output,
437 + rms_weight,
438 + up_weight,
439 + )
440 + graph.replay()
441 + torch.testing.assert_close(
442 + graph_actual,
443 + expected,
444 + atol=8e-2,
445 + rtol=3e-2,
446 + )
447 +
448
449 def _run_deferred_finalize_parity_test(
450 monkeypatch: pytest.MonkeyPatch,
vllm/models/kimi_k3/nvidia/ops/cute_dsl/latent_moe_tail/allreduce_rmsnorm_reduce_scatter_early_exit.py
+327 -161
@@ -23,21 +23,27 @@ from .primitives import (
23 PACKED_BYTES,
24 VEC_BF16,
25 bf16x8_to_packed_u32x4,
26 - block_sum_specialized,
26 + finalize_top16_bf16,
27 fragment_is_dirty,
28 load_global_u32x4,
29 + load_shared_f32x2,
30 + load_shared_f32x4,
31 load_volatile_u32,
32 map_shared_to_peer,
33 packed_u32x4_to_bf16x8,
34 red_async_release_gpu_add_u32,
35 sanitize_negative_zero,
36 + stmc_bf16x8,
37 store_global_u32x4,
38 store_lamport_sentinel_128,
39 store_shared_cluster_f32,
40 to_cute,
41 to_cute_dynamic_m,
42 + warp_sum_specialized,
43 )
44
45 +_SEVEN_CTA_MAX_M = 4
46 +
47
48 def _mapping(
49 tp_size: int,
@@ -92,7 +98,7 @@ def _select_routed_schedule(
98
99
100 class AllReduceRMSNormWithReduceScatterEarlyExit:
95 - """One routed role plus one ReduceScatter role per destination group."""
101 + """One routed role plus one compact ReduceScatter role."""
102
103 def __init__(
104 self,
@@ -127,22 +133,44 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
133 mapped_threads,
134 shared_roles,
135 ) = _mapping(tp_size, latent_dim, hidden_dim)
136 + seven_cta_geometry = (
137 + include_routed
138 + and max_m <= _SEVEN_CTA_MAX_M
139 + and max_token_ctas == max_m
140 + and (tp_size, latent_dim, hidden_dim) == (8, 3584, 7168)
141 + )
142 + self.seven_cta_geometry = seven_cta_geometry
143 + self.single_token_geometry = seven_cta_geometry and max_m == 1
144 if include_reduce_scatter:
131 - self.cluster_ctas = mapped_cluster
132 - self.threads = mapped_threads
145 + if seven_cta_geometry:
146 + self.cluster_ctas = 7
147 + self.threads = 64
148 + shared_roles = (tp_size + self.cluster_ctas - 1) // self.cluster_ctas
149 + else:
150 + self.cluster_ctas = mapped_cluster
151 + self.threads = mapped_threads
152 else:
134 - self.threads, self.cluster_ctas = _select_routed_schedule(
135 - tp_size, latent_dim, hidden_dim, max_m
136 - )
153 + if seven_cta_geometry:
154 + self.threads, self.cluster_ctas = 64, 7
155 + else:
156 + self.threads, self.cluster_ctas = _select_routed_schedule(
157 + tp_size, latent_dim, hidden_dim, max_m
158 + )
159 + self.shared_roles = 1 if include_reduce_scatter else shared_roles
160 + self.shared_destination_stride = self.shared_roles * self.cluster_ctas
161 + self.shared_destinations_per_cta = (
162 + self.tp_size + self.shared_destination_stride - 1
163 + ) // self.shared_destination_stride
164 + self.shard_vectors = self.shard_dim // VEC_BF16
165 # Diagnostic specialization: a one-role grid executes only the routed
166 # AllReduce/RMSNorm path. Keep this compile-time so the production
167 # fused path is unchanged when include_reduce_scatter=True.
168 if include_routed and include_reduce_scatter:
141 - self.roles = 1 + shared_roles
169 + self.roles = 1 + self.shared_roles
170 elif include_routed:
171 self.roles = 1
172 else:
145 - self.roles = shared_roles
173 + self.roles = self.shared_roles
174 self.warps = (self.threads + 31) // 32
175 self.last_warp_lanes = self.threads - (self.warps - 1) * 32
176 self.last_warp_mask = (1 << self.last_warp_lanes) - 1
@@ -178,6 +206,7 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
206 expert_weights: cute.Tensor,
207 expanded_idx_to_permuted_idx: cute.Tensor,
208 ):
209 + grid_x = m if cutlass.const_expr(self.seven_cta_geometry) else self.token_ctas
210 self.kernel(
211 latent_source,
212 gamma,
@@ -195,10 +224,10 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
224 expert_weights,
225 expanded_idx_to_permuted_idx,
226 ).launch(
198 - grid=(self.token_ctas, self.cluster_ctas, self.roles),
227 + grid=(grid_x, self.cluster_ctas, self.roles),
228 block=(self.threads, 1, 1),
229 cluster=(1, self.cluster_ctas, 1),
201 - smem=(self.warps + self.cluster_ctas) * 4,
230 + smem=2 * self.cluster_ctas * self.warps * 4,
231 stream=stream,
232 use_pdl=True,
233 )
@@ -233,6 +262,7 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
262
263 cute.arch.griddepcontrol_wait()
264 token = token_cta
265 + parity = Int32(0)
266 while token < m:
267 self._token_device(
268 latent_source,
@@ -249,6 +279,7 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
279 m,
280 epsilon,
281 token,
282 + parity,
283 token_cta,
284 cta_y,
285 logical_role,
@@ -258,6 +289,7 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
289 expanded_idx_to_permuted_idx,
290 )
291 token = token + self.token_ctas
292 + parity = parity ^ Int32(1)
293
294 @cute.jit
295 def _token_device(
@@ -276,6 +308,7 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
308 m: Int32,
309 epsilon: Float32,
310 token: Int32,
311 + parity: Int32,
312 token_cta: Int32,
313 cta_y: Int32,
314 role: Int32,
@@ -308,36 +341,72 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
341
342 if cutlass.const_expr(self.top_k > 0):
343 # finalize topk reduction
311 - local_values = cute.make_rmem_tensor(
312 - cute.make_layout((VEC_BF16,)), BFloat16
313 - )
314 - for element in cutlass.range_constexpr(VEC_BF16):
315 - local_values[element] = BFloat16(0.0)
316 - for slot in cutlass.range_constexpr(self.top_k):
317 - permuted_idx = expanded_idx_to_permuted_idx[token, slot]
318 - if permuted_idx >= Int32(0):
319 - permuted_element = (
320 - Int64(permuted_idx) * self.latent_dim
321 - + Int64(packed_idx) * VEC_BF16
322 - )
323 - permuted_ptr = cute.make_ptr(
324 - BFloat16,
325 - (latent_source.iterator + permuted_element).llvm_ptr,
326 - cute.AddressSpace.gmem,
327 - assumed_align=16,
328 - )
329 - values = packed_u32x4_to_bf16x8(
330 - load_global_u32x4(permuted_ptr, volatile=False)
344 + if cutlass.const_expr(
345 + self.top_k == 16
346 + and self.latent_dim == 3584
347 + and expert_weights.element_type == BFloat16
348 + ):
349 + gemm2_vector = cute.make_ptr(
350 + BFloat16,
351 + (
352 + latent_source.iterator + Int64(packed_idx) * VEC_BF16
353 + ).llvm_ptr,
354 + cute.AddressSpace.gmem,
355 + assumed_align=16,
356 + )
357 + route_indices = cute.make_ptr(
358 + Int32,
359 + (
360 + expanded_idx_to_permuted_idx.iterator
361 + + Int64(token) * self.top_k
362 + ).llvm_ptr,
363 + cute.AddressSpace.gmem,
364 + assumed_align=16,
365 + )
366 + route_weights = cute.make_ptr(
367 + BFloat16,
368 + (expert_weights.iterator + Int64(token) * self.top_k).llvm_ptr,
369 + cute.AddressSpace.gmem,
370 + assumed_align=16,
371 + )
372 + local_packed = sanitize_negative_zero(
373 + finalize_top16_bf16(
374 + gemm2_vector,
375 + route_indices,
376 + route_weights,
377 )
332 - weight = expert_weights[token, slot].to(Float32)
333 - for element in cutlass.range_constexpr(VEC_BF16):
334 - scaled = (values[element].to(Float32) * weight).to(BFloat16)
335 - local_values[element] = (
336 - local_values[element].to(Float32) + scaled.to(Float32)
337 - ).to(BFloat16)
338 - local_packed = sanitize_negative_zero(
339 - bf16x8_to_packed_u32x4(local_values.load())
340 - )
378 + )
379 + else:
380 + local_values = cute.make_rmem_tensor(
381 + cute.make_layout((VEC_BF16,)), Float32
382 + )
383 + for element in cutlass.range_constexpr(VEC_BF16):
384 + local_values[element] = Float32(0.0)
385 + for slot in cutlass.range_constexpr(self.top_k):
386 + permuted_idx = expanded_idx_to_permuted_idx[token, slot]
387 + if permuted_idx >= Int32(0):
388 + permuted_element = (
389 + Int64(permuted_idx) * self.latent_dim
390 + + Int64(packed_idx) * VEC_BF16
391 + )
392 + permuted_ptr = cute.make_ptr(
393 + BFloat16,
394 + (latent_source.iterator + permuted_element).llvm_ptr,
395 + cute.AddressSpace.gmem,
396 + assumed_align=16,
397 + )
398 + values = packed_u32x4_to_bf16x8(
399 + load_global_u32x4(permuted_ptr, volatile=False)
400 + )
401 + weight = expert_weights[token, slot].to(Float32)
402 + for element in cutlass.range_constexpr(VEC_BF16):
403 + local_values[element] = (
404 + local_values[element]
405 + + values[element].to(Float32) * weight
406 + )
407 + local_packed = sanitize_negative_zero(
408 + bf16x8_to_packed_u32x4(local_values.load().to(BFloat16))
409 + )
410 else:
411 local_ptr = cute.make_ptr(
412 BFloat16,
@@ -356,17 +425,20 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
425 )
426 * 2
427 )
359 - store_global_u32x4(
428 + stmc_bf16x8(
429 latent_multicast_ptr + multicast_offset,
430 local_packed,
362 - volatile=False,
431 )
432 + cute.arch.griddepcontrol_launch_dependents()
433
365 - cute.arch.cluster_arrive()
366 - if cluster_rank == 0 and tidx < 32:
367 - cute.arch.cluster_wait()
368 - if tidx == 0:
369 - red_async_release_gpu_add_u32(latent_flags.iterator + 8, Uint32(1))
434 + if cutlass.const_expr(not self.single_token_geometry):
435 + cute.arch.cluster_arrive()
436 + if cluster_rank == 0 and tidx < 32:
437 + cute.arch.cluster_wait()
438 + if tidx == 0:
439 + red_async_release_gpu_add_u32(
440 + latent_flags.iterator + 8, Uint32(1)
441 + )
442
443 global_tid = (
444 Int64(token) * self.cluster_ctas + Int64(cta_y)
@@ -425,9 +497,6 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
497 for element in cutlass.range_constexpr(VEC_BF16):
498 accum[element] = accum[element] + values[element]
499
428 - # Preserve the original early PDL point before RMSNorm.
429 - cute.arch.griddepcontrol_launch_dependents()
430 -
500 if cutlass.const_expr(self.fp32_internal):
501 # High-precision fused mode: retain the rank reduction in
502 # FP32 through the RMS square and row reduction.
@@ -445,32 +514,53 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
514 reduction_profile=0,
515 )
516 smem = cutlass.utils.SmemAllocator()
448 - warp_sums = smem.allocate_tensor(
449 - Float32, cute.make_layout((self.warps,)), byte_alignment=4
450 - )
517 cluster_sums = smem.allocate_tensor(
518 Float32,
453 - cute.make_layout((self.cluster_ctas,)),
454 - byte_alignment=4,
519 + cute.make_layout((2 * self.cluster_ctas * self.warps,)),
520 + byte_alignment=16,
521 )
456 - block_sum = block_sum_specialized(
522 + lane = cute.arch.lane_idx()
523 + warp_idx = cute.arch.warp_idx()
524 + warp_sum = warp_sum_specialized(
525 thread_sum,
458 - warp_sums,
459 - tidx,
526 + warp_idx,
527 + lane,
528 self.warps,
529 self.last_warp_lanes,
530 self.last_warp_mask,
531 )
464 - if tidx < self.cluster_ctas:
465 - local_slot = cluster_sums.iterator + cluster_rank
466 - remote_slot = map_shared_to_peer(local_slot, Int32(tidx))
467 - store_shared_cluster_f32(remote_slot, block_sum)
532 + # Alternate DSM slots until the next cluster synchronization so
533 + # peers may safely begin publishing the following token wave.
534 + parity_offset = parity * Int32(self.cluster_ctas * self.warps)
535 + if lane < self.cluster_ctas:
536 + local_slot = (
537 + cluster_sums.iterator
538 + + parity_offset
539 + + cluster_rank * self.warps
540 + + warp_idx
541 + )
542 + remote_slot = map_shared_to_peer(local_slot, lane)
543 + store_shared_cluster_f32(remote_slot, warp_sum)
544 cute.arch.cluster_arrive()
545 cute.arch.cluster_wait()
546
547 full_sum = Float32(0.0)
548 for peer in cutlass.range_constexpr(self.cluster_ctas):
473 - full_sum = full_sum + cluster_sums[peer]
549 + peer_slot = cluster_sums.iterator + parity_offset + peer * self.warps
550 + if cutlass.const_expr(self.warps == 4):
551 + sum0, sum1, sum2, sum3 = load_shared_f32x4(peer_slot)
552 + full_sum = full_sum + sum0 + sum1 + sum2 + sum3
553 + elif cutlass.const_expr(self.warps == 2):
554 + sum0, sum1 = load_shared_f32x2(peer_slot)
555 + full_sum = full_sum + sum0 + sum1
556 + else:
557 + for peer_warp in cutlass.range_constexpr(self.warps):
558 + full_sum = (
559 + full_sum
560 + + cluster_sums[
561 + parity_offset + peer * self.warps + peer_warp
562 + ]
563 + )
564 inv_rms = cute.math.rsqrt(
565 full_sum / Float32(self.latent_dim) + epsilon, fastmath=True
566 )
@@ -490,9 +580,9 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
580 volatile=False,
581 )
582
493 - # The x=0 CTA rotates only after reaching its final token wave.
494 - # Waiting for all M arrivals then guarantees every token-wave CTA
495 - # loaded the current generation before the metadata is advanced.
583 + # The general schedule waits until every token cluster has loaded
584 + # this generation. The M=1 schedule has only this cluster, whose
585 + # DSM barrier above already covers all routed CTAs.
586 if (
587 token_cta == 0
588 and token + self.token_ctas >= m
@@ -500,9 +590,10 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
590 and tidx == 0
591 ):
592 access_counter = latent_flags.iterator + 8
503 - arrived = load_volatile_u32(access_counter)
504 - while arrived < Uint32(m):
593 + if cutlass.const_expr(not self.single_token_geometry):
594 arrived = load_volatile_u32(access_counter)
595 + while arrived < Uint32(m):
596 + arrived = load_volatile_u32(access_counter)
597 next_index = (current_index + Uint32(1)) % Uint32(NUM_LAMPORT_BUFFERS)
598 actual_bytes = Uint32(m) * Uint32(self.tp_size * self.latent_dim * 2)
599 cute.arch.store((latent_flags.iterator + 0).llvm_ptr, next_index)
@@ -540,111 +631,136 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
631 )
632 dirty_elements = Int64(dirty_index) * (Int64(bytes_per_buffer) // Int64(2))
633
543 - source_element = (
544 - Int64(token) * self.hidden_dim
545 - + Int64(destination) * self.shard_dim
546 - + Int64(tidx) * VEC_BF16
547 - )
548 - source_ptr = cute.make_ptr(
549 - BFloat16,
550 - (shared_source.iterator + source_element).llvm_ptr,
551 - cute.AddressSpace.gmem,
552 - assumed_align=16,
553 - )
554 - local_packed = sanitize_negative_zero(
555 - load_global_u32x4(source_ptr, volatile=False)
556 - )
557 - peer_base = cute.arch.load(
558 - (shared_peer_ptrs.iterator + destination).llvm_ptr,
559 - Int64,
560 - )
561 - destination_element = current_elements + (
562 - (Int64(token) * self.tp_size + self.rank) * self.shard_dim
563 - + Int64(tidx) * VEC_BF16
564 - )
565 - store_global_u32x4(
566 - peer_base + destination_element * 2,
567 - local_packed,
568 - volatile=False,
569 - )
634 + for destination_round in cutlass.range_constexpr(
635 + self.shared_destinations_per_cta
636 + ):
637 + round_destination = destination + Int32(
638 + destination_round * self.shared_destination_stride
639 + )
640 + if round_destination < Int32(self.tp_size):
641 + peer_base = cute.arch.load(
642 + (shared_peer_ptrs.iterator + round_destination).llvm_ptr,
643 + Int64,
644 + )
645 + vector = tidx
646 + while vector < self.shard_vectors:
647 + source_element = (
648 + Int64(token) * self.hidden_dim
649 + + Int64(round_destination) * self.shard_dim
650 + + Int64(vector) * VEC_BF16
651 + )
652 + source_ptr = cute.make_ptr(
653 + BFloat16,
654 + (shared_source.iterator + source_element).llvm_ptr,
655 + cute.AddressSpace.gmem,
656 + assumed_align=16,
657 + )
658 + local_packed = sanitize_negative_zero(
659 + load_global_u32x4(source_ptr, volatile=False)
660 + )
661 + destination_element = current_elements + (
662 + (Int64(token) * self.tp_size + self.rank) * self.shard_dim
663 + + Int64(vector) * VEC_BF16
664 + )
665 + store_global_u32x4(
666 + peer_base + destination_element * 2,
667 + local_packed,
668 + volatile=False,
669 + )
670 + vector = vector + self.threads
671 +
672 + cute.arch.griddepcontrol_launch_dependents()
673
571 - # One arrival per shared destination group and token.
674 + # One arrival per shared cluster and token.
675 cute.arch.cluster_arrive()
676 if cluster_rank == 0 and tidx < 32:
677 cute.arch.cluster_wait()
678 if tidx == 0:
679 red_async_release_gpu_add_u32(shared_flags.iterator + 8, Uint32(1))
680
578 - global_tid = (
579 - Int64(token) * self.tp_size + Int64(destination)
580 - ) * self.threads + Int64(tidx)
681 total_threads = Int64(m) * self.tp_size * self.threads
682 clear_fragments = (Int64(bytes_to_clear) + PACKED_BYTES - 1) // PACKED_BYTES
583 - clear_idx = global_tid
584 - if dirty_num_stages > Uint32(0):
585 - while clear_idx < clear_fragments:
586 - clear_ptr = cute.make_ptr(
587 - BFloat16,
588 - (
589 - shared_workspace.iterator
590 - + dirty_elements
591 - + clear_idx * VEC_BF16
592 - ).llvm_ptr,
593 - cute.AddressSpace.gmem,
594 - assumed_align=16,
595 - )
596 - store_lamport_sentinel_128(clear_ptr)
597 - clear_idx = clear_idx + total_threads
598 -
599 - if destination == self.rank:
600 - rank_words = cute.make_rmem_tensor(
601 - cute.make_layout((self.tp_size, 4), stride=(4, 1)), Uint32
683 + for destination_round in cutlass.range_constexpr(
684 + self.shared_destinations_per_cta
685 + ):
686 + round_destination = destination + Int32(
687 + destination_round * self.shared_destination_stride
688 )
603 - valid = False
604 - while not valid:
605 - valid = True
606 - for source_rank in cutlass.range_constexpr(self.tp_size):
607 - remote_element = current_elements + (
608 - (Int64(token) * self.tp_size + source_rank) * self.shard_dim
609 - + Int64(tidx) * VEC_BF16
610 - )
611 - remote_ptr = cute.make_ptr(
689 + global_tid = (
690 + Int64(token) * self.tp_size + Int64(round_destination)
691 + ) * self.threads + Int64(tidx)
692 + clear_idx = global_tid
693 + if round_destination < Int32(
694 + self.tp_size
695 + ) and dirty_num_stages > Uint32(0):
696 + while clear_idx < clear_fragments:
697 + clear_ptr = cute.make_ptr(
698 BFloat16,
613 - (shared_workspace.iterator + remote_element).llvm_ptr,
699 + (
700 + shared_workspace.iterator
701 + + dirty_elements
702 + + clear_idx * VEC_BF16
703 + ).llvm_ptr,
704 cute.AddressSpace.gmem,
705 assumed_align=16,
706 )
617 - remote = load_global_u32x4(remote_ptr, volatile=True)
618 - for word in cutlass.range_constexpr(4):
619 - rank_words[source_rank, word] = remote[word]
620 - valid = valid & (not fragment_is_dirty(remote))
621 -
622 - accum = cute.make_rmem_tensor(cute.make_layout((VEC_BF16,)), Float32)
623 - for element in cutlass.range_constexpr(VEC_BF16):
624 - accum[element] = Float32(0.0)
625 - for source_rank in cutlass.range_constexpr(self.tp_size):
626 - values = packed_u32x4_to_bf16x8(
627 - rank_words[source_rank, None].load()
628 - ).to(Float32)
629 - for element in cutlass.range_constexpr(VEC_BF16):
630 - accum[element] = accum[element] + values[element]
631 - result = accum.load().to(BFloat16)
632 - output_element = (
633 - Int64(token) * self.hidden_dim
634 - + self.rank * self.shard_dim
635 - + Int64(tidx) * VEC_BF16
636 - )
637 - store_global_u32x4(
638 - Int64((shared_output.iterator + output_element).toint()),
639 - bf16x8_to_packed_u32x4(result),
640 - volatile=False,
641 - )
642 -
643 - if destination == self.rank:
644 - cute.arch.barrier()
645 -
646 - cute.arch.griddepcontrol_launch_dependents()
707 + store_lamport_sentinel_128(clear_ptr)
708 + clear_idx = clear_idx + total_threads
709 +
710 + if round_destination == self.rank:
711 + vector = tidx
712 + while vector < self.shard_vectors:
713 + rank_words = cute.make_rmem_tensor(
714 + cute.make_layout((self.tp_size, 4), stride=(4, 1)),
715 + Uint32,
716 + )
717 + valid = False
718 + while not valid:
719 + valid = True
720 + for source_rank in cutlass.range_constexpr(self.tp_size):
721 + remote_element = current_elements + (
722 + (Int64(token) * self.tp_size + source_rank)
723 + * self.shard_dim
724 + + Int64(vector) * VEC_BF16
725 + )
726 + remote_ptr = cute.make_ptr(
727 + BFloat16,
728 + (
729 + shared_workspace.iterator + remote_element
730 + ).llvm_ptr,
731 + cute.AddressSpace.gmem,
732 + assumed_align=16,
733 + )
734 + remote = load_global_u32x4(remote_ptr, volatile=True)
735 + for word in cutlass.range_constexpr(4):
736 + rank_words[source_rank, word] = remote[word]
737 + valid = valid & (not fragment_is_dirty(remote))
738 +
739 + accum = cute.make_rmem_tensor(
740 + cute.make_layout((VEC_BF16,)), Float32
741 + )
742 + for element in cutlass.range_constexpr(VEC_BF16):
743 + accum[element] = Float32(0.0)
744 + for source_rank in cutlass.range_constexpr(self.tp_size):
745 + values = packed_u32x4_to_bf16x8(
746 + rank_words[source_rank, None].load()
747 + ).to(Float32)
748 + for element in cutlass.range_constexpr(VEC_BF16):
749 + accum[element] = accum[element] + values[element]
750 + result = accum.load().to(BFloat16)
751 + output_element = (
752 + Int64(token) * self.hidden_dim
753 + + self.rank * self.shard_dim
754 + + Int64(vector) * VEC_BF16
755 + )
756 + store_global_u32x4(
757 + Int64((shared_output.iterator + output_element).toint()),
758 + bf16x8_to_packed_u32x4(result),
759 + volatile=False,
760 + )
761 + vector = vector + self.threads
762
763 + cute.arch.barrier()
764 if (
765 token_cta == 0
766 and token + self.token_ctas >= m
@@ -654,7 +770,7 @@ class AllReduceRMSNormWithReduceScatterEarlyExit:
770 ):
771 access_counter = shared_flags.iterator + 8
772 arrived = load_volatile_u32(access_counter)
657 - target = Uint32(m) * Uint32(self.tp_size // self.cluster_ctas)
773 + target = Uint32(m) * Uint32(self.shared_roles)
774 while arrived < target:
775 arrived = load_volatile_u32(access_counter)
776 next_index = (current_index + Uint32(1)) % Uint32(NUM_LAMPORT_BUFFERS)
@@ -948,6 +1064,14 @@ class CollectiveKernel:
1064 self._dummy_expanded_idx = torch.empty(
1065 (max_m, max(top_k, 1)), dtype=torch.int32, device=device
1066 )
1067 + self._seven_cta_max_m = (
1068 + min(max_m, _SEVEN_CTA_MAX_M)
1069 + if (tp_size, latent_dim, hidden_dim) == (8, 3584, 7168)
1070 + and top_k == 16
1071 + and self._dummy_expert_weights.dtype == torch.bfloat16
1072 + and torch.cuda.get_device_capability(device)[0] == 10
1073 + else 0
1074 + )
1075
1076 bytes_per_routed_buffer = max_m * tp_size * latent_dim * 2
1077 routed_bytes = NUM_LAMPORT_BUFFERS * bytes_per_routed_buffer
@@ -1009,6 +1133,39 @@ class CollectiveKernel:
1133
1134 torch.accelerator.synchronize(device)
1135 dist.barrier(group=group, device_ids=[device.index])
1136 + if self._seven_cta_max_m:
1137 + specializations = (
1138 + (1, 1),
1139 + (self._seven_cta_max_m, self._seven_cta_max_m),
1140 + )
1141 + for owner in range(tp_size):
1142 + if rank == owner:
1143 + for compile_max_m, compile_token_ctas in specializations:
1144 + if (compile_max_m, compile_token_ctas) == (
1145 + max_m,
1146 + max_token_ctas,
1147 + ):
1148 + continue
1149 + compile_kernel(
1150 + rank=rank,
1151 + tp_size=tp_size,
1152 + latent_dim=latent_dim,
1153 + hidden_dim=hidden_dim,
1154 + max_m=compile_max_m,
1155 + max_token_ctas=compile_token_ctas,
1156 + latent_output=self._latent_output,
1157 + routed_workspace=self._routed_workspace,
1158 + routed_flags=self._routed_flags,
1159 + routed_multicast_ptr=self._routed_multicast_ptr,
1160 + shared_output=self._shared_output,
1161 + shared_workspace=self._shared_workspace,
1162 + shared_flags=self._shared_flags,
1163 + shared_peer_ptrs=self._shared_peer_ptrs,
1164 + rms_eps=self.rms_eps,
1165 + fp32_internal=fp32_internal,
1166 + top_k=top_k,
1167 + )
1168 + dist.barrier(group=group, device_ids=[device.index])
1169 for owner in range(tp_size):
1170 if rank == owner:
1171 compile_kernel(
@@ -1110,6 +1267,15 @@ class CollectiveKernel:
1267 if not 1 <= m <= self.max_m:
1268 raise ValueError(f"runtime M={m} must be in [1, {self.max_m}]")
1269
1270 + launch_max_m = self.max_m
1271 + launch_token_ctas = self.max_token_ctas
1272 + if self._seven_cta_max_m and m <= self._seven_cta_max_m:
1273 + if m == 1:
1274 + launch_max_m = 1
1275 + launch_token_ctas = 1
1276 + else:
1277 + launch_max_m = self._seven_cta_max_m
1278 + launch_token_ctas = self._seven_cta_max_m
1279 with torch.accelerator.device_index(device.index):
1280 launch(
1281 gemm2_permuted,
@@ -1128,8 +1294,8 @@ class CollectiveKernel:
1294 tp_size=self.tp_size,
1295 latent_dim=self.latent_dim,
1296 hidden_dim=self.hidden_dim,
1131 - max_m=self.max_m,
1132 - max_token_ctas=self.max_token_ctas,
1297 + max_m=launch_max_m,
1298 + max_token_ctas=launch_token_ctas,
1299 fp32_internal=self.fp32_internal,
1300 top_k=self.top_k,
1301 expert_weights=expert_weights,
vllm/models/kimi_k3/nvidia/ops/cute_dsl/latent_moe_tail/fused_add_multicast_skinny_gemm.py
+24 -10
@@ -19,6 +19,7 @@ from .primitives import (
19 bf16x2_to_u32,
20 bf16x4_to_packed_u32x2,
21 bf16x8_to_packed_u32x4,
22 + fma_f32_bf16,
23 sanitize_negative_zero,
24 sanitize_negative_zero_u32,
25 sanitize_negative_zero_u32x2,
@@ -38,6 +39,13 @@ class SkinnyConfig:
39
40
41 def config_for_m(num_rows: int, shard_dim: int = 896) -> SkinnyConfig:
42 + if num_rows <= 5 and shard_dim in (448, 896):
43 + return SkinnyConfig(
44 + block_size=224,
45 + outputs_per_block=2,
46 + k_unroll=1,
47 + vector_width=16,
48 + )
49 if shard_dim == 448:
50 if num_rows >= 6:
51 return SkinnyConfig(
@@ -59,7 +67,7 @@ def config_for_m(num_rows: int, shard_dim: int = 896) -> SkinnyConfig:
67 def _as_cute(tensor: torch.Tensor):
68 return from_dlpack(
69 CUDAGraphCompatibleWrapper(tensor.detach()),
62 - assumed_align=16,
70 + assumed_align=32,
71 )
72
73
@@ -194,9 +202,11 @@ class FusedAddMulticastSkinnyGemm:
202 for vi in cutlass.range_constexpr(vector_width):
203 for mi in cutlass.range_constexpr(num_rows):
204 for ni in cutlass.range_constexpr(outputs_per_block):
197 - acc[mi, ni] = acc[mi, ni] + a_regs[mi, vi].to(Float32) * b_regs[
198 - ni, vi
199 - ].to(Float32)
205 + acc[mi, ni] = fma_f32_bf16(
206 + a_regs[mi, vi],
207 + b_regs[ni, vi],
208 + acc[mi, ni],
209 + )
210
211 for k_tile in cutlass.range(1, num_k_tiles, unroll=self.k_unroll):
212 for mi in cutlass.range_constexpr(num_rows):
@@ -211,9 +221,11 @@ class FusedAddMulticastSkinnyGemm:
221 for vi in cutlass.range_constexpr(vector_width):
222 for mi in cutlass.range_constexpr(num_rows):
223 for ni in cutlass.range_constexpr(outputs_per_block):
214 - acc[mi, ni] = acc[mi, ni] + a_regs[mi, vi].to(Float32) * b_regs[
215 - ni, vi
216 - ].to(Float32)
224 + acc[mi, ni] = fma_f32_bf16(
225 + a_regs[mi, vi],
226 + b_regs[ni, vi],
227 + acc[mi, ni],
228 + )
229
230 for mi in cutlass.range_constexpr(num_rows):
231 for ni in cutlass.range_constexpr(outputs_per_block):
@@ -315,19 +327,19 @@ def compile_kernel(
327 BFloat16,
328 (num_rows, latent_dim),
329 stride=(latent_dim, 1),
318 - assumed_align=16,
330 + assumed_align=32,
331 )
332 b = make_fake_tensor(
333 BFloat16,
334 (shard_dim, latent_dim),
335 stride=(latent_dim, 1),
324 - assumed_align=16,
336 + assumed_align=32,
337 )
338 shared = make_fake_tensor(
339 BFloat16,
340 (num_rows, shard_dim),
341 stride=(hidden_dim, 1),
330 - assumed_align=16,
342 + assumed_align=32,
343 )
344 compiled = cute.compile(
345 FusedAddMulticastSkinnyGemm(
@@ -424,6 +436,8 @@ class FusedAddMulticastSkinnyGemmKernel:
436 or not mailbox.is_contiguous()
437 ):
438 raise ValueError("skinny up-projection inputs have unsupported strides")
439 + if any(tensor.data_ptr() % 32 for tensor in (latent, weight, shared_shard)):
440 + raise ValueError("skinny up-projection inputs must be 32-byte aligned")
441 if mailbox.shape[1] < self.num_rows:
442 raise ValueError("mailbox capacity is smaller than runtime M")
443
vllm/models/kimi_k3/nvidia/ops/cute_dsl/latent_moe_tail/lamport_copy.py
+11 -20
@@ -40,8 +40,13 @@ class LamportCopy:
40 m: cutlass.Int32,
41 stream: cuda.CUstream,
42 ):
43 + fragments = m * cutlass.Int32(self.hidden_dim // VEC_BF16)
44 + grid_ctas = cutlass.min(
45 + cutlass.Int32(self.ctas),
46 + cute.ceil_div(fragments, self.threads),
47 + )
48 self.kernel(symmetric_mailbox, local_output, m).launch(
44 - grid=(self.ctas, 1, 1),
49 + grid=(grid_ctas, 1, 1),
50 block=(self.threads, 1, 1),
51 stream=stream,
52 use_pdl=True,
@@ -54,14 +59,15 @@ class LamportCopy:
59 local_output: cute.Tensor,
60 m: cutlass.Int32,
61 ):
57 - # The CTA may be scheduled early, but mailbox inspection must not pass
58 - # the producer GEMM's programmatic completion point.
59 - cute.arch.griddepcontrol_wait()
62 + # The Lamport marker carries producer readiness, so polling can begin
63 + # before the producer grid reaches ordinary completion.
64 + cute.arch.griddepcontrol_launch_dependents()
65
66 tidx, _, _ = cute.arch.thread_idx()
67 block, _, _ = cute.arch.block_idx()
68 + grid_x, _, _ = cute.arch.grid_dim()
69 thread = cutlass.Int64(block * self.threads + tidx)
64 - stride = cutlass.Int64(self.ctas * self.threads)
70 + stride = cutlass.Int64(grid_x * self.threads)
71 fragments = cutlass.Int64(m) * cutlass.Int64(self.hidden_dim // VEC_BF16)
72
73 fragment = thread
@@ -79,21 +85,6 @@ class LamportCopy:
85
86 destination = cutlass.Int64((local_output.iterator + element).toint())
87 store_global_u32x4(destination, packed, volatile=False)
82 - fragment = fragment + stride
83 -
84 - # The returned ordinary tensor is complete. A same-stream successor
85 - # may overlap the mailbox cleanup below.
86 - cute.arch.griddepcontrol_launch_dependents()
87 -
88 - fragment = thread
89 - while fragment < fragments:
90 - element = fragment * VEC_BF16
91 - source = cute.make_ptr(
92 - cutlass.BFloat16,
93 - (symmetric_mailbox.iterator + element).llvm_ptr,
94 - cute.AddressSpace.gmem,
95 - assumed_align=16,
96 - )
88 store_lamport_sentinel_128(source)
89 fragment = fragment + stride
90
vllm/models/kimi_k3/nvidia/ops/cute_dsl/latent_moe_tail/primitives.py
+183
@@ -59,6 +59,31 @@ def to_cute_dynamic_m(
59 )
60
61
62 +@dsl_user_op
63 +def fma_f32_bf16(
64 + a: BFloat16,
65 + b: BFloat16,
66 + acc: Float32,
67 + *,
68 + loc=None,
69 + ip=None,
70 +) -> Float32:
71 + a_bits = llvm.bitcast(T.i16(), a.ir_value(loc=loc, ip=ip), loc=loc, ip=ip)
72 + b_bits = llvm.bitcast(T.i16(), b.ir_value(loc=loc, ip=ip), loc=loc, ip=ip)
73 + result = llvm.inline_asm(
74 + T.f32(),
75 + [a_bits, b_bits, acc.ir_value(loc=loc, ip=ip)],
76 + "fma.rn.f32.bf16 $0, $1, $2, $3;",
77 + "=f,h,h,f",
78 + has_side_effects=False,
79 + is_align_stack=False,
80 + asm_dialect=llvm.AsmDialect.AD_ATT,
81 + loc=loc,
82 + ip=ip,
83 + )
84 + return Float32(result)
85 +
86 +
87 @dsl_user_op
88 def load_global_u32x4(
89 pointer: cute.Pointer,
@@ -96,6 +121,104 @@ def load_global_u32x4(
121 return cute.TensorSSA(packed, 4, Uint32)
122
123
124 +def _make_top16_bf16_finalize_asm() -> str:
125 + lines = [
126 + "{",
127 + ".reg .pred _valid<16>;",
128 + ".reg .s32 _index<16>;",
129 + ".reg .b32 _weight_words<8>;",
130 + ".reg .b16 _weights<16>;",
131 + ".reg .b32 _data<64>;",
132 + ".reg .b16 _lo, _hi;",
133 + ".reg .f32 _acc<8>;",
134 + ".reg .u64 _address<16>;",
135 + "ld.global.v4.u32 {_index0, _index1, _index2, _index3}, [$5];",
136 + "ld.global.v4.u32 {_index4, _index5, _index6, _index7}, [$5+16];",
137 + "ld.global.v4.u32 {_index8, _index9, _index10, _index11}, [$5+32];",
138 + "ld.global.v4.u32 {_index12, _index13, _index14, _index15}, [$5+48];",
139 + "ld.global.v4.u32 "
140 + "{_weight_words0, _weight_words1, _weight_words2, "
141 + "_weight_words3}, [$6];",
142 + "ld.global.v4.u32 "
143 + "{_weight_words4, _weight_words5, _weight_words6, "
144 + "_weight_words7}, [$6+16];",
145 + ]
146 + for pair in range(8):
147 + lines.append(
148 + f"mov.b32 {{_weights{2 * pair}, _weights{2 * pair + 1}}}, "
149 + f"_weight_words{pair};"
150 + )
151 + for element in range(8):
152 + lines.append(f"mov.f32 _acc{element}, 0f00000000;")
153 + for route in range(16):
154 + data = 4 * route
155 + lines.extend(
156 + [
157 + f"setp.ge.s32 _valid{route}, _index{route}, 0;",
158 + f"@_valid{route} mad.wide.s32 _address{route}, "
159 + f"_index{route}, 7168, $4;",
160 + f"@_valid{route} ld.global.v4.u32 "
161 + f"{{_data{data}, _data{data + 1}, _data{data + 2}, "
162 + f"_data{data + 3}}}, [_address{route}];",
163 + ]
164 + )
165 + for route in range(16):
166 + for pair in range(4):
167 + data = 4 * route + pair
168 + element = 2 * pair
169 + lines.extend(
170 + [
171 + f"@_valid{route} mov.b32 {{_lo, _hi}}, _data{data};",
172 + f"@_valid{route} fma.rn.f32.bf16 _acc{element}, _lo, "
173 + f"_weights{route}, _acc{element};",
174 + f"@_valid{route} fma.rn.f32.bf16 _acc{element + 1}, _hi, "
175 + f"_weights{route}, _acc{element + 1};",
176 + ]
177 + )
178 + for pair in range(4):
179 + lines.append(f"cvt.rn.bf16x2.f32 ${pair}, _acc{2 * pair + 1}, _acc{2 * pair};")
180 + lines.append("}")
181 + return "\n".join(lines)
182 +
183 +
184 +_TOP16_BF16_FINALIZE_ASM = _make_top16_bf16_finalize_asm()
185 +
186 +
187 +@dsl_user_op
188 +def finalize_top16_bf16(
189 + gemm2_vector: cute.Pointer,
190 + route_indices: cute.Pointer,
191 + route_weights: cute.Pointer,
192 + *,
193 + loc=None,
194 + ip=None,
195 +):
196 + """Finalize one Kimi K3 top-16 vector with wide metadata loads."""
197 +
198 + addresses = [
199 + pointer.toint(loc=loc, ip=ip).ir_value(loc=loc, ip=ip)
200 + for pointer in (gemm2_vector, route_indices, route_weights)
201 + ]
202 + out = llvm.inline_asm(
203 + llvm.StructType.get_literal([T.i32()] * 4),
204 + addresses,
205 + _TOP16_BF16_FINALIZE_ASM,
206 + "=r,=r,=r,=r,l,l,l",
207 + has_side_effects=False,
208 + is_align_stack=False,
209 + asm_dialect=llvm.AsmDialect.AD_ATT,
210 + loc=loc,
211 + ip=ip,
212 + )
213 + packed = vector.from_elements(
214 + ir.VectorType.get([4], T.i32(), loc=loc),
215 + [llvm.extractvalue(T.i32(), out, [i], loc=loc, ip=ip) for i in range(4)],
216 + loc=loc,
217 + ip=ip,
218 + )
219 + return cute.TensorSSA(packed, 4, Uint32)
220 +
221 +
222 @dsl_user_op
223 def store_global_u32x4(
224 address: Int64,
@@ -168,6 +291,24 @@ def store_global_u32(
291 )
292
293
294 +@dsl_user_op
295 +def stmc_bf16x8(address: Int64, packed, *, loc=None, ip=None) -> None:
296 + """Publish eight BF16 values through an NVLS multicast mapping."""
297 +
298 + words = [packed[i].ir_value(loc=loc, ip=ip) for i in range(4)]
299 + llvm.inline_asm(
300 + None,
301 + [address.ir_value(loc=loc, ip=ip), *words],
302 + "multimem.st.relaxed.sys.global.v4.bf16x2 [$0], {$1, $2, $3, $4};",
303 + "l,r,r,r,r",
304 + has_side_effects=True,
305 + is_align_stack=False,
306 + asm_dialect=llvm.AsmDialect.AD_ATT,
307 + loc=loc,
308 + ip=ip,
309 + )
310 +
311 +
312 @dsl_user_op
313 def store_lamport_sentinel_128(pointer: cute.Pointer, *, loc=None, ip=None) -> None:
314 """Reset one Lamport fragment to four FP32 negative-zero bit patterns."""
@@ -278,6 +419,48 @@ def store_shared_cluster_f32(
419 )
420
421
422 +@dsl_user_op
423 +def load_shared_f32x2(pointer: cute.Pointer, *, loc=None, ip=None):
424 + """Load two aligned FP32 DSM partials from local shared memory."""
425 +
426 + address = Int32(pointer.toint(loc=loc, ip=ip))
427 + out = llvm.inline_asm(
428 + llvm.StructType.get_literal([T.f32()] * 2),
429 + [address.ir_value(loc=loc, ip=ip)],
430 + "ld.shared.v2.f32 {$0, $1}, [$2];",
431 + "=f,=f,r",
432 + has_side_effects=True,
433 + is_align_stack=False,
434 + asm_dialect=llvm.AsmDialect.AD_ATT,
435 + loc=loc,
436 + ip=ip,
437 + )
438 + return tuple(
439 + Float32(llvm.extractvalue(T.f32(), out, [i], loc=loc, ip=ip)) for i in range(2)
440 + )
441 +
442 +
443 +@dsl_user_op
444 +def load_shared_f32x4(pointer: cute.Pointer, *, loc=None, ip=None):
445 + """Load four aligned FP32 DSM partials from local shared memory."""
446 +
447 + address = Int32(pointer.toint(loc=loc, ip=ip))
448 + out = llvm.inline_asm(
449 + llvm.StructType.get_literal([T.f32()] * 4),
450 + [address.ir_value(loc=loc, ip=ip)],
451 + "ld.shared.v4.f32 {$0, $1, $2, $3}, [$4];",
452 + "=f,=f,=f,=f,r",
453 + has_side_effects=True,
454 + is_align_stack=False,
455 + asm_dialect=llvm.AsmDialect.AD_ATT,
456 + loc=loc,
457 + ip=ip,
458 + )
459 + return tuple(
460 + Float32(llvm.extractvalue(T.f32(), out, [i], loc=loc, ip=ip)) for i in range(4)
461 + )
462 +
463 +
464 @dsl_user_op
465 def packed_u32x4_to_bf16x8(packed, *, loc=None, ip=None):
466 target_type = ir.VectorType.get([VEC_BF16], BFloat16.mlir_type, loc=loc)
vllm/models/kimi_k3/nvidia/ops/latent_moe_tail.py
+1 -1
@@ -22,7 +22,7 @@ _GEMM_CLUSTER_MN = (1, 8)
22 _B_PRIME_STAGES = 2
23 _COLLECTIVE_TOKEN_CTAS = 32
24 _LAMPORT_COPY_CTAS = 32
25 -_LAMPORT_COPY_THREADS = 224
25 +_LAMPORT_COPY_THREADS = 128
26 _SUPPORTED_TP_SIZES = (8, 16)
27
28