@@ -824,6 +824,7 @@ def _ulysses_ring_attention(
824824 attention_mask : jax .Array = None ,
825825 use_base2_exp : bool = True ,
826826 use_experimental_scheduler : bool = False ,
827+ bidirectional : bool = False ,
827828) -> jax .Array :
828829 """Hybrid Ulysses + Ring (USP) with the CUSTOM splash kernel on main's mesh.
829830
@@ -894,20 +895,39 @@ def wrap_ulysses_ring_attention(query, key, value):
894895 value , _ , _ = _pad_data_for_flash (value , heads , bkv )
895896
896897 bsizes = custom_splash ._BlockSizes (block_q = bq , block_kv = bkv , block_kv_compute = bkv_compute )
897- # (2) Ring (full ppermute over the cross-chip ring axis) with the custom kernel.
898- ring_kernel = tokamax_ring_attention_kernel .make_custom_ring_attention (
899- block_sizes = bsizes ,
900- bkv_compute_in = bkv_compute_in ,
901- orig_q_seq_len = query_seq_len ,
902- orig_kv_seq_len = key_seq_len ,
903- use_base2_exp = use_base2_exp ,
904- use_experimental_scheduler = use_experimental_scheduler ,
905- vmem_limit_bytes = vmem_limit_bytes ,
906- ring_axis = ring_axis ,
907- ring_size = num_ring_shards ,
908- )
909- vmapped_ring = jax .vmap (ring_kernel , in_axes = (0 , 0 , 0 ))
910- attention_output = vmapped_ring (query , key , value )
898+ if num_ring_shards == 1 :
899+ # (2a) R=1: the ring is trivial (no rotation) -> use the lighter dedicated
900+ # splash kernel (fuse_reciprocal, no fp32 online-softmax residual windows).
901+ # Same math as the 1-step ring, and it fits BQ=8448 where the ring kernel
902+ # OOMs (its 3x residual windows). make_splash_mha returns [H, D, S].
903+ splash_kernel = custom_splash .make_splash_mha (
904+ block_sizes = bsizes ,
905+ bkv_compute_in = bkv_compute_in ,
906+ orig_q_seq_len = query_seq_len ,
907+ orig_kv_seq_len = key_seq_len ,
908+ heads_per_tile = heads_per_tile ,
909+ use_base2_exp = use_base2_exp ,
910+ use_experimental_scheduler = use_experimental_scheduler ,
911+ vmem_limit_bytes = vmem_limit_bytes ,
912+ )
913+ attention_output = jnp .swapaxes (jax .vmap (splash_kernel , in_axes = (0 , 0 , 0 ))(query , key , value ), 2 , 3 )
914+ else :
915+ # (2b) Ring (full ppermute over the cross-chip ring axis) with the custom kernel.
916+ # bidirectional=True -> wrap-free schedule (streams K/V both directions one hop
917+ # at a time), for a non-wrapping ring axis. Selected by attention=ulysses_ring_custom_bidir.
918+ ring_kernel = tokamax_ring_attention_kernel .make_custom_ring_attention (
919+ block_sizes = bsizes ,
920+ bkv_compute_in = bkv_compute_in ,
921+ orig_q_seq_len = query_seq_len ,
922+ orig_kv_seq_len = key_seq_len ,
923+ use_base2_exp = use_base2_exp ,
924+ use_experimental_scheduler = use_experimental_scheduler ,
925+ vmem_limit_bytes = vmem_limit_bytes ,
926+ ring_axis = ring_axis ,
927+ ring_size = num_ring_shards ,
928+ bidirectional = bidirectional ,
929+ )
930+ attention_output = jax .vmap (ring_kernel , in_axes = (0 , 0 , 0 ))(query , key , value )
911931 attention_output = attention_output [:, :, :query_seq_len , :kv_size ].astype (query .dtype )
912932
913933 # (3) Ulysses all-to-all back: sequence -> heads, restoring the layout.
@@ -1087,6 +1107,30 @@ def ulysses_ring_custom_kernel(q, k, v, context):
10871107 )
10881108
10891109
1110+ @register_kernel ("ulysses_ring_custom_bidir" )
1111+ def ulysses_ring_custom_bidir_kernel (q , k , v , context ):
1112+ """Wrap-free (bidirectional) variant of ulysses_ring_custom: the ring streams
1113+ K/V both directions one hop at a time, avoiding the diameter-length wrap hop
1114+ on a non-wrapping ring axis. Same USP split as ulysses_ring_custom otherwise."""
1115+ return _ulysses_ring_attention (
1116+ q ,
1117+ k * context ["scale" ],
1118+ v ,
1119+ context ["heads" ],
1120+ context ["mesh" ],
1121+ context ["axis_names_q" ],
1122+ context ["axis_names_kv" ],
1123+ context ["flash_block_sizes" ],
1124+ context ["dtype" ],
1125+ mask_padding_tokens = context ["mask_padding_tokens" ],
1126+ residual_checkpoint_name = context ["residual_checkpoint_name" ],
1127+ attention_mask = context ["attention_mask" ],
1128+ use_base2_exp = context .get ("use_base2_exp" , True ),
1129+ use_experimental_scheduler = context .get ("use_experimental_scheduler" , False ),
1130+ bidirectional = True ,
1131+ )
1132+
1133+
10901134@register_kernel ("ulysses" )
10911135def ulysses_kernel (q , k , v , context ):
10921136 return _ulysses_attention (
0 commit comments