@@ -994,6 +994,8 @@ def cp_p2p_fwd_fused_attn(
994994 step ,
995995 cp_size ,
996996 return_max_logit ,
997+ softmax_type ,
998+ softmax_offset ,
997999 q_part ,
9981000 k_part ,
9991001 v_part ,
@@ -1110,6 +1112,8 @@ def cp_p2p_fwd_fused_attn(
11101112 cu_seqlens_kv_padded = cu_seqlens_kv_padded_ ,
11111113 ** fp8_meta_kwargs ,
11121114 return_max_logit = return_max_logit ,
1115+ softmax_type = softmax_type ,
1116+ softmax_offset = softmax_offset ,
11131117 cuda_graph = is_graph_capturing (),
11141118 qkv_scale_inv_format = qkv_scale_inv_format ,
11151119 )
@@ -1118,7 +1122,10 @@ def cp_p2p_fwd_fused_attn(
11181122 softmax_lse_per_step , rng_states = aux_ctx_tensors
11191123 else :
11201124 softmax_lse_per_step , rng_states , * rest = aux_ctx_tensors
1121- attn_bias = rest [0 ] if len (rest ) > 0 else None
1125+ # The aux pack is [stats, rng, (bias), (offset)], so an unconditional
1126+ # rest[0] would return the offset as the bias once a sink is active.
1127+ has_attn_bias = attn_bias_type not in ["no_bias" , "alibi" ] and attn_bias_inputs is not None
1128+ attn_bias = rest [0 ] if has_attn_bias else None
11221129
11231130 if return_max_logit :
11241131 return out_per_step , softmax_lse_per_step , rng_states , attn_bias , * max_logit
@@ -1314,6 +1321,8 @@ def cp_p2p_bwd_fused_attn(
13141321 softmax_lse ,
13151322 softmax_lse_ ,
13161323 rng_states ,
1324+ softmax_type ,
1325+ softmax_offset ,
13171326 attn_dbias ,
13181327 attn_biases ,
13191328 max_seqlen_q ,
@@ -1374,6 +1383,11 @@ def cp_p2p_bwd_fused_attn(
13741383 if attn_dbias is not None :
13751384 aux_tensors += [attn_biases [cp_size - step - 1 ]]
13761385
1386+ # Aux pack order is [softmax_stats, rng_state, (bias), (softmax_offset)],
1387+ # matching what the forward returned.
1388+ if softmax_offset is not None :
1389+ aux_tensors += [softmax_offset ]
1390+
13771391 fp8_meta_kwargs = {}
13781392 qkv_scale_inv_format = None
13791393 do_scale_inv_format = None
@@ -1410,7 +1424,7 @@ def cp_p2p_bwd_fused_attn(
14101424 fp8_meta_kwargs ["dp_quantizer" ] = dP_quantizer_per_step
14111425 fp8_meta_kwargs ["dqkv_quantizer" ] = dQKV_quantizer_per_step
14121426
1413- dq , dk , dv , dbias , * _ = fused_attn_bwd (
1427+ fused_bwd_out = fused_attn_bwd (
14141428 max_seqlen_q_ ,
14151429 max_seqlen_kv_ ,
14161430 cu_seqlens_q_per_step [cp_size - step - 1 ],
@@ -1433,14 +1447,16 @@ def cp_p2p_bwd_fused_attn(
14331447 dqkv_layout = dqkv_layout ,
14341448 attn_mask_type = attn_mask_type_ ,
14351449 attn_bias_type = attn_bias_type ,
1450+ softmax_type = softmax_type ,
14361451 deterministic = deterministic ,
14371452 cuda_graph = is_graph_capturing (),
14381453 qkv_scale_inv_format = qkv_scale_inv_format ,
14391454 do_scale_inv_format = do_scale_inv_format ,
14401455 ** fp8_meta_kwargs ,
14411456 )
14421457
1443- return dq , dk , dv , dbias
1458+ dq , dk , dv , dbias , d_softmax_offset = fused_bwd_out
1459+ return dq , dk , dv , dbias , d_softmax_offset
14441460
14451461
14461462def cp_p2p_bwd_flash_attn (
@@ -1618,6 +1634,7 @@ def forward(
16181634 use_fused_attention ,
16191635 return_max_logit ,
16201636 softcap ,
1637+ softmax_offset ,
16211638 fp8 ,
16221639 fp8_meta ,
16231640 cp_group ,
@@ -1629,6 +1646,7 @@ def forward(
16291646 use_flash_attn_4 ,
16301647 fp8_output ,
16311648 layer_number ,
1649+ softmax_type ,
16321650 ):
16331651 # pylint: disable=missing-function-docstring
16341652
@@ -2007,6 +2025,8 @@ def forward(
20072025 i ,
20082026 cp_size ,
20092027 return_max_logit ,
2028+ softmax_type ,
2029+ softmax_offset ,
20102030 ]
20112031 else :
20122032 flash_attn_inputs = [
@@ -2396,6 +2416,8 @@ def forward(
23962416 ctx .max_seqlen_q = max_seqlen_q
23972417 ctx .max_seqlen_kv = max_seqlen_kv
23982418 ctx .softmax_scale = softmax_scale
2419+ ctx .softmax_offset = softmax_offset
2420+ ctx .softmax_type = softmax_type
23992421 ctx .attn_mask_type = attn_mask_type
24002422 ctx .attn_bias_type = attn_bias_type
24012423 ctx .attn_bias_shape = None if attn_bias is None else attn_bias .shape
@@ -2520,6 +2542,12 @@ def backward(ctx, dout, *_args):
25202542 attn_dbias = None
25212543 attn_dbias_ = None
25222544
2545+ # Sink gradient accumulator. The offset is replicated on every rank and every
2546+ # per-tile fused call sees the same tensor, so tile contributions sum.
2547+ d_softmax_offset = (
2548+ torch .zeros_like (ctx .softmax_offset ) if ctx .softmax_offset is not None else None
2549+ )
2550+
25232551 # set up softmax_lse
25242552 softmax_lse_ = None
25252553 if causal and ctx .second_half_lse_seqlen is not None :
@@ -2779,6 +2807,8 @@ def backward(ctx, dout, *_args):
27792807 softmax_lse ,
27802808 softmax_lse_ ,
27812809 rng_states ,
2810+ ctx .softmax_type ,
2811+ ctx .softmax_offset ,
27822812 attn_dbias ,
27832813 attn_biases ,
27842814 ctx .max_seqlen_q ,
@@ -2831,12 +2861,13 @@ def backward(ctx, dout, *_args):
28312861 # Reverse the steps in forward. In the cp_size x cp_size (i.e. GPU x step) matrix,
28322862 # there are still three sections in these tiles based on their attention pattern
28332863 # for attn_mask_type = causal, and one for attn_mask_type != causal.
2864+ d_sink_ = None
28342865 if causal :
28352866 if i == (cp_size - 1 ):
28362867 section = "diagonal"
28372868 prepare_outputs = cp_p2p_bwd_prepare_qkv (* prepare_inputs , section )
28382869 if ctx .use_fused_attention :
2839- dq_ , dk_ , dv_ , dbias_ = cp_p2p_bwd_fused_attn (
2870+ dq_ , dk_ , dv_ , dbias_ , d_sink_ = cp_p2p_bwd_fused_attn (
28402871 * fused_attn_inputs , * prepare_outputs , section
28412872 )
28422873 else :
@@ -2849,7 +2880,7 @@ def backward(ctx, dout, *_args):
28492880 section = "lower-triangle"
28502881 prepare_outputs = cp_p2p_bwd_prepare_qkv (* prepare_inputs , section )
28512882 if ctx .use_fused_attention :
2852- dq_ , dk_ , dv_ , dbias_ = cp_p2p_bwd_fused_attn (
2883+ dq_ , dk_ , dv_ , dbias_ , d_sink_ = cp_p2p_bwd_fused_attn (
28532884 * fused_attn_inputs , * prepare_outputs , section
28542885 )
28552886 else :
@@ -2862,7 +2893,7 @@ def backward(ctx, dout, *_args):
28622893 section = "upper-triangle"
28632894 prepare_outputs = cp_p2p_bwd_prepare_qkv (* prepare_inputs , section )
28642895 if ctx .use_fused_attention :
2865- dq_ , dk_ , dv_ , dbias_ = cp_p2p_bwd_fused_attn (
2896+ dq_ , dk_ , dv_ , dbias_ , d_sink_ = cp_p2p_bwd_fused_attn (
28662897 * fused_attn_inputs , * prepare_outputs , section
28672898 )
28682899 else :
@@ -2875,7 +2906,7 @@ def backward(ctx, dout, *_args):
28752906 section = "all"
28762907 prepare_outputs = cp_p2p_bwd_prepare_qkv (* prepare_inputs , section )
28772908 if ctx .use_fused_attention :
2878- dq_ , dk_ , dv_ , dbias_ = cp_p2p_bwd_fused_attn (
2909+ dq_ , dk_ , dv_ , dbias_ , d_sink_ = cp_p2p_bwd_fused_attn (
28792910 * fused_attn_inputs , * prepare_outputs , section
28802911 )
28812912 else :
@@ -2885,6 +2916,13 @@ def backward(ctx, dout, *_args):
28852916 section ,
28862917 )
28872918
2919+ # Accumulate this tile's sink gradient. Every rank computes all query rows,
2920+ # so each already holds the full gradient for this replicated parameter and
2921+ # the outer framework's DDP average reproduces it; reducing here as well
2922+ # would double-count.
2923+ if d_sink_ is not None :
2924+ d_softmax_offset += d_sink_
2925+
28882926 # dq, dk, dv are reduced across steps in higher precision
28892927 # DelayedScaling: collect all results in uint8 to one tensor, dequantize to float32, then reduce
28902928 # CurrentScaling: dequantize partial results from each step to float32, then reduce
@@ -3204,6 +3242,8 @@ def backward(ctx, dout, *_args):
32043242 None ,
32053243 None ,
32063244 None ,
3245+ d_softmax_offset ,
3246+ None ,
32073247 None ,
32083248 None ,
32093249 None ,
@@ -5715,9 +5755,10 @@ def attn_forward_func_with_cp(
57155755 assert (
57165756 softmax_type == "vanilla" or use_fused_attention
57175757 ), f"Context parallelism only supports { softmax_type = } with FusedAttention backend!"
5718- assert (
5719- softmax_type == "vanilla" or cp_comm_type == "a2a"
5720- ), f"Context parallelism only supports { softmax_type = } with cp_comm_type = 'a2a'!"
5758+ assert softmax_type == "vanilla" or cp_comm_type in (
5759+ "a2a" ,
5760+ "p2p" ,
5761+ ), f"Context parallelism supports { softmax_type = } with cp_comm_type in ('a2a', 'p2p')!"
57215762 if get_cudnn_version () < (9 , 18 , 0 ):
57225763 assert softmax_type == "vanilla" or qkv_format != "thd" , (
57235764 f"Before cuDNN 9.18.0, context parallelism does not support { softmax_type = } with"
@@ -5749,6 +5790,7 @@ def attn_forward_func_with_cp(
57495790
57505791 if cp_comm_type in ["p2p" , "a2a+p2p" ]:
57515792 args += [
5793+ softmax_offset ,
57525794 fp8 ,
57535795 fp8_meta ,
57545796 cp_group ,
@@ -5760,6 +5802,7 @@ def attn_forward_func_with_cp(
57605802 use_flash_attn_4 ,
57615803 fp8_output ,
57625804 layer_number ,
5805+ softmax_type ,
57635806 ]
57645807 out = AttnFuncWithCPAndKVP2P .apply (* args )
57655808 elif cp_comm_type == "all_gather" :
0 commit comments