[Diffusion] Fuse Qwen-Image FP8 QKV projection and Blackwell epilogue (#37123)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xiaoyu Zhang
2026-09-01 08:34:41 +08:00
committed by GitHub
co-authored by Cursor
parent 07c8f7294d
commit 6715debb2a
9 changed files with 1062 additions and 92 deletions
@@ -0,0 +1,194 @@
import sys
from unittest.mock import patch
import pytest
import torch
from sglang.kernels.ops.diffusion import try_fused_qwen_qkv_epilogue
from sglang.multimodal_gen.runtime.layers.layernorm import (
RMSNorm,
apply_qk_norm_with_optional_rope,
)
from sglang.test.ci.ci_register import register_cuda_ci
register_cuda_ci(est_time=15, stage="base-b-kernel-unit", runner_config="4-gpu-b200")
pytestmark = pytest.mark.skipif(
not torch.cuda.is_available() or torch.cuda.get_device_capability()[0] < 10,
reason="Qwen-Image QKV epilogue requires SM100+",
)
@pytest.fixture(autouse=True)
def _seed_cuda():
torch.cuda.manual_seed(0)
def test_qwen_qkv_epilogue_is_bit_exact():
heads = 4
head_dim = 128
img_tokens = 17
txt_tokens = 7
img_qkv = [
torch.randn(
1,
img_tokens,
heads,
head_dim,
device="cuda",
dtype=torch.bfloat16,
)
for _ in range(3)
]
txt_qkv = [
torch.randn(
1,
txt_tokens,
heads,
head_dim,
device="cuda",
dtype=torch.bfloat16,
)
for _ in range(3)
]
norms = [
RMSNorm(head_dim, eps=1e-6).to(device="cuda", dtype=torch.bfloat16)
for _ in range(4)
]
def cache(tokens):
angles = torch.randn(tokens, head_dim // 2, device="cuda")
return torch.cat([angles.cos(), angles.sin()], dim=-1).contiguous()
img_cache = cache(img_tokens)
txt_cache = cache(txt_tokens)
img_reference = [tensor.clone() for tensor in img_qkv]
txt_reference = [tensor.clone() for tensor in txt_qkv]
img_reference[0], img_reference[1] = apply_qk_norm_with_optional_rope(
img_reference[0],
img_reference[1],
norms[0],
norms[1],
head_dim,
img_cache,
is_neox=False,
)
txt_reference[0], txt_reference[1] = apply_qk_norm_with_optional_rope(
txt_reference[0],
txt_reference[1],
norms[2],
norms[3],
head_dim,
txt_cache,
is_neox=False,
)
expected = tuple(
torch.cat([txt_reference[index], img_reference[index]], dim=1)
for index in range(3)
)
actual = try_fused_qwen_qkv_epilogue(
*img_qkv,
*txt_qkv,
norms[0].weight,
norms[1].weight,
norms[2].weight,
norms[3].weight,
img_cache,
txt_cache,
1e-6,
1e-6,
)
assert actual is not None
assert all(
torch.equal(result, reference) for result, reference in zip(actual, expected)
)
# ModelOpt FP8 produces one packed QKV GEMM output. Its chunked Q/K/V
# tensors are zero-copy views with a 3x token stride; the epilogue must
# consume those views directly rather than launching six contiguous copies.
img_packed = torch.cat([tensor.flatten(2) for tensor in img_qkv], dim=-1)
txt_packed = torch.cat([tensor.flatten(2) for tensor in txt_qkv], dim=-1)
img_views = [
tensor.unflatten(-1, (heads, head_dim))
for tensor in img_packed.chunk(3, dim=-1)
]
txt_views = [
tensor.unflatten(-1, (heads, head_dim))
for tensor in txt_packed.chunk(3, dim=-1)
]
assert all(not tensor.is_contiguous() for tensor in (*img_views, *txt_views))
packed_actual = try_fused_qwen_qkv_epilogue(
*img_views,
*txt_views,
norms[0].weight,
norms[1].weight,
norms[2].weight,
norms[3].weight,
img_cache,
txt_cache,
1e-6,
1e-6,
)
assert packed_actual is not None
assert all(
torch.equal(result, reference)
for result, reference in zip(packed_actual, expected)
)
def test_qwen_qkv_epilogue_rejects_compile():
tensor = torch.empty(1, 1, 1, 128, device="cuda", dtype=torch.bfloat16)
row = torch.empty(128, device="cuda", dtype=torch.bfloat16)
cache = torch.empty(1, 128, device="cuda", dtype=torch.float32)
with patch("torch.compiler.is_compiling", return_value=True):
assert (
try_fused_qwen_qkv_epilogue(
tensor,
tensor,
tensor,
tensor,
tensor,
tensor,
row,
row,
row,
row,
cache,
cache,
1e-6,
1e-6,
)
is None
)
def test_qwen_qkv_epilogue_rejects_unsupported_head_dim():
tensor = torch.empty(1, 1, 1, 64, device="cuda", dtype=torch.bfloat16)
row = torch.empty(64, device="cuda", dtype=torch.bfloat16)
cache = torch.empty(1, 64, device="cuda", dtype=torch.float32)
assert (
try_fused_qwen_qkv_epilogue(
tensor,
tensor,
tensor,
tensor,
tensor,
tensor,
row,
row,
row,
row,
cache,
cache,
1e-6,
1e-6,
)
is None
)
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-v"]))