1717"""Custom Pallas flash attention kernel for TPU."""
1818
1919import functools
20- import math
2120
2221import jax
2322import jax .numpy as jnp
2423import numpy as np
2524from jax import lax
2625from jax .experimental import pallas as pl
2726from jax .experimental .pallas import tpu as pltpu
28- from jax .experimental .shard_map import shard_map
29- from jax .sharding import PartitionSpec as P
3027
3128DEFAULT_MASK_VALUE = - 0.7 * float (np .finfo (np .dtype ("float32" )).max )
3229NUM_LANES = 128
3330NUM_SUBLANES = 8
3431NT_DIM_NUMBERS = (((1 ,), (1 ,)), ((), ()))
3532
36- # Default block sizes (tuned for 720p Wan2.1 on v6e/v7x)
37- DEFAULT_BQSIZE = 3328
38- DEFAULT_BKVSIZE = 2816
39- # Cranked up to 1024 for massive MXU throughput
40- DEFAULT_BKVCOMPUTESIZE = 1024
41- # Kept at 256 to protect VPU registers (V1 Optimization)
42- DEFAULT_BKVCOMPUTEINSIZE = 256
43-
4433
4534class _BlockSizes :
46- __slots__ = ("block_q" , "block_kv" , "block_kv_compute" )
35+ __slots__ = ("block_q" , "block_kv" , "block_kv_compute" , "block_kv_compute_in" )
4736
48- def __init__ (self , block_q : int , block_kv : int , block_kv_compute : int | None = None ):
37+ def __init__ (self , block_q : int , block_kv : int , block_kv_compute : int | None = None , block_kv_compute_in : int = 256 ):
4938 self .block_q = block_q
5039 self .block_kv = block_kv
5140 self .block_kv_compute = block_kv_compute if block_kv_compute is not None else block_kv
41+ self .block_kv_compute_in = block_kv_compute_in
5242
5343
5444def _flash_attention_kernel (
@@ -64,12 +54,10 @@ def _flash_attention_kernel(
6454 * ,
6555 mask_value : float ,
6656 grid_width : int ,
67- bq : int ,
6857 bkv : int ,
6958 bkv_compute : int ,
7059 bkv_compute_in : int ,
7160 head_dim_v : int ,
72- q_seq_len : int ,
7361 kv_seq_len : int ,
7462 use_base2_exp : bool = True ,
7563 fuse_reciprocal : bool = True ,
@@ -220,12 +208,10 @@ def _flash_attention_kernel_mhpt(
220208 * ,
221209 mask_value : float ,
222210 grid_width : int ,
223- bq : int ,
224211 bkv : int ,
225212 bkv_compute : int ,
226213 bkv_compute_in : int ,
227214 head_dim_v : int ,
228- q_seq_len : int ,
229215 kv_seq_len : int ,
230216 heads_per_tile : int ,
231217 use_base2_exp : bool = True ,
@@ -367,7 +353,6 @@ def _splash_attention_forward(
367353 k : jax .Array ,
368354 v : jax .Array ,
369355 block_sizes : _BlockSizes ,
370- bkv_compute_in : int ,
371356 q_seq_len : int | None = None ,
372357 kv_seq_len : int | None = None ,
373358 use_base2_exp : bool = True ,
@@ -378,6 +363,7 @@ def _splash_attention_forward(
378363 head_dim_v = v .shape [- 1 ]
379364 bq , bkv = block_sizes .block_q , block_sizes .block_kv
380365 bkv_compute = block_sizes .block_kv_compute
366+ bkv_compute_in = block_sizes .block_kv_compute_in
381367 num_kv_heads = k .shape [0 ]
382368 padded_kv_seq_len = k .shape [1 ]
383369
@@ -423,12 +409,10 @@ def v_index_map(h, i, j, *_):
423409 _flash_attention_kernel ,
424410 mask_value = DEFAULT_MASK_VALUE ,
425411 grid_width = grid_width ,
426- bq = bq ,
427412 bkv = bkv ,
428413 bkv_compute = bkv_compute ,
429414 bkv_compute_in = bkv_compute_in ,
430415 head_dim_v = head_dim_v ,
431- q_seq_len = actual_q_seq_len ,
432416 kv_seq_len = actual_kv_seq_len ,
433417 use_base2_exp = use_base2_exp ,
434418 ),
@@ -455,7 +439,6 @@ def _splash_attention_forward_ring(
455439 k : jax .Array ,
456440 v : jax .Array ,
457441 block_sizes : _BlockSizes ,
458- bkv_compute_in : int ,
459442 q_seq_len : int | None = None ,
460443 kv_seq_len : int | None = None ,
461444 use_base2_exp : bool = True ,
@@ -480,6 +463,7 @@ def _splash_attention_forward_ring(
480463 head_dim_v = v .shape [- 1 ]
481464 bq , bkv = block_sizes .block_q , block_sizes .block_kv
482465 bkv_compute = block_sizes .block_kv_compute
466+ bkv_compute_in = block_sizes .block_kv_compute_in
483467 num_kv_heads = k .shape [0 ]
484468 padded_kv_seq_len = k .shape [1 ]
485469
@@ -529,12 +513,10 @@ def v_index_map(h, i, j, *_):
529513 _flash_attention_kernel ,
530514 mask_value = DEFAULT_MASK_VALUE ,
531515 grid_width = grid_width ,
532- bq = bq ,
533516 bkv = bkv ,
534517 bkv_compute = bkv_compute ,
535518 bkv_compute_in = bkv_compute_in ,
536519 head_dim_v = head_dim_v ,
537- q_seq_len = actual_q_seq_len ,
538520 kv_seq_len = actual_kv_seq_len ,
539521 use_base2_exp = use_base2_exp ,
540522 fuse_reciprocal = False ,
@@ -565,7 +547,6 @@ def _splash_attention_forward_mhpt(
565547 k : jax .Array ,
566548 v : jax .Array ,
567549 block_sizes : _BlockSizes ,
568- bkv_compute_in : int ,
569550 heads_per_tile : int ,
570551 q_seq_len : int | None = None ,
571552 kv_seq_len : int | None = None ,
@@ -577,6 +558,7 @@ def _splash_attention_forward_mhpt(
577558 head_dim_v = v .shape [- 1 ]
578559 bq , bkv = block_sizes .block_q , block_sizes .block_kv
579560 bkv_compute = block_sizes .block_kv_compute
561+ bkv_compute_in = block_sizes .block_kv_compute_in
580562 num_kv_heads = k .shape [0 ]
581563 actual_q_seq_len = q_seq_len if q_seq_len is not None else padded_q_seq_len
582564 actual_kv_seq_len = kv_seq_len if kv_seq_len is not None else k .shape [1 ]
@@ -623,12 +605,10 @@ def out_index_map(h, i, j, *_):
623605 _flash_attention_kernel_mhpt ,
624606 mask_value = DEFAULT_MASK_VALUE ,
625607 grid_width = grid_width ,
626- bq = bq ,
627608 bkv = bkv ,
628609 bkv_compute = bkv_compute ,
629610 bkv_compute_in = bkv_compute_in ,
630611 head_dim_v = head_dim_v ,
631- q_seq_len = actual_q_seq_len ,
632612 kv_seq_len = actual_kv_seq_len ,
633613 heads_per_tile = hpt ,
634614 use_base2_exp = use_base2_exp ,
@@ -653,7 +633,6 @@ def out_index_map(h, i, j, *_):
653633
654634def make_splash_mha (
655635 block_sizes : _BlockSizes ,
656- bkv_compute_in : int = DEFAULT_BKVCOMPUTEINSIZE ,
657636 orig_q_seq_len : int | None = None ,
658637 orig_kv_seq_len : int | None = None ,
659638 heads_per_tile : int = 1 ,
@@ -668,7 +647,6 @@ def _splash_attention(q, k, v):
668647 k ,
669648 v ,
670649 block_sizes ,
671- bkv_compute_in ,
672650 heads_per_tile ,
673651 q_seq_len = orig_q_seq_len ,
674652 kv_seq_len = orig_kv_seq_len ,
@@ -681,7 +659,6 @@ def _splash_attention(q, k, v):
681659 k ,
682660 v ,
683661 block_sizes ,
684- bkv_compute_in ,
685662 q_seq_len = orig_q_seq_len ,
686663 kv_seq_len = orig_kv_seq_len ,
687664 use_base2_exp = use_base2_exp ,
@@ -690,200 +667,3 @@ def _splash_attention(q, k, v):
690667 )
691668
692669 return _splash_attention
693-
694-
695- # ---------------------------------------------------------------------------
696- # High-level attention function with shard_map
697- # ---------------------------------------------------------------------------
698-
699-
700- def tpu_custom_attention (
701- query ,
702- key ,
703- value ,
704- mesh ,
705- * ,
706- scale = None ,
707- block_q = None ,
708- block_kv = None ,
709- block_kv_compute = None ,
710- block_kv_compute_in = None ,
711- heads_per_tile = None ,
712- use_base2_exp = True ,
713- use_experimental_scheduler = False ,
714- vmem_limit_bytes = None ,
715- flash_block_sizes = None ,
716- ):
717- _LOG2_E = 1.44269504
718- num_heads = query .shape [1 ]
719-
720- if flash_block_sizes is not None :
721- block_q = flash_block_sizes .get ("block_q" , block_q )
722- block_kv = flash_block_sizes .get ("block_kv" , block_kv )
723- block_kv_compute = flash_block_sizes .get ("block_kv_compute" , block_kv_compute )
724- block_kv_compute_in = flash_block_sizes .get ("block_kv_compute_in" , block_kv_compute_in )
725- heads_per_tile = flash_block_sizes .get ("heads_per_tile" , heads_per_tile )
726- vmem_limit_bytes = flash_block_sizes .get ("vmem_limit_bytes" , vmem_limit_bytes )
727-
728- block_q = block_q if block_q is not None else DEFAULT_BQSIZE
729- block_kv = block_kv if block_kv is not None else DEFAULT_BKVSIZE
730- block_kv_compute = block_kv_compute if block_kv_compute is not None else DEFAULT_BKVCOMPUTESIZE
731- block_kv_compute_in = block_kv_compute_in if block_kv_compute_in is not None else DEFAULT_BKVCOMPUTEINSIZE
732- heads_per_tile = heads_per_tile if heads_per_tile is not None else 1
733-
734- def _attention_on_slices (q , k , v ):
735- scale_factor = 1.0 / math .sqrt (q .shape [- 1 ]) if scale is None else scale
736- if use_base2_exp :
737- q = q * scale_factor * _LOG2_E
738- else :
739- q = q * scale_factor
740-
741- def _pad_to_multiple (x , multiple , axis ):
742- seq_len = x .shape [axis ]
743- pad_len = (multiple - seq_len % multiple ) % multiple
744- if pad_len == 0 :
745- return x , seq_len
746- pad_width = [(0 , 0 )] * x .ndim
747- pad_width [axis ] = (0 , pad_len )
748- return jnp .pad (x , pad_width ), seq_len
749-
750- def _kernel_3d (q_3d , k_3d , v_3d ):
751- q_orig_len = q_3d .shape [1 ]
752- kv_orig_len = k_3d .shape [1 ]
753-
754- q_3d_padded , _ = _pad_to_multiple (q_3d , block_q , axis = 1 )
755- k_3d_padded , _ = _pad_to_multiple (k_3d , block_kv , axis = 1 )
756- v_3d_padded , _ = _pad_to_multiple (v_3d , block_kv , axis = 1 )
757-
758- padded_q_seq_len = q_3d_padded .shape [1 ]
759- padded_kv_seq_len = k_3d_padded .shape [1 ]
760-
761- bsizes = _BlockSizes (
762- block_q = min (block_q , padded_q_seq_len ),
763- block_kv = min (block_kv , padded_kv_seq_len ),
764- block_kv_compute = min (block_kv_compute , padded_kv_seq_len ),
765- )
766- splash_kernel = make_splash_mha (
767- block_sizes = bsizes ,
768- bkv_compute_in = block_kv_compute_in ,
769- orig_q_seq_len = q_orig_len ,
770- orig_kv_seq_len = kv_orig_len ,
771- heads_per_tile = heads_per_tile ,
772- use_base2_exp = use_base2_exp ,
773- use_experimental_scheduler = use_experimental_scheduler ,
774- vmem_limit_bytes = vmem_limit_bytes ,
775- )
776- out = splash_kernel (
777- q_3d_padded .astype (jnp .bfloat16 ),
778- k_3d_padded ,
779- v_3d_padded ,
780- )
781- out = jnp .swapaxes (out , 1 , 2 )
782- return out [:, :q_orig_len , ...]
783-
784- return jax .vmap (_kernel_3d , in_axes = (0 , 0 , 0 ), out_axes = 0 )(q , k , v )
785-
786- batch_size = query .shape [0 ]
787- if num_heads < mesh .size :
788- q_partition_spec = P ()
789- kv_partition_spec = P ()
790- out_constraint = P ()
791- else :
792- axis_names = mesh .axis_names
793- if len (axis_names ) == 1 :
794- tp_axis = axis_names [0 ]
795- q_partition_spec = P (None , tp_axis , None , None )
796- kv_partition_spec = P (None , tp_axis , None , None )
797- out_constraint = P (None , None , tp_axis , None )
798- elif len (axis_names ) == 2 :
799- dp_axis , tp_axis = axis_names [0 ], axis_names [1 ]
800- dp_size = mesh .shape [dp_axis ]
801- if batch_size >= dp_size :
802- q_partition_spec = P (dp_axis , tp_axis , None , None )
803- kv_partition_spec = P (dp_axis , tp_axis , None , None )
804- out_constraint = P (dp_axis , None , tp_axis , None )
805- else :
806- all_axes = tuple (axis_names )
807- q_partition_spec = P (None , all_axes , None , None )
808- kv_partition_spec = P (None , all_axes , None , None )
809- out_constraint = P (None , None , all_axes , None )
810- else :
811- q_partition_spec = P (axis_names [0 ], axis_names [1 ], axis_names [2 ], None )
812- kv_partition_spec = P (axis_names [0 ], axis_names [1 ], None , None )
813- out_constraint = P (axis_names [0 ], None , (axis_names [1 ], axis_names [2 ]), None )
814-
815- sharded_fn = shard_map (
816- _attention_on_slices ,
817- mesh = mesh ,
818- in_specs = (q_partition_spec , kv_partition_spec , kv_partition_spec ),
819- out_specs = q_partition_spec ,
820- check_rep = False ,
821- )
822- out = sharded_fn (query , key , value )
823- out = jax .lax .with_sharding_constraint (out , out_constraint )
824- return out
825-
826-
827- # ---------------------------------------------------------------------------
828- # TorchAX SDPA wrapper
829- # ---------------------------------------------------------------------------
830-
831-
832- def make_custom_splash_sdpa (mesh , env , ** kwargs ):
833- flash_block_sizes = kwargs .get ("flash_block_sizes" , None )
834- bq = kwargs .get ("block_q" , DEFAULT_BQSIZE )
835- bkv = kwargs .get ("block_kv" , DEFAULT_BKVSIZE )
836- bkv_compute = kwargs .get ("block_kv_compute" , DEFAULT_BKVCOMPUTESIZE )
837- bkv_compute_in = kwargs .get ("block_kv_compute_in" , DEFAULT_BKVCOMPUTEINSIZE )
838- hpt = kwargs .get ("heads_per_tile" , 1 )
839- use_k_smooth = kwargs .get ("use_k_smooth" , True )
840- use_base2_exp = kwargs .get ("use_base2_exp" , True )
841- use_experimental_scheduler = kwargs .get ("use_experimental_scheduler" , False )
842- vmem_limit_bytes = kwargs .get ("vmem_limit_bytes" , None )
843-
844- def _simple_attention (q , k , v , scale = None ):
845- s = scale if scale is not None else 1.0 / math .sqrt (q .shape [- 1 ])
846- attn = jnp .einsum ("bhsd,bhtd->bhst" , q * s , k )
847- attn = jax .nn .softmax (attn .astype (jnp .float32 ), axis = - 1 ).astype (q .dtype )
848- return jnp .einsum ("bhst,bhtd->bhsd" , attn , v )
849-
850- def _sdpa (
851- query ,
852- key ,
853- value ,
854- attn_mask = None ,
855- dropout_p = 0.0 ,
856- is_causal = False ,
857- scale = None ,
858- enable_gqa = False ,
859- ):
860- jquery , jkey , jvalue = env .t2j_iso ((query , key , value ))
861- num_heads = jquery .shape [1 ]
862-
863- if num_heads <= 8 :
864- result = _simple_attention (jquery , jkey , jvalue , scale = scale )
865- return env .j2t_iso (result )
866-
867- if use_k_smooth :
868- key_mean = jnp .mean (jkey , axis = 2 , keepdims = True )
869- jkey = jkey - key_mean
870-
871- result = tpu_custom_attention (
872- jquery ,
873- jkey ,
874- jvalue ,
875- mesh ,
876- scale = scale ,
877- block_q = bq ,
878- block_kv = bkv ,
879- block_kv_compute = bkv_compute ,
880- block_kv_compute_in = bkv_compute_in ,
881- heads_per_tile = hpt ,
882- use_base2_exp = use_base2_exp ,
883- use_experimental_scheduler = use_experimental_scheduler ,
884- vmem_limit_bytes = vmem_limit_bytes ,
885- flash_block_sizes = flash_block_sizes ,
886- )
887- return env .j2t_iso (result )
888-
889- return _sdpa
0 commit comments