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 (
@@ -62,12 +52,10 @@ def _flash_attention_kernel(
6252 * ,
6353 mask_value : float ,
6454 grid_width : int ,
65- bq : int ,
6655 bkv : int ,
6756 bkv_compute : int ,
6857 bkv_compute_in : int ,
6958 head_dim_v : int ,
70- q_seq_len : int ,
7159 kv_seq_len : int ,
7260 use_base2_exp : bool = True ,
7361):
@@ -207,12 +195,10 @@ def _flash_attention_kernel_mhpt(
207195 * ,
208196 mask_value : float ,
209197 grid_width : int ,
210- bq : int ,
211198 bkv : int ,
212199 bkv_compute : int ,
213200 bkv_compute_in : int ,
214201 head_dim_v : int ,
215- q_seq_len : int ,
216202 kv_seq_len : int ,
217203 heads_per_tile : int ,
218204 use_base2_exp : bool = True ,
@@ -354,7 +340,6 @@ def _splash_attention_forward(
354340 k : jax .Array ,
355341 v : jax .Array ,
356342 block_sizes : _BlockSizes ,
357- bkv_compute_in : int ,
358343 q_seq_len : int | None = None ,
359344 kv_seq_len : int | None = None ,
360345 use_base2_exp : bool = True ,
@@ -365,6 +350,7 @@ def _splash_attention_forward(
365350 head_dim_v = v .shape [- 1 ]
366351 bq , bkv = block_sizes .block_q , block_sizes .block_kv
367352 bkv_compute = block_sizes .block_kv_compute
353+ bkv_compute_in = block_sizes .block_kv_compute_in
368354 num_kv_heads = k .shape [0 ]
369355 padded_kv_seq_len = k .shape [1 ]
370356
@@ -410,12 +396,10 @@ def v_index_map(h, i, j, *_):
410396 _flash_attention_kernel ,
411397 mask_value = DEFAULT_MASK_VALUE ,
412398 grid_width = grid_width ,
413- bq = bq ,
414399 bkv = bkv ,
415400 bkv_compute = bkv_compute ,
416401 bkv_compute_in = bkv_compute_in ,
417402 head_dim_v = head_dim_v ,
418- q_seq_len = actual_q_seq_len ,
419403 kv_seq_len = actual_kv_seq_len ,
420404 use_base2_exp = use_base2_exp ,
421405 ),
@@ -442,7 +426,6 @@ def _splash_attention_forward_mhpt(
442426 k : jax .Array ,
443427 v : jax .Array ,
444428 block_sizes : _BlockSizes ,
445- bkv_compute_in : int ,
446429 heads_per_tile : int ,
447430 q_seq_len : int | None = None ,
448431 kv_seq_len : int | None = None ,
@@ -454,6 +437,7 @@ def _splash_attention_forward_mhpt(
454437 head_dim_v = v .shape [- 1 ]
455438 bq , bkv = block_sizes .block_q , block_sizes .block_kv
456439 bkv_compute = block_sizes .block_kv_compute
440+ bkv_compute_in = block_sizes .block_kv_compute_in
457441 num_kv_heads = k .shape [0 ]
458442 actual_q_seq_len = q_seq_len if q_seq_len is not None else padded_q_seq_len
459443 actual_kv_seq_len = kv_seq_len if kv_seq_len is not None else k .shape [1 ]
@@ -500,12 +484,10 @@ def out_index_map(h, i, j, *_):
500484 _flash_attention_kernel_mhpt ,
501485 mask_value = DEFAULT_MASK_VALUE ,
502486 grid_width = grid_width ,
503- bq = bq ,
504487 bkv = bkv ,
505488 bkv_compute = bkv_compute ,
506489 bkv_compute_in = bkv_compute_in ,
507490 head_dim_v = head_dim_v ,
508- q_seq_len = actual_q_seq_len ,
509491 kv_seq_len = actual_kv_seq_len ,
510492 heads_per_tile = hpt ,
511493 use_base2_exp = use_base2_exp ,
@@ -530,7 +512,6 @@ def out_index_map(h, i, j, *_):
530512
531513def make_splash_mha (
532514 block_sizes : _BlockSizes ,
533- bkv_compute_in : int = DEFAULT_BKVCOMPUTEINSIZE ,
534515 orig_q_seq_len : int | None = None ,
535516 orig_kv_seq_len : int | None = None ,
536517 heads_per_tile : int = 1 ,
@@ -545,7 +526,6 @@ def _splash_attention(q, k, v):
545526 k ,
546527 v ,
547528 block_sizes ,
548- bkv_compute_in ,
549529 heads_per_tile ,
550530 q_seq_len = orig_q_seq_len ,
551531 kv_seq_len = orig_kv_seq_len ,
@@ -558,7 +538,6 @@ def _splash_attention(q, k, v):
558538 k ,
559539 v ,
560540 block_sizes ,
561- bkv_compute_in ,
562541 q_seq_len = orig_q_seq_len ,
563542 kv_seq_len = orig_kv_seq_len ,
564543 use_base2_exp = use_base2_exp ,
@@ -567,200 +546,3 @@ def _splash_attention(q, k, v):
567546 )
568547
569548 return _splash_attention
570-
571-
572- # ---------------------------------------------------------------------------
573- # High-level attention function with shard_map
574- # ---------------------------------------------------------------------------
575-
576-
577- def tpu_custom_attention (
578- query ,
579- key ,
580- value ,
581- mesh ,
582- * ,
583- scale = None ,
584- block_q = None ,
585- block_kv = None ,
586- block_kv_compute = None ,
587- block_kv_compute_in = None ,
588- heads_per_tile = None ,
589- use_base2_exp = True ,
590- use_experimental_scheduler = False ,
591- vmem_limit_bytes = None ,
592- flash_block_sizes = None ,
593- ):
594- _LOG2_E = 1.44269504
595- num_heads = query .shape [1 ]
596-
597- if flash_block_sizes is not None :
598- block_q = flash_block_sizes .get ("block_q" , block_q )
599- block_kv = flash_block_sizes .get ("block_kv" , block_kv )
600- block_kv_compute = flash_block_sizes .get ("block_kv_compute" , block_kv_compute )
601- block_kv_compute_in = flash_block_sizes .get ("block_kv_compute_in" , block_kv_compute_in )
602- heads_per_tile = flash_block_sizes .get ("heads_per_tile" , heads_per_tile )
603- vmem_limit_bytes = flash_block_sizes .get ("vmem_limit_bytes" , vmem_limit_bytes )
604-
605- block_q = block_q if block_q is not None else DEFAULT_BQSIZE
606- block_kv = block_kv if block_kv is not None else DEFAULT_BKVSIZE
607- block_kv_compute = block_kv_compute if block_kv_compute is not None else DEFAULT_BKVCOMPUTESIZE
608- block_kv_compute_in = block_kv_compute_in if block_kv_compute_in is not None else DEFAULT_BKVCOMPUTEINSIZE
609- heads_per_tile = heads_per_tile if heads_per_tile is not None else 1
610-
611- def _attention_on_slices (q , k , v ):
612- scale_factor = 1.0 / math .sqrt (q .shape [- 1 ]) if scale is None else scale
613- if use_base2_exp :
614- q = q * scale_factor * _LOG2_E
615- else :
616- q = q * scale_factor
617-
618- def _pad_to_multiple (x , multiple , axis ):
619- seq_len = x .shape [axis ]
620- pad_len = (multiple - seq_len % multiple ) % multiple
621- if pad_len == 0 :
622- return x , seq_len
623- pad_width = [(0 , 0 )] * x .ndim
624- pad_width [axis ] = (0 , pad_len )
625- return jnp .pad (x , pad_width ), seq_len
626-
627- def _kernel_3d (q_3d , k_3d , v_3d ):
628- q_orig_len = q_3d .shape [1 ]
629- kv_orig_len = k_3d .shape [1 ]
630-
631- q_3d_padded , _ = _pad_to_multiple (q_3d , block_q , axis = 1 )
632- k_3d_padded , _ = _pad_to_multiple (k_3d , block_kv , axis = 1 )
633- v_3d_padded , _ = _pad_to_multiple (v_3d , block_kv , axis = 1 )
634-
635- padded_q_seq_len = q_3d_padded .shape [1 ]
636- padded_kv_seq_len = k_3d_padded .shape [1 ]
637-
638- bsizes = _BlockSizes (
639- block_q = min (block_q , padded_q_seq_len ),
640- block_kv = min (block_kv , padded_kv_seq_len ),
641- block_kv_compute = min (block_kv_compute , padded_kv_seq_len ),
642- )
643- splash_kernel = make_splash_mha (
644- block_sizes = bsizes ,
645- bkv_compute_in = block_kv_compute_in ,
646- orig_q_seq_len = q_orig_len ,
647- orig_kv_seq_len = kv_orig_len ,
648- heads_per_tile = heads_per_tile ,
649- use_base2_exp = use_base2_exp ,
650- use_experimental_scheduler = use_experimental_scheduler ,
651- vmem_limit_bytes = vmem_limit_bytes ,
652- )
653- out = splash_kernel (
654- q_3d_padded .astype (jnp .bfloat16 ),
655- k_3d_padded ,
656- v_3d_padded ,
657- )
658- out = jnp .swapaxes (out , 1 , 2 )
659- return out [:, :q_orig_len , ...]
660-
661- return jax .vmap (_kernel_3d , in_axes = (0 , 0 , 0 ), out_axes = 0 )(q , k , v )
662-
663- batch_size = query .shape [0 ]
664- if num_heads < mesh .size :
665- q_partition_spec = P ()
666- kv_partition_spec = P ()
667- out_constraint = P ()
668- else :
669- axis_names = mesh .axis_names
670- if len (axis_names ) == 1 :
671- tp_axis = axis_names [0 ]
672- q_partition_spec = P (None , tp_axis , None , None )
673- kv_partition_spec = P (None , tp_axis , None , None )
674- out_constraint = P (None , None , tp_axis , None )
675- elif len (axis_names ) == 2 :
676- dp_axis , tp_axis = axis_names [0 ], axis_names [1 ]
677- dp_size = mesh .shape [dp_axis ]
678- if batch_size >= dp_size :
679- q_partition_spec = P (dp_axis , tp_axis , None , None )
680- kv_partition_spec = P (dp_axis , tp_axis , None , None )
681- out_constraint = P (dp_axis , None , tp_axis , None )
682- else :
683- all_axes = tuple (axis_names )
684- q_partition_spec = P (None , all_axes , None , None )
685- kv_partition_spec = P (None , all_axes , None , None )
686- out_constraint = P (None , None , all_axes , None )
687- else :
688- q_partition_spec = P (axis_names [0 ], axis_names [1 ], axis_names [2 ], None )
689- kv_partition_spec = P (axis_names [0 ], axis_names [1 ], None , None )
690- out_constraint = P (axis_names [0 ], None , (axis_names [1 ], axis_names [2 ]), None )
691-
692- sharded_fn = shard_map (
693- _attention_on_slices ,
694- mesh = mesh ,
695- in_specs = (q_partition_spec , kv_partition_spec , kv_partition_spec ),
696- out_specs = q_partition_spec ,
697- check_rep = False ,
698- )
699- out = sharded_fn (query , key , value )
700- out = jax .lax .with_sharding_constraint (out , out_constraint )
701- return out
702-
703-
704- # ---------------------------------------------------------------------------
705- # TorchAX SDPA wrapper
706- # ---------------------------------------------------------------------------
707-
708-
709- def make_custom_splash_sdpa (mesh , env , ** kwargs ):
710- flash_block_sizes = kwargs .get ("flash_block_sizes" , None )
711- bq = kwargs .get ("block_q" , DEFAULT_BQSIZE )
712- bkv = kwargs .get ("block_kv" , DEFAULT_BKVSIZE )
713- bkv_compute = kwargs .get ("block_kv_compute" , DEFAULT_BKVCOMPUTESIZE )
714- bkv_compute_in = kwargs .get ("block_kv_compute_in" , DEFAULT_BKVCOMPUTEINSIZE )
715- hpt = kwargs .get ("heads_per_tile" , 1 )
716- use_k_smooth = kwargs .get ("use_k_smooth" , True )
717- use_base2_exp = kwargs .get ("use_base2_exp" , True )
718- use_experimental_scheduler = kwargs .get ("use_experimental_scheduler" , False )
719- vmem_limit_bytes = kwargs .get ("vmem_limit_bytes" , None )
720-
721- def _simple_attention (q , k , v , scale = None ):
722- s = scale if scale is not None else 1.0 / math .sqrt (q .shape [- 1 ])
723- attn = jnp .einsum ("bhsd,bhtd->bhst" , q * s , k )
724- attn = jax .nn .softmax (attn .astype (jnp .float32 ), axis = - 1 ).astype (q .dtype )
725- return jnp .einsum ("bhst,bhtd->bhsd" , attn , v )
726-
727- def _sdpa (
728- query ,
729- key ,
730- value ,
731- attn_mask = None ,
732- dropout_p = 0.0 ,
733- is_causal = False ,
734- scale = None ,
735- enable_gqa = False ,
736- ):
737- jquery , jkey , jvalue = env .t2j_iso ((query , key , value ))
738- num_heads = jquery .shape [1 ]
739-
740- if num_heads <= 8 :
741- result = _simple_attention (jquery , jkey , jvalue , scale = scale )
742- return env .j2t_iso (result )
743-
744- if use_k_smooth :
745- key_mean = jnp .mean (jkey , axis = 2 , keepdims = True )
746- jkey = jkey - key_mean
747-
748- result = tpu_custom_attention (
749- jquery ,
750- jkey ,
751- jvalue ,
752- mesh ,
753- scale = scale ,
754- block_q = bq ,
755- block_kv = bkv ,
756- block_kv_compute = bkv_compute ,
757- block_kv_compute_in = bkv_compute_in ,
758- heads_per_tile = hpt ,
759- use_base2_exp = use_base2_exp ,
760- use_experimental_scheduler = use_experimental_scheduler ,
761- vmem_limit_bytes = vmem_limit_bytes ,
762- flash_block_sizes = flash_block_sizes ,
763- )
764- return env .j2t_iso (result )
765-
766- return _sdpa
0 commit comments