Skip to content

Commit f6c2c11

Browse files
committed
Cleaning up custom_splash_attention
1 parent 0984457 commit f6c2c11

4 files changed

Lines changed: 17 additions & 240 deletions

File tree

src/maxdiffusion/kernels/custom_splash_attention.py

Lines changed: 6 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,28 @@
1717
"""Custom Pallas flash attention kernel for TPU."""
1818

1919
import functools
20-
import math
2120

2221
import jax
2322
import jax.numpy as jnp
2423
import numpy as np
2524
from jax import lax
2625
from jax.experimental import pallas as pl
2726
from 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

3128
DEFAULT_MASK_VALUE = -0.7 * float(np.finfo(np.dtype("float32")).max)
3229
NUM_LANES = 128
3330
NUM_SUBLANES = 8
3431
NT_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

4534
class _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

5444
def _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

654634
def 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

Comments
 (0)