[Model] Fuse MammothModa2 QK norm and RoPE (#7969)

Signed-off-by: levius <2114377220@qq.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
This commit is contained in:
SunandOpenAI Codex authored and GitHub committed 2026-10-05 02:44:45 +08:00
1 parent 855254d4af
commit 3f20252151
5 files changed
+801 -21

No files matched your search

@@ -0,0 +1,262 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Benchmark MammothModa2's Q/K RMSNorm + adjacent-pair real RoPE.
Example:
CUDA_VISIBLE_DEVICES=0 python \
benchmarks/diffusion/benchmark_mammoth_moda2_qk_norm_rope.py \
--tokens 512 4173 8346 --warmup 50 --iters 200
"""
from __future__ import annotations
import argparse
import importlib.metadata
import json
import os
import statistics
from collections.abc import Callable
import torch
from vllm_omni.diffusion.layers.fused_qk_norm_rope import (
_fused_cuda_supported,
fused_qk_norm_rope,
)
from vllm_omni.diffusion.layers.norm import RMSNorm
from vllm_omni.diffusion.models.mammoth_moda2.rope_real import (
apply_real_rotary_emb,
)
_HEAD_DIM = 120
_Q_HEADS = 21
_KV_HEADS = 7
_EPS = 1e-5
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--tokens", nargs="+", type=int, default=[512, 4173, 8346])
parser.add_argument("--warmup", type=int, default=50)
parser.add_argument("--iters", type=int, default=200)
parser.add_argument("--rounds", type=int, default=3)
parser.add_argument("--attention-sequences", nargs="*", type=int, default=[])
return parser.parse_args()
def _version(distribution: str) -> str | None:
try:
return importlib.metadata.version(distribution)
except importlib.metadata.PackageNotFoundError:
return None
def _measure(
fn: Callable[[], object],
warmup: int,
iters: int,
) -> dict[str, float]:
for _ in range(warmup):
fn()
torch.accelerator.synchronize()
torch.accelerator.reset_peak_memory_stats()
baseline_bytes = torch.accelerator.memory_allocated()
samples = []
for _ in range(iters):
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
fn()
end.record()
end.synchronize()
samples.append(float(start.elapsed_time(end)))
ordered = sorted(samples)
return {
"median_ms": statistics.median(ordered),
"mean_ms": statistics.mean(ordered),
"stddev_ms": statistics.pstdev(ordered),
"min_ms": ordered[0],
"max_ms": ordered[-1],
"p90_ms": ordered[min(len(ordered) - 1, int(len(ordered) * 0.9))],
"extra_peak_mib": (torch.accelerator.max_memory_allocated() - baseline_bytes) / 2**20,
}
@torch.inference_mode()
def _run_shape(tokens: int, warmup: int, iters: int, *, fused_first: bool = False) -> dict[str, object]:
dtype = torch.bfloat16
device = torch.device("cuda")
torch.manual_seed(42)
query = torch.randn(tokens, _Q_HEADS, _HEAD_DIM, device=device, dtype=dtype)
key = torch.randn(tokens, _KV_HEADS, _HEAD_DIM, device=device, dtype=dtype)
norm_q = RMSNorm(_HEAD_DIM, eps=_EPS).to(device=device, dtype=dtype)
norm_k = RMSNorm(_HEAD_DIM, eps=_EPS).to(device=device, dtype=dtype)
norm_q.weight.normal_(mean=1.0, std=0.2)
norm_k.weight.normal_(mean=1.0, std=0.2)
angles = torch.rand(tokens, _HEAD_DIM // 2, device=device) * (2 * torch.pi)
cos = angles.cos().repeat_interleave(2, dim=-1).to(dtype)
sin = angles.sin().repeat_interleave(2, dim=-1).to(dtype)
def native() -> tuple[torch.Tensor, torch.Tensor]:
q_out = apply_real_rotary_emb(norm_q(query).unsqueeze(0), cos.unsqueeze(0), sin.unsqueeze(0)).squeeze(0)
k_out = apply_real_rotary_emb(norm_k(key).unsqueeze(0), cos.unsqueeze(0), sin.unsqueeze(0)).squeeze(0)
return q_out, k_out
def fused() -> tuple[torch.Tensor, torch.Tensor]:
rope_table = torch.cat((cos[..., 0::2], sin[..., 0::2]), dim=-1)
return fused_qk_norm_rope(
query,
key,
norm_q.weight,
norm_k.weight,
rope_table,
_EPS,
head_dim=_HEAD_DIM,
rotary_dim=_HEAD_DIM,
interleaved=True,
)
expected_q, expected_k = native()
actual_q, actual_k = fused()
errors = {
"q_max_abs": (actual_q.float() - expected_q.float()).abs().max().item(),
"q_mean_abs": (actual_q.float() - expected_q.float()).abs().mean().item(),
"k_max_abs": (actual_k.float() - expected_k.float()).abs().max().item(),
"k_mean_abs": (actual_k.float() - expected_k.float()).abs().mean().item(),
}
torch.testing.assert_close(actual_q, expected_q, atol=0.0625, rtol=0.02)
torch.testing.assert_close(actual_k, expected_k, atol=0.0625, rtol=0.02)
functions = {"native": native, "fused": fused}
order = ("fused", "native") if fused_first else ("native", "fused")
measured = {name: _measure(functions[name], warmup, iters) for name in order}
native_stats, fused_stats = measured["native"], measured["fused"]
return {
"tokens": tokens,
"native": native_stats,
"fused_including_rope_pack": fused_stats,
"median_speedup": native_stats["median_ms"] / fused_stats["median_ms"],
"errors": errors,
}
@torch.inference_mode()
def _run_attention(seq: int, warmup: int, iters: int, *, fused_first: bool = False) -> dict[str, object]:
from vllm_omni.diffusion.models.mammoth_moda2.mammothmoda2_dit_model import TransformerBlock
torch.manual_seed(42)
block = (
TransformerBlock(
2520,
_Q_HEADS,
_KV_HEADS,
multiple_of=256,
ffn_dim_multiplier=1.0,
norm_eps=_EPS,
rope_repeats_pairs=True,
)
.to(device="cuda", dtype=torch.bfloat16)
.eval()
)
block.attn.norm_q.weight.normal_(1.0, 0.2)
block.attn.norm_k.weight.normal_(1.0, 0.2)
hidden = torch.randn(2, seq, 2520, device="cuda", dtype=torch.bfloat16)
mask = torch.ones(2, seq, device="cuda", dtype=torch.bool)
mask[0, seq - min(300, seq // 2) :] = False
mask[1, seq - min(17, seq // 2) :] = False
angles = torch.rand(2, seq, _HEAD_DIM // 2, device="cuda") * (2 * torch.pi)
# The production producer supplies FP32 tables; each example has its own positions.
rotary = angles.cos().repeat_interleave(2, dim=-1), angles.sin().repeat_interleave(2, dim=-1)
env = "VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS"
previous = os.environ.get(env)
def run():
return block.attn(hidden, hidden, attention_mask=mask, image_rotary_emb=rotary)
try:
outputs = {}
timings = {}
order = ("fused", "native") if fused_first else ("native", "fused")
for name in order:
os.environ[env] = "0" if name == "fused" else str(2 * seq + 1)
outputs[name] = run()
timings[name] = _measure(run, warmup, iters)
diff = (outputs["native"].float() - outputs["fused"].float()).abs()[mask]
max_diff, mean_diff = diff.max().item(), diff.mean().item()
assert max_diff < 2e-2 and mean_diff < 1e-3, (max_diff, mean_diff)
assert torch.count_nonzero(outputs["fused"][~mask]) == 0
return {
"sequence": seq,
"batch": 2,
"backend": block.attn.omni_attn.attn_backend.__name__,
"native": timings["native"],
"fused": timings["fused"],
"median_speedup": timings["native"]["median_ms"] / timings["fused"]["median_ms"],
"max_abs_diff": max_diff,
"mean_abs_diff": mean_diff,
}
finally:
if previous is None:
os.environ.pop(env, None)
else:
os.environ[env] = previous
def main() -> None:
args = _parse_args()
if (
args.rounds < 1
or args.iters < 1
or args.warmup < 0
or any(value < 1 for value in [*args.tokens, *args.attention_sequences])
):
raise ValueError("rounds, iters and sequence lengths must be positive; warmup must be nonnegative")
if not torch.cuda.is_available():
raise RuntimeError("This benchmark requires CUDA")
probe_q = torch.empty(1, _Q_HEADS, _HEAD_DIM, device="cuda", dtype=torch.bfloat16)
probe_k = torch.empty(1, _KV_HEADS, _HEAD_DIM, device="cuda", dtype=torch.bfloat16)
if not _fused_cuda_supported(probe_q, probe_k, _HEAD_DIM, _HEAD_DIM, interleaved=True):
raise RuntimeError("The fused CUDA QK norm/RoPE path is unavailable")
result = {
"environment": {
"gpu": torch.cuda.get_device_name(),
"torch": torch.__version__,
"cuda": torch.version.cuda,
"triton": _version("triton"),
"vllm": _version("vllm"),
"vllm_omni": _version("vllm-omni"),
},
"config": {
"dtype": "bfloat16",
"q_heads": _Q_HEADS,
"kv_heads": _KV_HEADS,
"head_dim": _HEAD_DIM,
"warmup": args.warmup,
"iters": args.iters,
"rounds": args.rounds,
"inference_mode": True,
"alternating_order": True,
"norm_weights": "normal(mean=1.0, std=0.2)",
},
"results": [
{"round": round_id, **_run_shape(tokens, args.warmup, args.iters, fused_first=bool(round_id % 2))}
for round_id in range(args.rounds)
for tokens in args.tokens
],
"attention_results": [
{"round": round_id, **_run_attention(seq, args.warmup, args.iters, fused_first=bool(round_id % 2))}
for round_id in range(args.rounds)
for seq in args.attention_sequences
],
}
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
+1 -1
View File
@@ -130,7 +130,7 @@ settings.
| Name | Type and default | Applies to and read time | Precedence and invalid values | Lifecycle |
| --- | --- | --- | --- | --- |
| `VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS` | Non-negative integer; unset or empty means each consumer's own default (Boogu-Image: `2048`) | Token gate of the shared `fused_qk_norm_rope` op: a rotary table is packed for the fused kernel only when it spans at least this many positions (`B*S`); read at forward time | Environment-only. `0` = always fuse; a very large value disables the fused path. Any other value raises `ValueError` on the first forward. The crossover is host-dependent; re-benchmark before overriding. | Experimental performance control |
| `VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS` | Non-negative integer; unset or empty means each consumer's own default (Boogu-Image: `2048`; MammothModa2: `0`) | Token gate of the shared `fused_qk_norm_rope` op: a rotary table is packed for the fused kernel only when it spans at least this many positions (`B*S`); read at forward time | Environment-only. `0` = always fuse; a very large value disables the fused path. Any other value raises `ValueError` on the first forward. The crossover is host-dependent; re-benchmark before overriding. | Experimental performance control |
| `VLLM_OMNI_SKIP_NVFP4_NAN_CLAMP` | Boolean truthy spellings: `1`, `true`, `yes`, `on`; default false | ModelOpt NVFP4 compatibility patch; read when `vllm_omni.patch` imports | Environment-only escape hatch. Any other value means false. Set only to diagnose the upstream NaN-scale issue. | Diagnostic and temporary |
| `VLLM_OMNI_USE_QUACK_FP8` | Boolean truthy spellings: `1`, `true`, `yes`, `on`; unset means hardware auto-detection | FP8 scaled matrix multiplication; evaluated when quack capability is selected | A set value overrides auto-detection. Any non-truthy value forces quack off. If quack cannot load, vLLM-Omni warns and falls back to FlashInfer. | Experimental performance control |
@@ -187,3 +187,69 @@ def test_empty_text_stream_skips_the_kernel(monkeypatch, with_mask):
image_rotary_emb=(angles.cos(), angles.sin()),
)
assert out.shape == (BATCH, 0, DIM)
def test_model_owns_repeated_pair_rope_contract():
from vllm_omni.diffusion.models.mammoth_moda2.mammothmoda2_dit_model import Transformer2DModel
from vllm_omni.diffusion.models.mammoth_moda2.rope_real import RotaryPosEmbedReal
with set_current_diffusion_config(_SDPA_CONFIG):
model = Transformer2DModel(
hidden_size=48,
num_attention_heads=6,
num_kv_heads=2,
num_layers=1,
num_refiner_layers=1,
multiple_of=8,
ffn_dim_multiplier=1.0,
axes_dim_rope=(2, 2, 4),
axes_lens=(8, 8, 8),
text_feat_dim=32,
)
for blocks in (model.layers, model.noise_refiner, model.ref_image_refiner, model.context_refiner):
assert all(block.attn.processor.rope_repeats_pairs for block in blocks)
assert not _block(2).attn.processor.rope_repeats_pairs
freqs = RotaryPosEmbedReal.get_freqs_real((2, 2, 4), (8, 8, 8), 10000)
ids = torch.tensor([[[0, 1, 2], [3, 4, 5]], [[1, 0, 3], [2, 5, 4]]])
cos, sin = model.rope_embedder._get_freqs_real(freqs, ids)
for table in (cos, sin):
torch.testing.assert_close(table[..., 0::2], table[..., 1::2], atol=0, rtol=0)
assert not torch.equal(cos[0], cos[1])
def test_embedding_preparation_preserves_stubbed_rope_tables(monkeypatch):
from types import SimpleNamespace
from torch import nn
import vllm_omni.diffusion.models.mammoth_moda2.mammothmoda2_dit_model as mammoth_dit
# Embedding-only tests can stub the RoPE producer without constructing
# attention layers or supplying full transformer configuration.
model = object.__new__(mammoth_dit.Transformer2DModel)
nn.Module.__init__(model)
object.__setattr__(model, "_internal_dict", SimpleNamespace(patch_size=2))
class CaptionEmbedding(nn.Module):
def forward(self, timestep, text, dtype):
return torch.zeros(2, 8), text
model.time_caption_embed = CaptionEmbedding()
model.x_embedder = nn.Identity()
model.rope_embedder = lambda *args: (None, None, None, None, [4, 4], [8, 8])
def forbid_gate(*args):
raise AssertionError("Embedding preparation must not resolve the fusion token gate")
monkeypatch.setattr(mammoth_dit, "fused_qk_norm_rope_min_tokens", forbid_gate)
prepared = model._prepare_embeddings(
torch.zeros(2, 4, 4, 4),
torch.ones(2),
torch.zeros(2, 4, 8),
torch.ones(2, 4, dtype=torch.bool),
torch.zeros(1),
2,
4,
4,
)
assert prepared[6:9] == (None, None, None)
@@ -1,14 +1,16 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Real-shape check of the MammothModa2 DiT attention on CUDA: the shared
backend (FLASH_ATTN by default, head_dim 120) against the pre-change arithmetic
in bf16, with the padding mask the joint text+image sequence carries."""
"""Real-shape checks for MammothModa2's fused QK norm/RoPE and shared CUDA
attention backend against the native shared RMSNorm path."""
import pytest
import torch
from diffusers.models.attention_processor import Attention
import vllm_omni.diffusion.attention.backends.sdpa as sdpa_backend
import vllm_omni.diffusion.models.mammoth_moda2.mammothmoda2_dit_model as mammoth_dit
from tests.helpers.mark import hardware_marks
from vllm_omni.diffusion.layers.norm import RMSNorm
from vllm_omni.diffusion.models.mammoth_moda2.mammothmoda2_dit_model import TransformerBlock
from .test_dit_attention import _reference_attention
@@ -24,11 +26,127 @@ pytestmark = [
DIM, HEADS, KV_HEADS = 2520, 21, 7
def _norm_attention(head_dim, dtype):
attn = Attention(query_dim=head_dim * HEADS, heads=HEADS, kv_heads=KV_HEADS, dim_head=head_dim)
attn.norm_q = RMSNorm(head_dim)
attn.norm_k = RMSNorm(head_dim)
return attn.to(device="cuda", dtype=dtype)
def test_unpaired_rope_preserves_native_arithmetic(monkeypatch):
"""A generic block accepts independently valued even and odd RoPE lanes."""
torch.manual_seed(42)
q = torch.randn(2, 17, HEADS, 120, device="cuda", dtype=torch.bfloat16)
k = torch.randn(2, 17, KV_HEADS, 120, device="cuda", dtype=torch.bfloat16)
attn = _norm_attention(120, torch.bfloat16)
angles = torch.rand(2, 17, 120, device="cuda") * 6.283
rotary = angles.cos().to(q.dtype), angles.sin().to(q.dtype)
monkeypatch.setenv("VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS", "0")
with torch.no_grad():
got = mammoth_dit._apply_qk_norm_rope(attn, q, k, rotary)
expected = (
mammoth_dit.apply_real_rotary_emb(attn.norm_q(q), *rotary),
mammoth_dit.apply_real_rotary_emb(attn.norm_k(k), *rotary),
)
for actual, want in zip(got, expected):
torch.testing.assert_close(actual, want, atol=0, rtol=0)
def test_qk_norm_rope_benchmark_measures_inference(monkeypatch):
from benchmarks.diffusion import benchmark_mammoth_moda2_qk_norm_rope as benchmark
calls = []
def measure(fn, warmup, iters):
outputs = fn()
assert not torch.is_grad_enabled()
assert all(not output.requires_grad for output in outputs)
calls.append(fn.__name__)
return {"median_ms": 1.0}
monkeypatch.setattr(benchmark, "_measure", measure)
benchmark._run_shape(17, warmup=0, iters=1)
assert calls == ["native", "fused"]
def _production_rotary(batch, seq, head_dim, dtype):
"""Mammoth's real RoPE repeats one angle across each adjacent pair."""
angles = torch.rand(batch, seq, head_dim // 2, device="cuda") * 6.283
return (
angles.cos().repeat_interleave(2, dim=-1).to(dtype),
angles.sin().repeat_interleave(2, dim=-1).to(dtype),
)
@pytest.mark.parametrize("force_native", [False, True])
def test_production_rope_uses_fused_qk_norm_rope(monkeypatch, force_native):
torch.manual_seed(0)
seq = 512
block = (
TransformerBlock(
DIM,
HEADS,
KV_HEADS,
multiple_of=256,
ffn_dim_multiplier=1.0,
norm_eps=1e-5,
rope_repeats_pairs=True,
)
.cuda()
.to(torch.bfloat16)
.eval()
)
hidden = torch.randn(2, seq, DIM, device="cuda", dtype=torch.bfloat16)
mask = torch.ones(2, seq, dtype=torch.bool, device="cuda")
mask[0, -37:] = False
rotary = _production_rotary(1, seq, block.head_dim, torch.bfloat16)
calls = []
fused_qk_norm_rope = mammoth_dit.fused_qk_norm_rope
def record_fused(q, k, q_weight, k_weight, rope_table, eps, **kwargs):
calls.append((q.shape, k.shape, rope_table.shape, kwargs))
return fused_qk_norm_rope(q, k, q_weight, k_weight, rope_table, eps, **kwargs)
monkeypatch.setenv("VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS", "0")
if force_native:
monkeypatch.setattr(mammoth_dit, "_fused_cuda_supported", lambda *args, **kwargs: False)
monkeypatch.setattr(mammoth_dit, "fused_qk_norm_rope", record_fused)
with torch.no_grad():
got = block.attn(hidden, hidden, attention_mask=mask, image_rotary_emb=rotary)
want = _reference_attention(block.attn, hidden, mask, rotary)
supported = mammoth_dit._fused_cuda_supported(hidden, hidden, block.head_dim, block.head_dim, interleaved=True)
expected_calls = (
[
(
torch.Size((2 * seq, HEADS, block.head_dim)),
torch.Size((2 * seq, KV_HEADS, block.head_dim)),
torch.Size((2 * seq, block.head_dim)),
{"head_dim": block.head_dim, "rotary_dim": block.head_dim, "interleaved": True},
)
]
if supported
else []
)
assert calls == expected_calls
diff = (got.float() - want.float()).abs()[mask]
assert diff.max().item() < 2e-2, diff.max().item()
assert diff.mean().item() < 1e-3, diff.mean().item()
@pytest.mark.parametrize("seq", [77 + 4096, 512], ids=["t2i_1024", "short"])
def test_real_shape_matches_previous_arithmetic_bf16(seq):
torch.manual_seed(0)
block = (
TransformerBlock(DIM, HEADS, KV_HEADS, multiple_of=256, ffn_dim_multiplier=1.0, norm_eps=1e-5)
TransformerBlock(
DIM,
HEADS,
KV_HEADS,
multiple_of=256,
ffn_dim_multiplier=1.0,
norm_eps=1e-5,
rope_repeats_pairs=True,
)
.cuda()
.to(torch.bfloat16)
.eval()
@@ -37,8 +155,7 @@ def test_real_shape_matches_previous_arithmetic_bf16(seq):
mask = torch.ones(2, seq, dtype=torch.bool, device="cuda")
mask[0, seq - 300 :] = False
mask[1, seq - 17 :] = False
angles = torch.rand(1, seq, block.head_dim, device="cuda") * 6.283
rotary = (angles.cos().to(torch.bfloat16), angles.sin().to(torch.bfloat16))
rotary = _production_rotary(1, seq, block.head_dim, torch.bfloat16)
with torch.no_grad():
got = block.attn(
hidden_states=hidden, encoder_hidden_states=hidden, attention_mask=mask, image_rotary_emb=rotary
@@ -88,7 +205,15 @@ def test_fp32_falls_back_to_sdpa_and_matches_reference(monkeypatch, seq, force_g
torch.manual_seed(0)
dt = torch.float32
block = (
TransformerBlock(DIM, HEADS, KV_HEADS, multiple_of=256, ffn_dim_multiplier=1.0, norm_eps=1e-5)
TransformerBlock(
DIM,
HEADS,
KV_HEADS,
multiple_of=256,
ffn_dim_multiplier=1.0,
norm_eps=1e-5,
rope_repeats_pairs=True,
)
.cuda()
.to(dt)
.eval()
@@ -122,7 +247,11 @@ def test_fp32_falls_back_to_sdpa_and_matches_reference(monkeypatch, seq, force_g
calls.append((query.shape, key.shape, value.shape, query.dtype, kwargs))
return sdpa(query, key, value, **kwargs)
def reject_fused(*args, **kwargs):
raise AssertionError("FP32 must keep Mammoth's native QK norm/RoPE path")
with torch.no_grad(), monkeypatch.context() as patch:
patch.setattr(mammoth_dit, "fused_qk_norm_rope", reject_fused)
patch.setattr(sdpa_backend, "can_sdpa_use_fused_gqa", check_gqa)
patch.setattr(torch.nn.functional, "scaled_dot_product_attention", record_sdpa)
patch.setattr(strategy, "pre_attention", prepare)
@@ -147,3 +276,206 @@ def test_fp32_falls_back_to_sdpa_and_matches_reference(monkeypatch, seq, force_g
assert torch.count_nonzero(got[~mask]) == 0
diff = (got.float() - want.float()).abs()[mask]
assert diff.max().item() < 1e-3, diff.max().item()
@pytest.mark.parametrize("layout", ["unbatched", "broadcast", "per_example", "strided"])
@pytest.mark.parametrize("table_dtype", [torch.bfloat16, torch.float32])
@pytest.mark.parametrize("force_native", [False, True])
def test_paired_rope_with_learned_norm_weights(monkeypatch, layout, table_dtype, force_native):
torch.manual_seed(42)
batch, seq, head_dim = 2, 19, 120
attn = _norm_attention(head_dim, torch.bfloat16)
with torch.no_grad():
attn.norm_q.weight.normal_(1.0, 0.2)
attn.norm_k.weight.normal_(1.0, 0.2)
# Mimic views into packed QKV projections, including their nontrivial stride.
packed = torch.randn(batch, seq, HEADS + 2 * KV_HEADS, head_dim, device="cuda", dtype=torch.bfloat16)
q, k, _ = packed.split((HEADS, KV_HEADS, KV_HEADS), dim=2)
rotary = _production_rotary(batch if layout in ("per_example", "strided") else 1, seq, head_dim, table_dtype)
if layout == "unbatched":
rotary = tuple(t.squeeze(0) for t in rotary)
elif layout == "strided":
rotary = tuple(t.transpose(0, 1).contiguous().transpose(0, 1) for t in rotary)
assert not rotary[0].is_contiguous()
calls = []
fused = mammoth_dit.fused_qk_norm_rope
def record(*args, **kwargs):
calls.append(args[4].shape)
return fused(*args, **kwargs)
monkeypatch.setattr(mammoth_dit, "fused_qk_norm_rope", record)
if force_native:
monkeypatch.setattr(mammoth_dit, "_fused_cuda_supported", lambda *args, **kwargs: False)
monkeypatch.setenv("VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS", "0")
with torch.no_grad():
got = mammoth_dit._apply_qk_norm_rope(attn, q, k, rotary, rope_repeats_pairs=True)
expected = (
mammoth_dit.apply_real_rotary_emb(attn.norm_q(q), *rotary),
mammoth_dit.apply_real_rotary_emb(attn.norm_k(k), *rotary),
)
supported = mammoth_dit._fused_cuda_supported(q, k, head_dim, head_dim, interleaved=True)
assert calls == ([torch.Size((batch * seq, head_dim))] if supported else [])
for actual, want in zip(got, expected):
torch.testing.assert_close(actual, want.to(actual.dtype), atol=0.0625, rtol=0.02)
@pytest.mark.parametrize(
"reason", ["support_query", "token_gate", "no_q_norm", "no_k_norm", "epsilon", "fp16", "head_dim", "no_rope"]
)
def test_qk_norm_rope_unsupported_inputs_keep_native_chain(monkeypatch, reason):
torch.manual_seed(42)
dtype = torch.float16 if reason == "fp16" else torch.bfloat16
dim = 260 if reason == "head_dim" else 120
attn = _norm_attention(dim, dtype)
q = torch.randn(2, 19, HEADS, dim, device="cuda", dtype=dtype)
k = torch.randn(2, 19, KV_HEADS, dim, device="cuda", dtype=dtype)
rotary = _production_rotary(2, 19, dim, dtype) if reason != "no_rope" else None
if reason == "support_query":
monkeypatch.setattr(mammoth_dit, "_fused_cuda_supported", lambda *args, **kwargs: False)
elif reason == "no_q_norm":
attn.norm_q = None
elif reason == "no_k_norm":
attn.norm_k = None
elif reason == "epsilon":
attn.norm_k.variance_epsilon = 1e-3
monkeypatch.setenv("VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS", "39" if reason == "token_gate" else "0")
def reject(*args, **kwargs):
raise AssertionError(f"Unsupported {reason} must keep the native chain")
monkeypatch.setattr(mammoth_dit, "fused_qk_norm_rope", reject)
with torch.no_grad():
got = mammoth_dit._apply_qk_norm_rope(attn, q, k, rotary, rope_repeats_pairs=True)
expected_q = attn.norm_q(q) if attn.norm_q is not None else q
expected_k = attn.norm_k(k) if attn.norm_k is not None else k
if rotary is not None:
expected_q = mammoth_dit.apply_real_rotary_emb(expected_q, *rotary)
expected_k = mammoth_dit.apply_real_rotary_emb(expected_k, *rotary)
torch.testing.assert_close(got[0], expected_q, atol=0, rtol=0)
torch.testing.assert_close(got[1], expected_k, atol=0, rtol=0)
def test_attention_benchmark_single_token_and_restores_gate(monkeypatch):
from benchmarks.diffusion import benchmark_mammoth_moda2_qk_norm_rope as benchmark
env = "VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS"
monkeypatch.setenv(env, "123")
result = benchmark._run_attention(1, warmup=0, iters=1)
assert result["sequence"] == 1
assert benchmark.os.environ[env] == "123"
@pytest.mark.parametrize("packed", [False, True], ids=["gated_native", "reused_packed"])
def test_prepared_rope_table_skips_per_layer_gate_and_packing(monkeypatch, packed):
torch.manual_seed(7)
batch, seq, dim = 2, 19, 120
attn = _norm_attention(dim, torch.bfloat16)
q = torch.randn(batch, seq, HEADS, dim, device="cuda", dtype=torch.bfloat16)
k = torch.randn(batch, seq, KV_HEADS, dim, device="cuda", dtype=torch.bfloat16)
cos, sin = _production_rotary(batch, seq, dim, torch.float32)
table = torch.cat((cos[..., 0::2], sin[..., 0::2]), dim=-1).reshape(batch * seq, dim) if packed else None
rotary = cos, sin, table
calls = []
fused = mammoth_dit.fused_qk_norm_rope
def record(*args, **kwargs):
calls.append(args[4])
return fused(*args, **kwargs)
def forbid_gate(*args, **kwargs):
raise AssertionError("Prepared RoPE must not resolve the token gate per attention layer")
monkeypatch.setattr(mammoth_dit, "fused_qk_norm_rope", record)
monkeypatch.setattr(mammoth_dit, "fused_qk_norm_rope_min_tokens", forbid_gate)
with torch.no_grad():
for _ in range(2):
got = mammoth_dit._apply_qk_norm_rope(attn, q, k, rotary, rope_repeats_pairs=True)
expected = (
mammoth_dit.apply_real_rotary_emb(attn.norm_q(q), cos, sin),
mammoth_dit.apply_real_rotary_emb(attn.norm_k(k), cos, sin),
)
for actual, want in zip(got, expected):
torch.testing.assert_close(
actual, want.to(actual.dtype), atol=0.0625 if packed else 0, rtol=0.02 if packed else 0
)
supported = packed and mammoth_dit._fused_cuda_supported(q, k, dim, dim, interleaved=True)
assert calls == ([table, table] if supported else [])
@pytest.mark.parametrize("force_native", [False, True])
def test_model_prepares_three_rope_tables_with_one_threshold_lookup(monkeypatch, force_native):
from vllm_omni.diffusion.models.mammoth_moda2.mammothmoda2_dit_model import Transformer2DModel
from vllm_omni.diffusion.models.mammoth_moda2.rope_real import RotaryPosEmbedReal
torch.manual_seed(7)
model = (
Transformer2DModel(
patch_size=2,
in_channels=4,
hidden_size=48,
num_layers=1,
num_refiner_layers=1,
num_attention_heads=6,
num_kv_heads=2,
multiple_of=8,
ffn_dim_multiplier=1.0,
axes_dim_rope=(2, 2, 4),
axes_lens=(8, 8, 8),
text_feat_dim=32,
)
.cuda()
.to(torch.bfloat16)
.eval()
)
hidden = torch.randn(2, 4, 4, 4, device="cuda", dtype=torch.bfloat16)
text = torch.randn(2, 4, 32, device="cuda", dtype=torch.bfloat16)
mask = torch.ones(2, 4, device="cuda", dtype=torch.bool)
freqs = RotaryPosEmbedReal.get_freqs_real((2, 2, 4), (8, 8, 8), 10000)
calls = []
def threshold(default):
calls.append(default)
return 9
if force_native:
monkeypatch.setattr(mammoth_dit, "_fused_cuda_supported", lambda *args, **kwargs: False)
supported = mammoth_dit._fused_cuda_supported(hidden, hidden, 8, 8, interleaved=True)
tables: list[mammoth_dit.RotaryEmbedding] = []
refiners = model._apply_refiners
layers = model._apply_transformer_layers
def record_refiners(text, text_mask, context, img, img_mask, noise, temb):
tables.extend((context, noise))
return refiners(text, text_mask, context, img, img_mask, noise, temb)
def record_layers(hidden, mask, joint, temb):
tables.append(joint)
return layers(hidden, mask, joint, temb)
monkeypatch.setattr(model, "_apply_refiners", record_refiners)
monkeypatch.setattr(model, "_apply_transformer_layers", record_layers)
monkeypatch.setattr(mammoth_dit, "fused_qk_norm_rope_min_tokens", threshold)
args = (hidden, torch.ones(2, device="cuda"), text, freqs, mask)
with torch.no_grad():
fused_output = model(*args)
context, noise, joint = tables
assert calls == ([0] if supported else [])
assert all(len(rotary) == 3 for rotary in (context, noise, joint))
assert context[2] is None and noise[2] is None
if supported:
assert joint[2].shape == (2 * 8, 8)
else:
assert joint[2] is None
for rotary in (context, noise, joint):
if rotary[2] is not None:
expected = torch.cat((rotary[0][..., 0::2], rotary[1][..., 0::2]), dim=-1).reshape(-1, 8).float()
torch.testing.assert_close(rotary[2], expected, atol=0, rtol=0)
# Exercise the prepared tuple through every real refiner and joint block,
# then compare the same model forward with the unsupported-device fallback.
with torch.no_grad():
monkeypatch.setattr(mammoth_dit, "_fused_cuda_supported", lambda *args, **kwargs: False)
native_output = model(*args)
assert fused_output.shape == hidden.shape
torch.testing.assert_close(fused_output, native_output, atol=0.0625, rtol=0.02)
@@ -12,10 +12,112 @@ from torch import nn
from vllm_omni.diffusion.attention.backends.abstract import AttentionMetadata
from vllm_omni.diffusion.attention.layer import Attention as OmniAttention
from vllm_omni.diffusion.layers.fused_qk_norm_rope import (
_fused_cuda_supported,
fused_qk_norm_rope,
fused_qk_norm_rope_min_tokens,
)
from vllm_omni.diffusion.layers.norm import RMSNorm
from .rope_real import RotaryPosEmbedReal, apply_real_rotary_emb
# On the measured RTX 4090, fusion won even for the shortest tested tables;
# deployments can tune the crossover with VLLM_OMNI_FUSED_QK_NORM_ROPE_MIN_TOKENS.
_MAMMOTH_FUSED_QK_NORM_ROPE_MIN_TOKENS = 0
RotaryEmbedding = tuple[torch.Tensor, torch.Tensor] | tuple[torch.Tensor, torch.Tensor, torch.Tensor | None]
def _with_packed_rope_table(rotary_emb: tuple[torch.Tensor, torch.Tensor], min_tokens: int | None) -> RotaryEmbedding:
"""Pack a model-produced adjacent-pair RoPE table once for all layers.
A third element of ``None`` records that the support or token gate chose
the eager path, so attention layers need not resolve that gate again.
"""
cos, sin = rotary_emb
if min_tokens is None or cos.shape[0] * cos.shape[1] < min_tokens:
return cos, sin, None
packed = torch.cat((cos[..., 0::2], sin[..., 0::2]), dim=-1)
return cos, sin, packed.reshape(-1, cos.shape[-1]).float()
def _apply_qk_norm_rope(
attn: Attention,
query: torch.Tensor,
key: torch.Tensor,
image_rotary_emb: RotaryEmbedding | None,
*,
rope_repeats_pairs: bool = False,
) -> tuple[torch.Tensor, torch.Tensor]:
"""Apply Mammoth's per-head Q/K norm and adjacent-pair real RoPE."""
batch_size, sequence_length, _, head_dim = query.shape
cos, sin = (image_rotary_emb[0], image_rotary_emb[1]) if image_rotary_emb is not None else (None, None)
prepared = image_rotary_emb is not None and len(image_rotary_emb) == 3
packed = image_rotary_emb[2] if prepared else None
use_fused = (
rope_repeats_pairs
and cos is not None
and sin is not None
and attn.norm_q is not None
and attn.norm_k is not None
and attn.norm_q.variance_epsilon == attn.norm_k.variance_epsilon
and key.shape[0] == batch_size
and key.shape[1] == sequence_length
and key.shape[-1] == head_dim
and _fused_cuda_supported(query, key, head_dim, head_dim, interleaved=True)
and cos.shape[-2:] == (sequence_length, head_dim)
and sin.shape == cos.shape
and cos.ndim in (2, 3)
and (cos.ndim == 2 or cos.shape[0] in (1, batch_size))
and cos.device == query.device
and cos.dtype in (query.dtype, torch.float32)
and sin.device == query.device
and sin.dtype == cos.dtype
and (
packed is not None
if prepared
else batch_size * sequence_length >= fused_qk_norm_rope_min_tokens(_MAMMOTH_FUSED_QK_NORM_ROPE_MIN_TOKENS)
)
)
if use_fused:
assert cos is not None and sin is not None
if prepared:
assert packed is not None
rope_table = packed
else:
if cos.ndim == 2:
cos, sin = cos.unsqueeze(0), sin.unsqueeze(0)
if cos.shape[0] == 1 and batch_size > 1:
cos = cos.expand(batch_size, -1, -1)
sin = sin.expand(batch_size, -1, -1)
rope_table = torch.cat((cos[..., 0::2], sin[..., 0::2]), dim=-1).reshape(
batch_size * sequence_length, head_dim
)
query, key = fused_qk_norm_rope(
query.reshape(batch_size * sequence_length, query.shape[2], head_dim),
key.reshape(batch_size * sequence_length, key.shape[2], head_dim),
attn.norm_q.weight,
attn.norm_k.weight,
rope_table,
attn.norm_q.variance_epsilon,
head_dim=head_dim,
rotary_dim=head_dim,
interleaved=True,
)
return (
query.reshape(batch_size, sequence_length, query.shape[1], head_dim),
key.reshape(batch_size, sequence_length, key.shape[1], head_dim),
)
if attn.norm_q is not None:
query = attn.norm_q(query)
if attn.norm_k is not None:
key = attn.norm_k(key)
if image_rotary_emb is not None:
query = apply_real_rotary_emb(query, image_rotary_emb[0], image_rotary_emb[1])
key = apply_real_rotary_emb(key, image_rotary_emb[0], image_rotary_emb[1])
return query, key
class LuminaRMSNormZero(nn.Module):
"""
@@ -275,13 +377,18 @@ class AttnProcessor:
head count first.
"""
def __init__(self, *, rope_repeats_pairs: bool = False) -> None:
# Only the model's RotaryPosEmbedReal producer guarantees equal adjacent
# lanes. Generic processors retain the native four-lane arithmetic.
self.rope_repeats_pairs = rope_repeats_pairs
def __call__(
self,
attn: Attention,
hidden_states: torch.Tensor,
encoder_hidden_states: torch.Tensor,
attention_mask: torch.Tensor | None = None,
image_rotary_emb: torch.Tensor | None = None,
image_rotary_emb: RotaryEmbedding | None = None,
) -> torch.Tensor:
batch_size, sequence_length, _ = hidden_states.shape
@@ -307,14 +414,7 @@ class AttnProcessor:
key = key.view(batch_size, -1, kv_heads, head_dim)
value = value.view(batch_size, -1, kv_heads, head_dim)
if attn.norm_q is not None:
query = attn.norm_q(query)
if attn.norm_k is not None:
key = attn.norm_k(key)
if image_rotary_emb is not None:
query = apply_real_rotary_emb(query, image_rotary_emb[0], image_rotary_emb[1])
key = apply_real_rotary_emb(key, image_rotary_emb[0], image_rotary_emb[1])
query, key = _apply_qk_norm_rope(attn, query, key, image_rotary_emb, rope_repeats_pairs=self.rope_repeats_pairs)
query, key = query.to(dtype), key.to(dtype)
@@ -347,13 +447,15 @@ class TransformerBlock(nn.Module):
ffn_dim_multiplier: float,
norm_eps: float,
modulation: bool = True,
*,
rope_repeats_pairs: bool = False,
) -> None:
"""Initialize the transformer block."""
super().__init__()
self.head_dim = dim // num_attention_heads
self.modulation = modulation
processor = AttnProcessor()
processor = AttnProcessor(rope_repeats_pairs=rope_repeats_pairs)
# Initialize attention layer
self.attn = Attention(
@@ -403,7 +505,7 @@ class TransformerBlock(nn.Module):
self,
hidden_states: torch.Tensor,
attention_mask: torch.Tensor,
image_rotary_emb: torch.Tensor,
image_rotary_emb: RotaryEmbedding,
temb: torch.Tensor | None = None,
) -> torch.Tensor:
if self.modulation:
@@ -505,6 +607,7 @@ class Transformer2DModel(ModelMixin, ConfigMixin):
ffn_dim_multiplier,
norm_eps,
modulation=True,
rope_repeats_pairs=True,
)
for _ in range(num_refiner_layers)
]
@@ -520,6 +623,7 @@ class Transformer2DModel(ModelMixin, ConfigMixin):
ffn_dim_multiplier,
norm_eps,
modulation=True,
rope_repeats_pairs=True,
)
for _ in range(num_refiner_layers)
]
@@ -535,6 +639,7 @@ class Transformer2DModel(ModelMixin, ConfigMixin):
ffn_dim_multiplier,
norm_eps,
modulation=False,
rope_repeats_pairs=True,
)
for _ in range(num_refiner_layers)
]
@@ -551,6 +656,7 @@ class Transformer2DModel(ModelMixin, ConfigMixin):
ffn_dim_multiplier,
norm_eps,
modulation=True,
rope_repeats_pairs=True,
)
for _ in range(num_layers)
]
@@ -705,10 +811,10 @@ class Transformer2DModel(ModelMixin, ConfigMixin):
self,
text_hidden_states: torch.Tensor,
text_attention_mask: torch.Tensor,
context_rotary_emb: torch.Tensor,
context_rotary_emb: RotaryEmbedding,
img_tokens: torch.Tensor,
img_mask: torch.Tensor,
noise_rotary_emb: torch.Tensor,
noise_rotary_emb: RotaryEmbedding,
temb: torch.Tensor,
):
for layer in self.context_refiner:
@@ -777,6 +883,20 @@ class Transformer2DModel(ModelMixin, ConfigMixin):
ar_image_attention_mask,
)
# The producer repeats each frequency across adjacent lanes. Resolve
# support and the token gate once, then share each of the three packed
# tables across its refiner or transformer layers. Keep preparation in
# forward so embedding-only callers retain the producer's return values.
head_dim = rotary_emb[0].shape[-1]
min_tokens = (
fused_qk_norm_rope_min_tokens(_MAMMOTH_FUSED_QK_NORM_ROPE_MIN_TOKENS)
if _fused_cuda_supported(hidden_states, hidden_states, head_dim, head_dim, interleaved=True)
else None
)
context_rotary_emb = _with_packed_rope_table(context_rotary_emb, min_tokens)
noise_rotary_emb = _with_packed_rope_table(noise_rotary_emb, min_tokens)
rotary_emb = _with_packed_rope_table(rotary_emb, min_tokens)
text_hidden_states, img_tokens = self._apply_refiners(
text_hidden_states,
text_attention_mask,