Skip to content

Commit eb65d89

Browse files
committed
Remove dead TorchAX wrapper and tpu_custom_attention
Since maxdiffusion internally uses attention_flax.py (which calls the core splash kernels directly via make_splash_mha), the high-level tpu_custom_attention orchestration and make_custom_splash_sdpa TorchAX wrapper are effectively dead code in this repository. Removing them to keep custom_splash_attention.py strictly focused on Pallas kernel definitions.
1 parent b77f9c6 commit eb65d89

1 file changed

Lines changed: 0 additions & 224 deletions

File tree

src/maxdiffusion/kernels/custom_splash_attention.py

Lines changed: 0 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -569,227 +569,3 @@ def _splash_attention(q, k, v):
569569
return _splash_attention
570570

571571

572-
# ---------------------------------------------------------------------------
573-
# High-level attention function with shard_map
574-
# TODO: Move `tpu_custom_attention` and `make_custom_splash_sdpa`
575-
# out of this file and into `attention_flax.py` where other orchestration
576-
# functions live. This file should be strictly Pallas kernel definitions.
577-
# ---------------------------------------------------------------------------
578-
579-
def tpu_custom_attention(
580-
query,
581-
key,
582-
value,
583-
mesh,
584-
*,
585-
scale=None,
586-
block_q=None,
587-
block_kv=None,
588-
block_kv_compute=None,
589-
block_kv_compute_in=None,
590-
heads_per_tile=None,
591-
use_base2_exp=True,
592-
use_experimental_scheduler=False,
593-
vmem_limit_bytes=None,
594-
flash_block_sizes=None,
595-
):
596-
_LOG2_E = 1.44269504
597-
num_heads = query.shape[1]
598-
599-
if flash_block_sizes is not None:
600-
block_q = flash_block_sizes.get("block_q", block_q)
601-
block_kv = flash_block_sizes.get("block_kv", block_kv)
602-
block_kv_compute = flash_block_sizes.get("block_kv_compute", block_kv_compute)
603-
block_kv_compute_in = flash_block_sizes.get("block_kv_compute_in", block_kv_compute_in)
604-
heads_per_tile = flash_block_sizes.get("heads_per_tile", heads_per_tile)
605-
vmem_limit_bytes = flash_block_sizes.get("vmem_limit_bytes", vmem_limit_bytes)
606-
607-
block_q = block_q if block_q is not None else DEFAULT_BQSIZE
608-
block_kv = block_kv if block_kv is not None else DEFAULT_BKVSIZE
609-
block_kv_compute = block_kv_compute if block_kv_compute is not None else DEFAULT_BKVCOMPUTESIZE
610-
block_kv_compute_in = block_kv_compute_in if block_kv_compute_in is not None else DEFAULT_BKVCOMPUTEINSIZE
611-
heads_per_tile = heads_per_tile if heads_per_tile is not None else 1
612-
613-
def _attention_on_slices(q, k, v):
614-
scale_factor = 1.0 / math.sqrt(q.shape[-1]) if scale is None else scale
615-
if use_base2_exp:
616-
q = q * scale_factor * _LOG2_E
617-
else:
618-
q = q * scale_factor
619-
620-
def _pad_to_multiple(x, multiple, axis):
621-
seq_len = x.shape[axis]
622-
pad_len = (multiple - seq_len % multiple) % multiple
623-
if pad_len == 0:
624-
return x, seq_len
625-
pad_width = [(0, 0)] * x.ndim
626-
pad_width[axis] = (0, pad_len)
627-
return jnp.pad(x, pad_width), seq_len
628-
629-
def _kernel_3d(q_3d, k_3d, v_3d):
630-
q_orig_len = q_3d.shape[1]
631-
kv_orig_len = k_3d.shape[1]
632-
633-
q_3d_padded, _ = _pad_to_multiple(q_3d, block_q, axis=1)
634-
k_3d_padded, _ = _pad_to_multiple(k_3d, block_kv, axis=1)
635-
v_3d_padded, _ = _pad_to_multiple(v_3d, block_kv, axis=1)
636-
637-
padded_q_seq_len = q_3d_padded.shape[1]
638-
padded_kv_seq_len = k_3d_padded.shape[1]
639-
640-
bsizes = _BlockSizes(
641-
block_q=min(block_q, padded_q_seq_len),
642-
block_kv=min(block_kv, padded_kv_seq_len),
643-
block_kv_compute=min(block_kv_compute, padded_kv_seq_len),
644-
)
645-
splash_kernel = make_splash_mha(
646-
block_sizes=bsizes,
647-
bkv_compute_in=block_kv_compute_in,
648-
orig_q_seq_len=q_orig_len,
649-
orig_kv_seq_len=kv_orig_len,
650-
heads_per_tile=heads_per_tile,
651-
use_base2_exp=use_base2_exp,
652-
use_experimental_scheduler=use_experimental_scheduler,
653-
vmem_limit_bytes=vmem_limit_bytes,
654-
)
655-
out = splash_kernel(
656-
q_3d_padded.astype(jnp.bfloat16),
657-
k_3d_padded,
658-
v_3d_padded,
659-
)
660-
out = jnp.swapaxes(out, 1, 2)
661-
return out[:, :q_orig_len, ...]
662-
663-
return jax.vmap(_kernel_3d, in_axes=(0, 0, 0), out_axes=0)(q, k, v)
664-
665-
batch_size = query.shape[0]
666-
if num_heads < mesh.size:
667-
q_partition_spec = P()
668-
kv_partition_spec = P()
669-
out_constraint = P()
670-
else:
671-
# Name-based axis resolution for the standard 4D mesh ("data", "fsdp", "context", "tensor").
672-
# Axes are grouped by semantic role:
673-
# - Batch-parallel: "data", "fsdp" (shard the batch dimension)
674-
# - Head-parallel: "context", "tensor" (shard the head dimension)
675-
# Only axes with size > 1 participate in sharding to avoid shard_map errors.
676-
# Q and KV are sharded identically (symmetric head-parallel), matching the
677-
# strategy that the legacy 2D (dp, tp) path used.
678-
batch_axes = tuple(a for a in ("data", "fsdp") if a in mesh.shape and mesh.shape[a] > 1)
679-
head_axes = tuple(a for a in ("context", "tensor") if a in mesh.shape and mesh.shape[a] > 1)
680-
681-
batch_parallel_size = 1
682-
for a in batch_axes:
683-
batch_parallel_size *= mesh.shape[a]
684-
head_parallel_size = 1
685-
for a in head_axes:
686-
head_parallel_size *= mesh.shape[a]
687-
688-
# Flatten singleton tuples for clean PartitionSpecs.
689-
batch_spec = batch_axes[0] if len(batch_axes) == 1 else (batch_axes if batch_axes else None)
690-
head_spec = head_axes[0] if len(head_axes) == 1 else (head_axes if head_axes else None)
691-
692-
if (
693-
batch_parallel_size > 1
694-
and batch_size >= batch_parallel_size
695-
and num_heads % head_parallel_size == 0
696-
):
697-
# Head-parallel: batch on data/fsdp, heads on context/tensor.
698-
q_partition_spec = P(batch_spec, head_spec, None, None)
699-
kv_partition_spec = P(batch_spec, head_spec, None, None)
700-
out_constraint = P(batch_spec, None, head_spec, None)
701-
elif head_parallel_size > 1 or batch_parallel_size > 1:
702-
# Batch too small for batch-parallel; shard all parallelism onto heads.
703-
all_axes = batch_axes + head_axes
704-
all_parallel_size = batch_parallel_size * head_parallel_size
705-
all_spec = all_axes[0] if len(all_axes) == 1 else (all_axes if all_axes else None)
706-
if num_heads % all_parallel_size == 0:
707-
q_partition_spec = P(None, all_spec, None, None)
708-
kv_partition_spec = P(None, all_spec, None, None)
709-
out_constraint = P(None, None, all_spec, None)
710-
else:
711-
# Heads not divisible by available parallelism; fall back to replicated.
712-
q_partition_spec = P()
713-
kv_partition_spec = P()
714-
out_constraint = P()
715-
else:
716-
# All axes are size-1; fully replicated (e.g. single-device).
717-
q_partition_spec = P()
718-
kv_partition_spec = P()
719-
out_constraint = P()
720-
721-
sharded_fn = shard_map(
722-
_attention_on_slices,
723-
mesh=mesh,
724-
in_specs=(q_partition_spec, kv_partition_spec, kv_partition_spec),
725-
out_specs=q_partition_spec,
726-
check_rep=False,
727-
)
728-
out = sharded_fn(query, key, value)
729-
out = jax.lax.with_sharding_constraint(out, out_constraint)
730-
return out
731-
732-
733-
# ---------------------------------------------------------------------------
734-
# TorchAX SDPA wrapper
735-
# ---------------------------------------------------------------------------
736-
737-
738-
def make_custom_splash_sdpa(mesh, env, **kwargs):
739-
flash_block_sizes = kwargs.get("flash_block_sizes", None)
740-
bq = kwargs.get("block_q", DEFAULT_BQSIZE)
741-
bkv = kwargs.get("block_kv", DEFAULT_BKVSIZE)
742-
bkv_compute = kwargs.get("block_kv_compute", DEFAULT_BKVCOMPUTESIZE)
743-
bkv_compute_in = kwargs.get("block_kv_compute_in", DEFAULT_BKVCOMPUTEINSIZE)
744-
hpt = kwargs.get("heads_per_tile", 1)
745-
use_k_smooth = kwargs.get("use_k_smooth", True)
746-
use_base2_exp = kwargs.get("use_base2_exp", True)
747-
use_experimental_scheduler = kwargs.get("use_experimental_scheduler", False)
748-
vmem_limit_bytes = kwargs.get("vmem_limit_bytes", None)
749-
750-
def _simple_attention(q, k, v, scale=None):
751-
s = scale if scale is not None else 1.0 / math.sqrt(q.shape[-1])
752-
attn = jnp.einsum("bhsd,bhtd->bhst", q * s, k)
753-
attn = jax.nn.softmax(attn.astype(jnp.float32), axis=-1).astype(q.dtype)
754-
return jnp.einsum("bhst,bhtd->bhsd", attn, v)
755-
756-
def _sdpa(
757-
query,
758-
key,
759-
value,
760-
attn_mask=None,
761-
dropout_p=0.0,
762-
is_causal=False,
763-
scale=None,
764-
enable_gqa=False,
765-
):
766-
jquery, jkey, jvalue = env.t2j_iso((query, key, value))
767-
num_heads = jquery.shape[1]
768-
769-
if num_heads <= 8:
770-
result = _simple_attention(jquery, jkey, jvalue, scale=scale)
771-
return env.j2t_iso(result)
772-
773-
if use_k_smooth:
774-
key_mean = jnp.mean(jkey, axis=2, keepdims=True)
775-
jkey = jkey - key_mean
776-
777-
result = tpu_custom_attention(
778-
jquery,
779-
jkey,
780-
jvalue,
781-
mesh,
782-
scale=scale,
783-
block_q=bq,
784-
block_kv=bkv,
785-
block_kv_compute=bkv_compute,
786-
block_kv_compute_in=bkv_compute_in,
787-
heads_per_tile=hpt,
788-
use_base2_exp=use_base2_exp,
789-
use_experimental_scheduler=use_experimental_scheduler,
790-
vmem_limit_bytes=vmem_limit_bytes,
791-
flash_block_sizes=flash_block_sizes,
792-
)
793-
return env.j2t_iso(result)
794-
795-
return _sdpa

0 commit comments

Comments
 (0)