3131def fmha_kernel (Q , K , V , Out ,
3232 qk_scale : float ,
3333 input_pos : int ,
34- TILE_D : ConstInt , # TILE_D = hidden_size
34+ Dqk : ConstInt , # Head dimension of Q and K
35+ Dv : ConstInt , # Head dimension of V
3536 H : ConstInt ,
3637 TILE_M : ConstInt ,
3738 TILE_N : ConstInt ,
@@ -64,12 +65,12 @@ def fmha_kernel(Q, K, V, Out,
6465 # Initialize online softmax accumulators in float32 for stability
6566 m_i = ct .full ((TILE_M , 1 ), - np .inf , dtype = np .float32 )
6667 l_i = ct .full ((TILE_M , 1 ), 0.0 , dtype = np .float32 )
67- acc = ct .full ((TILE_M , TILE_D ), 0.0 , dtype = np .float32 )
68+ acc = ct .full ((TILE_M , Dv ), 0.0 , dtype = np .float32 )
6869
6970 # Load query tile for this batch, head, and M-chunk
7071 q = ct .load (
71- Q , index = (batch_idx , head_idx , bid_x , 0 ), shape = (1 , 1 , TILE_M , TILE_D )
72- ).reshape ((TILE_M , TILE_D )) # [TILE_M, TILE_D ]
72+ Q , index = (batch_idx , head_idx , bid_x , 0 ), shape = (1 , 1 , TILE_M , Dqk )
73+ ).reshape ((TILE_M , Dqk )) # [TILE_M, Dqk ]
7374
7475 # loop over k, v and update accumulator
7576 m_end = input_pos + (bid_x + 1 ) * TILE_M
@@ -88,11 +89,11 @@ def fmha_kernel(Q, K, V, Out,
8889 for j in range (0 , Tc ):
8990 # --- Compute QK product ---
9091 k = ct .load (
91- K , index = (batch_idx , off_kv_h , 0 , j ), shape = (1 , 1 , TILE_D , TILE_N ),
92+ K , index = (batch_idx , off_kv_h , 0 , j ), shape = (1 , 1 , Dqk , TILE_N ),
9293 order = (0 , 1 , 3 , 2 ),
9394 latency = 2 ,
9495 )
95- k = k .reshape ((TILE_D , TILE_N )) # [TILE_D , TILE_N]
96+ k = k .reshape ((Dqk , TILE_N )) # [Dqk , TILE_N]
9697 qk = ct .full ((TILE_M , TILE_N ), 0. , dtype = np .float32 )
9798 qk = ct .mma (q , k , qk ) # [TILE_M, TILE_N]
9899
@@ -125,16 +126,16 @@ def fmha_kernel(Q, K, V, Out,
125126
126127 # --- Compute PV product ---
127128 v = ct .load (
128- V , index = (batch_idx , off_kv_h , j , 0 ), shape = (1 , 1 , TILE_N , TILE_D ),
129+ V , index = (batch_idx , off_kv_h , j , 0 ), shape = (1 , 1 , TILE_N , Dv ),
129130 latency = 4 ,
130- ).reshape ((TILE_N , TILE_D )) # [TILE_N, TILE_D ]
131+ ).reshape ((TILE_N , Dv )) # [TILE_N, Dv ]
131132 p = p .astype (Q .dtype )
132133 acc = ct .mma (p , v , acc ) # [TILE_M, TILE_N]
133134 m_i = m_ij # [TILE_M, 1]
134135
135136 # --- Final Normalization and Store ---
136137 acc = ct .truediv (acc , l_i , flush_to_zero = True , rounding_mode = RMd .APPROX )
137- acc = acc .reshape ((1 , 1 , TILE_M , TILE_D )).astype (Out .dtype )
138+ acc = acc .reshape ((1 , 1 , TILE_M , Dv )).astype (Out .dtype )
138139 ct .store (Out , index = (batch_idx , head_idx , bid_x , 0 ), tile = acc )
139140
140141
@@ -202,6 +203,7 @@ def cutile_fmha(Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor,
202203 qk_scale ,
203204 input_pos ,
204205 D_k ,
206+ D_v ,
205207 Heads ,
206208 tile_m ,
207209 tile_n ,
@@ -273,12 +275,18 @@ def cutile_autotune_fmha(Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor,
273275
274276
275277def torch_fmha (Q : torch .Tensor , K : torch .Tensor , V : torch .Tensor ,
276- is_causal : bool , enable_gqa : bool ) -> torch .Tensor :
277- backend = SDPBackend .CUDNN_ATTENTION \
278- if (Q .shape [2 ] == K .shape [2 ]) \
279- else SDPBackend .FLASH_ATTENTION
280- with sdpa_kernel (backend ):
281- ret = scaled_dot_product_attention (Q , K , V ,
278+ is_causal : bool , enable_gqa : bool ,
279+ use_backend_selection_rule : bool = False ) -> torch .Tensor :
280+ if use_backend_selection_rule :
281+ backend = SDPBackend .CUDNN_ATTENTION \
282+ if (Q .shape [2 ] == K .shape [2 ]) \
283+ else SDPBackend .FLASH_ATTENTION
284+ with sdpa_kernel (backend ):
285+ ret = scaled_dot_product_attention (Q , K , V ,
286+ is_causal = is_causal ,
287+ enable_gqa = enable_gqa )
288+ else :
289+ ret = scaled_dot_product_attention (Q , K , V ,
282290 is_causal = is_causal ,
283291 enable_gqa = enable_gqa )
284292 return ret
@@ -296,13 +304,14 @@ def torch_fmha(Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor,
296304
297305 # --- User Configuration ---
298306 BATCH_SIZE = 2
299- NUM_HEADS = 8
307+ NUM_HEADS = 32
300308 SEQ_LEN_Q = 128
301- SEQ_LEN_KV = 128
302- D_K = 64
309+ SEQ_LEN_KV = 256
310+ D_K = 128
303311 D_V = 64
304312
305- QUERY_GROUP_SIZE = 1
313+ QUERY_GROUP_SIZE = 8
314+ enable_gqa = QUERY_GROUP_SIZE > 1
306315
307316 DTYPE = torch .float16
308317
@@ -336,7 +345,7 @@ def torch_fmha(Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor,
336345 dtype:{ output_fmha_cutile_non_causal .dtype } """ )
337346 if args .correctness_check :
338347 ref_fmha = torch_fmha (Q_input , K_input , V_input ,
339- is_causal = False , enable_gqa = False )
348+ is_causal = False , enable_gqa = enable_gqa )
340349 torch .testing .assert_close (output_fmha_cutile_non_causal , ref_fmha , atol = 1e-3 , rtol = 1e-3 )
341350 print ("Correctness check passed" )
342351 else :
@@ -354,7 +363,7 @@ def torch_fmha(Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor,
354363 dtype: { output_fmha_cutile_causal .dtype } """ )
355364 if args .correctness_check :
356365 ref_fmha = torch_fmha (Q_input , K_input , V_input ,
357- is_causal = True , enable_gqa = False )
366+ is_causal = True , enable_gqa = enable_gqa )
358367 torch .testing .assert_close (output_fmha_cutile_causal , ref_fmha , atol = 1e-3 , rtol = 1e-3 )
359368 print ("Correctness check passed" )
360369 else :
@@ -394,7 +403,7 @@ def torch_fmha(Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor,
394403 dtype: { output_fmha_cutile_autotune_causal .dtype } """ )
395404 print (f"Tuned config: { tuned_config } " )
396405 if args .correctness_check :
397- ref_fmha = torch_fmha (Q_input , K_input , V_input , is_causal = True , enable_gqa = False )
406+ ref_fmha = torch_fmha (Q_input , K_input , V_input , is_causal = True , enable_gqa = enable_gqa )
398407 torch .testing .assert_close (
399408 output_fmha_cutile_autotune_causal , ref_fmha , atol = 1e-2 , rtol = 5e-2
400409 )
0 commit comments