@@ -209,6 +209,13 @@ def __init__(
209209 # despite the literal `is_sm103` name.
210210 is_sm103 = self .arch .is_family_of (Arch .sm_103f )
211211 self .is_sm103 = is_sm103
212+ # SM103 ld.red is profitable except for D32 when scores are unmodified.
213+ self .use_ldred_rowmax = (
214+ is_sm103
215+ and self .score_mod is None
216+ and self .mask_mod is None
217+ and self .head_dim_padded != 32
218+ )
212219 # enable_ex2_emu is derived: True if tuning config has freq > 0, else fallback to default logic
213220 _default_enable_ex2_emu = (self .head_dim_padded <= 128 or (self .head_dim_padded == 192 and self .use_2cta_instrs and not self .is_causal and not self .is_local )) and not is_sm103
214221 self .enable_ex2_emu = _default_enable_ex2_emu
@@ -731,6 +738,9 @@ class SharedStorage:
731738 head_divmod = FastDivmodDivisor (self .qhead_per_kvhead )
732739
733740 self .use_block_sparsity = cutlass .const_expr (blocksparse_tensors is not None )
741+ self .use_ldred_rowmax = cutlass .const_expr (
742+ self .use_ldred_rowmax and not self .use_block_sparsity
743+ )
734744 if cutlass .const_expr (self .use_block_sparsity and mPageTable is not None ):
735745 raise NotImplementedError ("Block sparsity + paged KV not supported on SM100" )
736746 if cutlass .const_expr (self .use_block_sparsity and self .is_varlen_q ):
@@ -1920,9 +1930,12 @@ def softmax_loop(
19201930 )
19211931 tStP = cute .make_tensor (tSAcc .iterator + self .tmem_s_to_p_offset , tStP_layout )
19221932
1923- tmem_load_atom = cute .make_copy_atom (
1924- tcgen05 .copy .Ld32x32bOp (tcgen05 .copy .Repetition (32 )), self .qk_acc_dtype
1933+ tmem_load_op = (
1934+ tcgen05 .copy .LdRed32x32bOp (tcgen05 .copy .Repetition (32 ))
1935+ if const_expr (self .use_ldred_rowmax )
1936+ else tcgen05 .copy .Ld32x32bOp (tcgen05 .copy .Repetition (32 ))
19251937 )
1938+ tmem_load_atom = cute .make_copy_atom (tmem_load_op , self .qk_acc_dtype )
19261939 thr_tmem_load = tcgen05 .make_tmem_copy (tmem_load_atom , tSAcc ).get_slice (tidx )
19271940 tStS_t2r = thr_tmem_load .partition_S (tSAcc ) # (((32,32),1),1,4)
19281941
@@ -2271,6 +2284,8 @@ def softmax_step(
22712284 4. Transforming scores using exp2(x*scale - max*scale)
22722285 5. Computing row sums for normalization
22732286 6. Coordinating pipeline synchronization between different processing stages
2287+
2288+ A None mask_fn means the tcgen05.ld.red hardware max is valid.
22742289 """
22752290 warp_idx = cute .arch .make_warp_uniform (cute .arch .warp_idx ()) % 4
22762291 tilePlikeFP32 = self .mma_tiler_qk [1 ] // Float32 .width * self .v_dtype .width
@@ -2284,7 +2299,15 @@ def softmax_step(
22842299 # Wait for Si
22852300 pipeline_s_p_o .consumer_wait_w_index_phase (stage , mma_si_consumer_phase )
22862301 tSrS_t2r = cute .make_rmem_tensor (thr_tmem_load .partition_D (tScS ).shape , self .qk_acc_dtype )
2287- cute .copy (thr_tmem_load , tStS_t2r , tSrS_t2r )
2302+ hw_row_max = Float32 (- Float32 .inf )
2303+ if const_expr (self .use_ldred_rowmax ):
2304+ # ld.red returns each x32 tile's max in an extra register.
2305+ tSrS_red = cute .make_rmem_tensor (((1 , 1 ), * tSrS_t2r .shape [1 :]), self .qk_acc_dtype )
2306+ cute .copy (thr_tmem_load , tStS_t2r , (tSrS_t2r , tSrS_red ))
2307+ for i in cutlass .range_constexpr (cute .size (tSrS_red .shape )):
2308+ hw_row_max = cute .arch .fmax (hw_row_max , tSrS_red [i ])
2309+ else :
2310+ cute .copy (thr_tmem_load , tStS_t2r , tSrS_t2r )
22882311 # tSrS_t2r = copy_utils.load_t2r(thr_tmem_load, tScS_shape, tStS_t2r)
22892312 if cutlass .const_expr (self .score_mod is not None ):
22902313 self .apply_score_mod (
@@ -2304,7 +2327,11 @@ def softmax_step(
23042327
23052328 if const_expr (mask_fn is not None ):
23062329 mask_fn (tSrS_t2r , n_block = n_block )
2307- row_max , acc_scale = softmax .update_row_max (tSrS_t2r .load (), is_first )
2330+ # Masked iterations reduce over post-mask values in software.
2331+ if const_expr (self .use_ldred_rowmax and mask_fn is None ):
2332+ row_max , acc_scale = softmax .update_row_max_precomputed (hw_row_max , is_first )
2333+ else :
2334+ row_max , acc_scale = softmax .update_row_max (tSrS_t2r .load (), is_first )
23082335
23092336 if const_expr (not is_first ):
23102337 # tSrScale_r2t = cute.make_rmem_tensor(thr_tmem_store_scale.partition_S(tScScale).shape, Float32)
0 commit comments