[Diffusion] Add native paged KV backend for diffusion workers (#6102)
Signed-off-by: Acerak01-fy <wfy2003324@163.com> Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
This commit is contained in:
1 parent
c2daa471c3
commit
72ee535fcd
34 files changed
+4595
-71
No files matched your search
@@ -24,6 +24,7 @@ from vllm.logger import init_logger
|
||||
from vllm.utils.mem_utils import DeviceMemoryProfiler, GiB_bytes
|
||||
from vllm.v1.kv_cache_interface import KVCacheConfig, KVCacheSpec
|
||||
|
||||
from vllm_omni.diffusion.attention.layer import Attention
|
||||
from vllm_omni.diffusion.cache.cachedit import cache_summary
|
||||
from vllm_omni.diffusion.cache.prompt_embed_cache import (
|
||||
install_prompt_embed_cache,
|
||||
@@ -34,6 +35,11 @@ from vllm_omni.diffusion.compile import regionally_compile
|
||||
from vllm_omni.diffusion.data import DiffusionOutput, OmniDiffusionConfig
|
||||
from vllm_omni.diffusion.diffusion_kv.config import DiffusionKVCacheMode
|
||||
from vllm_omni.diffusion.diffusion_kv.metadata import DiffusionKVMetadata
|
||||
from vllm_omni.diffusion.diffusion_kv.model_runner_backend import DiffusionKVModelRunnerBackend
|
||||
from vllm_omni.diffusion.diffusion_kv.paged_attention_adapter import (
|
||||
DiffusionPagedAttentionRow,
|
||||
PreparedDiffusionPagedAttentionBatch,
|
||||
)
|
||||
from vllm_omni.diffusion.forward_context import set_forward_context
|
||||
from vllm_omni.diffusion.model_loader.diffusers_loader import DiffusersPipelineLoader
|
||||
from vllm_omni.diffusion.models.interface import (
|
||||
@@ -144,9 +150,13 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
|
||||
self.prompt_embed_cache: Any | None = None
|
||||
self.input_batch: InputBatch | None = None
|
||||
self.model_memory_usage = 0
|
||||
# Rank-local native config generated by the Engine. Physical tensors
|
||||
# and BlockTable binding remain the responsibility of the paged
|
||||
# data-plane initialization path.
|
||||
self.diffusion_kv_backend = DiffusionKVModelRunnerBackend(
|
||||
vllm_config=vllm_config,
|
||||
od_config=od_config,
|
||||
device=device,
|
||||
)
|
||||
# Compatibility view for callers that inspect the installed config;
|
||||
# physical ownership remains in ``diffusion_kv_backend``.
|
||||
self.kv_cache_config: KVCacheConfig | None = None
|
||||
|
||||
# Cache for per-request stepwise state.
|
||||
@@ -381,41 +391,60 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
|
||||
def get_kv_cache_spec(self) -> dict[str, KVCacheSpec]:
|
||||
"""Collect native specs from cache-enabled loaded attention modules."""
|
||||
|
||||
from vllm_omni.diffusion.attention.layer import Attention
|
||||
from vllm_omni.diffusion.diffusion_kv.config import DiffusionKVCacheMode
|
||||
|
||||
if self.od_config.diffusion_kv_mode is not DiffusionKVCacheMode.PAGED_SCHEDULER:
|
||||
return {}
|
||||
if self.pipeline is None:
|
||||
raise RuntimeError("Model must be loaded before collecting Diffusion KV cache specs")
|
||||
|
||||
specs: dict[str, KVCacheSpec] = {}
|
||||
cache_layers: dict[str, tuple[Attention, KVCacheSpec]] = {}
|
||||
for layer_name, module in self.pipeline.named_modules():
|
||||
if not isinstance(module, Attention):
|
||||
continue
|
||||
spec = module.get_kv_cache_spec(self.vllm_config)
|
||||
if spec is not None:
|
||||
specs[layer_name] = spec
|
||||
if not specs:
|
||||
cache_layers[layer_name] = (module, spec)
|
||||
if not cache_layers:
|
||||
raise RuntimeError(
|
||||
"paged_scheduler Diffusion KV found no cache-enabled Attention modules "
|
||||
f"in {type(self.pipeline).__name__}"
|
||||
)
|
||||
return specs
|
||||
return self.diffusion_kv_backend.register_kv_cache_layers(cache_layers)
|
||||
|
||||
def set_kv_cache_config(self, kv_cache_config: KVCacheConfig) -> None:
|
||||
"""Retain the Engine-generated rank-local config for the data plane."""
|
||||
"""Physically initialize the Engine-generated rank-local config."""
|
||||
|
||||
configured_layers = {
|
||||
layer_name for group in kv_cache_config.kv_cache_groups for layer_name in group.layer_names
|
||||
}
|
||||
expected_layers = set(self.get_kv_cache_spec())
|
||||
if configured_layers != expected_layers:
|
||||
raise ValueError(
|
||||
"Rank-local Diffusion KVCacheConfig layer mismatch: "
|
||||
f"expected={sorted(expected_layers)}, configured={sorted(configured_layers)}"
|
||||
)
|
||||
self.kv_cache_config = kv_cache_config
|
||||
self.diffusion_kv_backend.initialize_kv_cache(kv_cache_config)
|
||||
self.kv_cache_config = self.diffusion_kv_backend.kv_cache_config
|
||||
|
||||
def install_diffusion_kv_metadata(self, metadata: DiffusionKVMetadata) -> bool:
|
||||
return self.diffusion_kv_backend.install_diffusion_kv_metadata(metadata)
|
||||
|
||||
def get_diffusion_kv_row(
|
||||
self,
|
||||
request_id: str,
|
||||
sequence_id: int | None,
|
||||
context_id: str | None = None,
|
||||
) -> int:
|
||||
return self.diffusion_kv_backend.get_diffusion_kv_row(request_id, sequence_id, context_id)
|
||||
|
||||
def remove_diffusion_kv_requests(self, request_ids: list[str]) -> int:
|
||||
return self.diffusion_kv_backend.remove_diffusion_kv_requests(request_ids)
|
||||
|
||||
def refresh_diffusion_kv_block_table_layout(self) -> None:
|
||||
self.diffusion_kv_backend.refresh_block_table_layout()
|
||||
|
||||
def prepare_paged_attention_batch(
|
||||
self,
|
||||
rows: list[DiffusionPagedAttentionRow] | tuple[DiffusionPagedAttentionRow, ...],
|
||||
) -> PreparedDiffusionPagedAttentionBatch:
|
||||
"""Prepare model-specific query/write spans against installed Worker rows."""
|
||||
|
||||
return self.diffusion_kv_backend.prepare_paged_attention_batch(rows)
|
||||
|
||||
def activate_paged_attention(self, batch: PreparedDiffusionPagedAttentionBatch):
|
||||
"""Expose a prepared paged batch to Omni Attention for one forward."""
|
||||
|
||||
return self.diffusion_kv_backend.activate_paged_attention(batch)
|
||||
|
||||
def clear_prompt_embed_cache(self) -> None:
|
||||
"""Evict all cached text-encoder outputs (e.g. between training epochs).
|
||||
@@ -664,17 +693,25 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
|
||||
request_id=req.request_id,
|
||||
metadata=diffusion_kv_metadata,
|
||||
)
|
||||
runner_output = self._execute_request_list(
|
||||
[req],
|
||||
od_config=self.od_config,
|
||||
allow_single_output=True,
|
||||
require_request_batch_support=False,
|
||||
kv_prefetch_job=kv_prefetch_job,
|
||||
record_name="pipeline_forward",
|
||||
)
|
||||
output = runner_output.runner_outputs[0].result
|
||||
assert output is not None
|
||||
return output
|
||||
installed_request = False
|
||||
if diffusion_kv_metadata is not None:
|
||||
self.install_diffusion_kv_metadata(diffusion_kv_metadata)
|
||||
installed_request = True
|
||||
try:
|
||||
runner_output = self._execute_request_list(
|
||||
[req],
|
||||
od_config=self.od_config,
|
||||
allow_single_output=True,
|
||||
require_request_batch_support=False,
|
||||
kv_prefetch_job=kv_prefetch_job,
|
||||
record_name="pipeline_forward",
|
||||
)
|
||||
output = runner_output.runner_outputs[0].result
|
||||
assert output is not None
|
||||
return output
|
||||
finally:
|
||||
if installed_request:
|
||||
self.remove_diffusion_kv_requests([req.request_id])
|
||||
|
||||
def profile_run(self, requests: list[OmniDiffusionRequest]) -> None:
|
||||
"""Run the maximum per-rank request batch for memory profiling.
|
||||
@@ -746,14 +783,23 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
|
||||
request_id=new_req.req.request_id,
|
||||
metadata=new_req.diffusion_kv_metadata,
|
||||
)
|
||||
reqs = [nr.req for nr in scheduler_output.scheduled_new_reqs]
|
||||
return self._execute_request_list(
|
||||
reqs,
|
||||
od_config=od_config,
|
||||
allow_single_output=False,
|
||||
require_request_batch_support=True,
|
||||
record_name="pipeline_forward_batch",
|
||||
)
|
||||
installed_request_ids: list[str] = []
|
||||
try:
|
||||
for new_req in scheduler_output.scheduled_new_reqs:
|
||||
if new_req.diffusion_kv_metadata is not None:
|
||||
self.install_diffusion_kv_metadata(new_req.diffusion_kv_metadata)
|
||||
installed_request_ids.append(new_req.request_id)
|
||||
reqs = [nr.req for nr in scheduler_output.scheduled_new_reqs]
|
||||
return self._execute_request_list(
|
||||
reqs,
|
||||
od_config=od_config,
|
||||
allow_single_output=False,
|
||||
require_request_batch_support=True,
|
||||
record_name="pipeline_forward_batch",
|
||||
)
|
||||
finally:
|
||||
if installed_request_ids:
|
||||
self.remove_diffusion_kv_requests(installed_request_ids)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step-wise execution
|
||||
@@ -884,6 +930,40 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
|
||||
if self.od_config.cache_backend not in (None, "none"):
|
||||
raise ValueError("Step mode does not support cache_backend yet.")
|
||||
|
||||
# Scheduler metadata is installed only for newly admitted requests;
|
||||
# cached requests continue to use the row installed on their first
|
||||
# step. Paged rows are retired here for scheduler-finished requests so
|
||||
# a subsequent wave can reuse the native table slots. Dense execution
|
||||
# must not issue Worker KV cleanup side effects.
|
||||
if (
|
||||
getattr(self.od_config, "diffusion_kv_mode", DiffusionKVCacheMode.DENSE_LEGACY)
|
||||
is DiffusionKVCacheMode.PAGED_SCHEDULER
|
||||
and scheduler_output.finished_req_ids
|
||||
):
|
||||
self.remove_diffusion_kv_requests(list(scheduler_output.finished_req_ids))
|
||||
installed_request_ids: list[str] = []
|
||||
try:
|
||||
for new_req in scheduler_output.scheduled_new_reqs:
|
||||
if new_req.diffusion_kv_metadata is not None:
|
||||
self.install_diffusion_kv_metadata(new_req.diffusion_kv_metadata)
|
||||
installed_request_ids.append(new_req.request_id)
|
||||
return self._execute_stepwise_core(
|
||||
scheduler_output,
|
||||
record_output_peak_memory=record_output_peak_memory,
|
||||
)
|
||||
except Exception:
|
||||
if installed_request_ids:
|
||||
self.remove_diffusion_kv_requests(installed_request_ids)
|
||||
raise
|
||||
|
||||
def _execute_stepwise_core(
|
||||
self,
|
||||
scheduler_output: DiffusionSchedulerOutput,
|
||||
*,
|
||||
record_output_peak_memory: bool,
|
||||
) -> BatchRunnerOutput:
|
||||
"""Run the denoise step after metadata admission and row installation."""
|
||||
|
||||
use_hsdp = self.od_config.parallel_config.use_hsdp
|
||||
grad_context = torch.no_grad() if use_hsdp else torch.inference_mode()
|
||||
with grad_context:
|
||||
@@ -1005,7 +1085,16 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
|
||||
state.peak_memory_mb,
|
||||
)
|
||||
|
||||
terminal_request_ids = [
|
||||
runner_output.request_id for runner_output in runner_output_list if runner_output.finished
|
||||
]
|
||||
self._update_states_after(states, input_batch, pipeline_interrupted)
|
||||
if (
|
||||
getattr(self.od_config, "diffusion_kv_mode", DiffusionKVCacheMode.DENSE_LEGACY)
|
||||
is DiffusionKVCacheMode.PAGED_SCHEDULER
|
||||
and terminal_request_ids
|
||||
):
|
||||
self.remove_diffusion_kv_requests(terminal_request_ids)
|
||||
|
||||
return BatchRunnerOutput.from_list(runner_output_list)
|
||||
|
||||
|
||||
Reference in new issue
Block a user