@@ -354,6 +354,125 @@ def multimem_reduce_scatter_v(
354354 return output_tensor
355355
356356
357+ @triton .jit
358+ def _ordered_reduce_scatter_v_kernel (
359+ local_ptr ,
360+ buffer_ptrs_dev ,
361+ signal_pad_ptrs ,
362+ local_tokens ,
363+ rank_token_offset_ptr ,
364+ ep_max_tokens_ptr ,
365+ input_byte_offset ,
366+ HIDDEN_SIZE : tl .constexpr ,
367+ BLOCK_SIZE : tl .constexpr ,
368+ RANK : tl .constexpr ,
369+ WORLD_SIZE : tl .constexpr ,
370+ ):
371+ """Variable-count reduce-scatter with explicit rank-order fp32 addition.
372+
373+ This is intentionally not a multimem.ld_reduce kernel. Each rank reads the
374+ same token row from every peer symmetric buffer in rank order and accumulates
375+ in fp32, which gives batch-invariant MoE a defined cross-rank reduction tree.
376+ """
377+ pid = tl .program_id (axis = 0 )
378+
379+ ep_max_tokens = tl .load (ep_max_tokens_ptr )
380+ if pid >= ep_max_tokens :
381+ return
382+
383+ symm_mem_sync (
384+ signal_pad_ptrs ,
385+ None ,
386+ RANK ,
387+ WORLD_SIZE ,
388+ hasPreviousMemAccess = False ,
389+ hasSubsequentMemAccess = True ,
390+ )
391+ sync_threads ()
392+
393+ tid = tl .arange (0 , BLOCK_SIZE )
394+ rank_token_offset = tl .load (rank_token_offset_ptr )
395+ buffer_ptrs = buffer_ptrs_dev .to (tl .pointer_type (tl .uint64 ))
396+
397+ for token_offset in range (pid , local_tokens , tl .num_programs (axis = 0 )):
398+ global_token = rank_token_offset + token_offset
399+
400+ for channel_offset in range (0 , HIDDEN_SIZE , BLOCK_SIZE ):
401+ offsets = channel_offset + tid
402+ mask = offsets < HIDDEN_SIZE
403+ acc = tl .zeros ([BLOCK_SIZE ], dtype = tl .float32 )
404+
405+ for src_rank in tl .range (0 , WORLD_SIZE ):
406+ peer_base = tl .load (buffer_ptrs + src_rank ).to (tl .pointer_type (tl .uint8 ))
407+ peer_ptr = (peer_base + input_byte_offset ).to (tl .pointer_type (tl .float32 ))
408+ vals = tl .load (peer_ptr + global_token * HIDDEN_SIZE + offsets , mask = mask , other = 0.0 )
409+ acc += vals
410+
411+ tl .store (local_ptr + token_offset * HIDDEN_SIZE + offsets , acc , mask = mask )
412+
413+
414+ def ordered_reduce_scatter_v (
415+ output_tensor : torch .Tensor ,
416+ input_tensor : torch .Tensor ,
417+ symm_mem_hdl : _SymmetricMemory ,
418+ rank_token_offset : torch .Tensor ,
419+ ep_max_tokens : torch .Tensor ,
420+ per_rank_max_tokens : int ,
421+ input_byte_offset : int = 0 ,
422+ ** kwargs ,
423+ ) -> torch .Tensor :
424+ """Variable-count reduce-scatter with fixed rank-order fp32 accumulation.
425+
426+ This is the batch-invariant alternative to multimem_reduce_scatter_v. It
427+ uses symmetric memory for peer visibility, but performs no hardware FP
428+ reduction; each output token is accumulated by explicitly loading peers in
429+ rank order.
430+ """
431+ assert HAVE_TRITON , "Triton is required for ordered_reduce_scatter_v."
432+ assert (
433+ output_tensor .ndim == 2 and input_tensor .ndim == 2
434+ ), "output_tensor and input_tensor must be 2-D [tokens, hidden_size]."
435+ assert is_device_nvls_capable (
436+ output_tensor .device
437+ ), "ordered_reduce_scatter_v requires a Hopper+ GPU with NVLink (SM >= 9)."
438+ assert (
439+ output_tensor .dtype == input_tensor .dtype == torch .float32
440+ ), "ordered_reduce_scatter_v requires fp32 input and output tensors."
441+ assert (
442+ rank_token_offset .numel () == 1
443+ and rank_token_offset .dtype == torch .int32
444+ and rank_token_offset .is_cuda
445+ ), "rank_token_offset must be a scalar int32 CUDA tensor."
446+
447+ hidden_size = output_tensor .shape [1 ]
448+ assert (
449+ input_tensor .shape [1 ] == hidden_size
450+ ), f"input and output hidden_size mismatch: { input_tensor .shape [1 ]} vs { hidden_size } "
451+
452+ MAX_NUM_BLOCKS = kwargs .get ("max_num_blocks" , 128 )
453+ MAX_BLOCK_SIZE = 1024
454+ WARP_SIZE = 32
455+ block_size = min (triton .next_power_of_2 (hidden_size ), MAX_BLOCK_SIZE )
456+ num_warps = max (1 , block_size // WARP_SIZE )
457+ num_blocks = min (per_rank_max_tokens , MAX_NUM_BLOCKS )
458+
459+ _ordered_reduce_scatter_v_kernel [(num_blocks , 1 , 1 )](
460+ output_tensor ,
461+ symm_mem_hdl .buffer_ptrs_dev ,
462+ symm_mem_hdl .signal_pad_ptrs_dev ,
463+ local_tokens = output_tensor .shape [0 ],
464+ rank_token_offset_ptr = rank_token_offset ,
465+ ep_max_tokens_ptr = ep_max_tokens ,
466+ input_byte_offset = input_byte_offset ,
467+ HIDDEN_SIZE = hidden_size ,
468+ BLOCK_SIZE = block_size ,
469+ RANK = symm_mem_hdl .rank ,
470+ WORLD_SIZE = symm_mem_hdl .world_size ,
471+ num_warps = num_warps ,
472+ )
473+ return output_tensor
474+
475+
357476@triton .jit
358477def _multimem_all_gatherv_3tensor_kernel (
359478 local_ptr_0 ,
0 commit comments