Skip to content

Commit fb64673

Browse files
committed
Cleaning up custom_splash_attention
1 parent 5e884cf commit fb64673

3 files changed

Lines changed: 9 additions & 228 deletions

File tree

src/maxdiffusion/kernels/custom_splash_attention.py

Lines changed: 5 additions & 223 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(
@@ -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

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

src/maxdiffusion/loaders/ltx2_lora_nnx_loader.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ def translate_fn(nnx_path_str):
7676
# the merge_fn warns about unmatched keys in each dict, so we only warn about any leftovers
7777
unmatched_keys = set(h_state_dict) - set(transformer_state_dict) - set(connector_state_dict)
7878
if unmatched_keys:
79-
max_logging.log(
80-
f"{len(unmatched_keys)} key(s) in LoRA dictionary routed to no merge target: {unmatched_keys}"
81-
)
79+
max_logging.log(f"{len(unmatched_keys)} key(s) in LoRA dictionary routed to no merge target: {unmatched_keys}")
8280

8381
return pipeline

src/maxdiffusion/models/attention_flax.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,11 +656,12 @@ def wrap_ulysses_attention(query, key, value):
656656
key, _, key_seq_len = _pad_data_for_flash(key, heads, bkv)
657657
value, _, _ = _pad_data_for_flash(value, heads, bkv)
658658

659-
bsizes = custom_splash._BlockSizes(block_q=bq, block_kv=bkv, block_kv_compute=bkv_compute)
659+
bsizes = custom_splash._BlockSizes(
660+
block_q=bq, block_kv=bkv, block_kv_compute=bkv_compute, block_kv_compute_in=bkv_compute_in
661+
)
660662

661663
splash_kernel = custom_splash.make_splash_mha(
662664
block_sizes=bsizes,
663-
bkv_compute_in=bkv_compute_in,
664665
orig_q_seq_len=query_seq_len,
665666
orig_kv_seq_len=key_seq_len,
666667
heads_per_tile=heads_per_tile,

0 commit comments

Comments
 (0)