[Diffusion][HunyuanImage3] Add request paged prefix caching (#7719)

Signed-off-by: zwhzzz0821 <2831474076@qq.com>
This commit is contained in:
Yukim1 authored and GitHub committed 2026-09-23 15:34:56 +00:00
1 parent 9a1a05facd
commit 205aa01c7b
49 files changed
+2889 -127

No files matched your search

@@ -113,7 +113,7 @@ class _FakeLayer:
self.head_size_v = 4
self.spec = _FakeSpec(non_causal=non_causal)
self.updates: list[tuple[torch.Tensor, torch.Tensor, torch.Tensor]] = []
self.calls: list[tuple[torch.Tensor, torch.Tensor, torch.Tensor, object]] = []
self.calls: list[tuple[torch.Tensor, torch.Tensor, torch.Tensor, _FakeNativeMetadata]] = []
self.native_events: list[str] = []
self.layer_name = "layer-0"
self.kv_cache = object()
@@ -122,6 +122,8 @@ class _FakeLayer:
class _FakeNativeImpl:
vllm_flash_attn_version: int
def __init__(self, layer: _FakeLayer) -> None:
self.layer = layer
@@ -143,7 +145,7 @@ class _FakeNativeImpl:
key: torch.Tensor,
value: torch.Tensor,
_kv_cache,
metadata: object,
metadata: _FakeNativeMetadata,
output: torch.Tensor,
) -> torch.Tensor:
self.layer.native_events.append("forward")
@@ -202,7 +204,7 @@ def _make_adapter(
config = SimpleNamespace(
name="vllm-config",
model_config=SimpleNamespace(dtype=torch.float32),
model_config=SimpleNamespace(dtype=torch.float32, rswa_window=None),
)
adapter = DiffusionPagedAttentionAdapter(
vllm_config=config,
@@ -1222,9 +1224,15 @@ def test_row_contract_rejects_invalid_identity_or_span(kwargs: dict, message: st
DiffusionPagedAttentionRow(**values)
@pytest.mark.parametrize("pcp_size", [1, 2])
@pytest.mark.parametrize("selection_fails", [False, True])
def test_layer_adapter_accepts_platform_native_backend_and_uses_rank_local_heads(
monkeypatch: pytest.MonkeyPatch,
pcp_size: int,
selection_fails: bool,
) -> None:
from vllm.v1.attention import selector
selected_backends = []
specialized_backends = []
impl_cls = Mock(return_value=SimpleNamespace(forward=Mock(), do_kv_cache_update=Mock()))
@@ -1237,28 +1245,39 @@ def test_layer_adapter_accepts_platform_native_backend_and_uses_rank_local_heads
original_backend_per_kind = {"full": object()}
config = SimpleNamespace(
attention_config=SimpleNamespace(backend=None, backend_per_kind=original_backend_per_kind),
parallel_config=SimpleNamespace(prefill_context_parallel_size=2),
model_config=SimpleNamespace(dtype=torch.float16),
cache_config=SimpleNamespace(cache_dtype="auto"),
attention_config=SimpleNamespace(backend=None, backend_per_kind=original_backend_per_kind, use_non_causal=True),
parallel_config=SimpleNamespace(prefill_context_parallel_size=pcp_size, decode_context_parallel_size=1),
model_config=SimpleNamespace(dtype=torch.float16, rswa_window=None),
cache_config=SimpleNamespace(cache_dtype="auto", user_specified_block_size=False),
kv_transfer_config=None,
speculative_config=None,
)
def select_backend(**_kwargs):
selected_backends.append(
(
config.attention_config.backend,
config.attention_config.backend_per_kind,
config.parallel_config.prefill_context_parallel_size,
)
)
def select_backend(*, backend, attn_selector_config, num_heads):
# Exercise the real 0.29 selector, replacing only platform resolution.
# Ulysses has already gathered tokens and sharded heads before the
# native kernel; the MoE PCP mapping must not request PCP attention.
assert not attn_selector_config.use_pcp
assert not attn_selector_config.use_dcp
assert attn_selector_config.use_non_causal
assert num_heads == 4
selected_backends.append((backend, config.attention_config.backend_per_kind))
if selection_fails:
raise ValueError("test backend unavailable")
return native_backend
monkeypatch.setattr(adapter_module, "get_attn_backend", select_backend)
monkeypatch.setattr("vllm.config.get_current_vllm_config", lambda: config)
monkeypatch.setattr(selector, "_cached_get_attn_backend", select_backend)
monkeypatch.setattr(adapter_module, "set_current_vllm_config", lambda _config: nullcontext())
def specialize_backend(backend, *, ulysses_degree):
specialized_backends.append((backend, ulysses_degree))
return backend
monkeypatch.setattr(
adapter_module.current_omni_platform,
"get_diffusion_paged_kv_attn_backend",
lambda backend, *, ulysses_degree: specialized_backends.append((backend, ulysses_degree)) or backend,
specialize_backend,
)
layer = SimpleNamespace(num_heads=8, softmax_scale=0.125)
spec = FullAttentionSpec(
@@ -1269,7 +1288,7 @@ def test_layer_adapter_accepts_platform_native_backend_and_uses_rank_local_heads
non_causal=True,
)
native_layer = adapter_module.DiffusionPagedAttentionLayerAdapter(
kwargs = dict(
layer_name="layer-0",
layer=layer,
spec=spec,
@@ -1277,12 +1296,21 @@ def test_layer_adapter_accepts_platform_native_backend_and_uses_rank_local_heads
device=torch.device("cpu"),
ulysses_degree=2,
)
if selection_fails:
with pytest.raises(ValueError, match="test backend unavailable"):
adapter_module.DiffusionPagedAttentionLayerAdapter(**kwargs)
else:
native_layer = adapter_module.DiffusionPagedAttentionLayerAdapter(**kwargs)
assert selected_backends == [(adapter_module.AttentionBackendEnum.FLASH_ATTN, {}, 1)]
assert specialized_backends == [(native_backend, 2)]
assert selected_backends == [(adapter_module.AttentionBackendEnum.FLASH_ATTN, {})]
assert config.attention_config.backend is None
assert config.attention_config.backend_per_kind is original_backend_per_kind
assert config.parallel_config.prefill_context_parallel_size == 2
assert config.parallel_config.prefill_context_parallel_size == pcp_size
if selection_fails:
assert not specialized_backends
impl_cls.assert_not_called()
return
assert specialized_backends == [(native_backend, 2)]
assert native_layer.num_heads == 4
assert native_layer.num_kv_heads == 2
assert native_layer.spec.num_kv_heads == 2
@@ -1432,7 +1460,12 @@ def test_omni_attention_keeps_dense_kernel_without_active_adapter() -> None:
layer._no_parallel_strategy = object()
layer._get_active_parallel_strategy = lambda: Strategy()
layer._with_kv_cache_dtype = lambda metadata: metadata
layer._run_local_attention = lambda query, _key, _value, _metadata: events.append("dense") or query
def run_local_attention(query, _key, _value, _metadata):
events.append("dense")
return query
layer._run_local_attention = run_local_attention
qkv = torch.zeros(1, 2, 2, 4)
assert not layer.is_paged_kv_active()