[Attention] Fetch MLA prefill workspace at use instead of init (#60654)

Signed-off-by: Matthew Bonanni <mbonanni@redhat.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Matthew Bonanni
2026-10-13 17:25:03 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent e83222396f
commit 624a5ddafd
3 changed files with 72 additions and 11 deletions
@@ -2,14 +2,26 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Tests for MLA prefill backend registry."""
import gc
from unittest.mock import MagicMock
import pytest
import torch
from vllm.v1.attention.backends.mla.prefill.base import MLAPrefillBackend
from vllm.v1.attention.backends.mla.prefill.flashinfer import FlashInferPrefillBackend
from vllm.v1.attention.backends.mla.prefill.registry import (
MLAPrefillBackendEnum,
register_mla_prefill_backend,
)
from vllm.v1.attention.backends.mla.prefill.trtllm_ragged import (
TrtllmRaggedPrefillBackend,
)
from vllm.v1.worker.workspace import (
current_workspace_manager,
init_workspace_manager,
reset_workspace_manager,
)
class CustomMLAPrefillBackend(MLAPrefillBackend):
@@ -50,6 +62,38 @@ def test_prefill_backend_clone_has_isolated_metadata():
assert clone._prefill_metadata is not backend._prefill_metadata
@pytest.mark.parametrize(
"backend_cls", [TrtllmRaggedPrefillBackend, FlashInferPrefillBackend]
)
def test_prefill_backend_does_not_pin_grown_workspace(monkeypatch, backend_cls):
"""Reserving workspace at init must not keep a view of it: when another user
grows the shared workspace, the old buffer must be freed."""
monkeypatch.setenv("VLLM_FLASHINFER_WORKSPACE_BUFFER_SIZE", "4096")
init_workspace_manager(torch.device("cpu"))
try:
manager = current_workspace_manager()
backend = backend_cls(
num_heads=4,
scale=0.5,
kv_lora_rank=8,
qk_nope_head_dim=16,
qk_rope_head_dim=8,
v_head_dim=32,
vllm_config=MagicMock(),
)
(view,) = manager.get_simultaneous(((1,), torch.uint8))
old_storage = view.untyped_storage()._weak_ref()
del view
manager.get_simultaneous(((1 << 20,), torch.uint8))
gc.collect()
assert torch.UntypedStorage._expired(old_storage)
del backend
finally:
reset_workspace_manager()
@pytest.fixture(autouse=True)
def cleanup_overrides():
"""Clear any overrides after each test."""
@@ -91,9 +91,14 @@ class FlashInferPrefillBackend(MLAPrefillBackend):
self._prefill_main: BatchPrefillWithRaggedKVCacheWrapper | None = None
self._prefill_chunks: list[BatchPrefillWithRaggedKVCacheWrapper] = []
self._global_hyperparameters: PerLayerParameters | None = None
(self._workspace_buffer,) = current_workspace_manager().get_simultaneous(
((envs.VLLM_FLASHINFER_WORKSPACE_BUFFER_SIZE,), torch.uint8),
self._wrapper_workspace_ptr = 0
self._workspace_spec = (
(envs.VLLM_FLASHINFER_WORKSPACE_BUFFER_SIZE,),
torch.uint8,
)
# Reserve capacity without retaining a view: a retained view would pin
# the old buffer when another user grows the shared workspace.
current_workspace_manager().get_simultaneous(self._workspace_spec)
def _ensure_chunks(
self,
@@ -146,16 +151,24 @@ class FlashInferPrefillBackend(MLAPrefillBackend):
global_hyperparameters = self._resolve_global_hyperparameters()
qo_indptr = prefill_metadata.query_start_loc
has_context = prefill_metadata.chunked_context is not None
if self._prefill_main is None:
(workspace_buffer,) = current_workspace_manager().get_simultaneous(
self._workspace_spec
)
if (
self._prefill_main is None
or self._wrapper_workspace_ptr != workspace_buffer.data_ptr()
):
self._prefill_main = BatchPrefillWithRaggedKVCacheWrapper(
self._workspace_buffer, "NHD", backend="cutlass"
workspace_buffer, "NHD", backend="cutlass"
)
self._ensure_chunks(_DEFAULT_NUM_CHUNKS, self._workspace_buffer)
self._prefill_chunks = []
self._ensure_chunks(_DEFAULT_NUM_CHUNKS, workspace_buffer)
self._wrapper_workspace_ptr = workspace_buffer.data_ptr()
if has_context:
chunked_context = prefill_metadata.chunked_context
assert chunked_context is not None
self._ensure_chunks(len(chunked_context.chunks), self._workspace_buffer)
self._ensure_chunks(len(chunked_context.chunks), workspace_buffer)
num_qo_heads = self.num_heads
num_kv_heads = num_qo_heads
@@ -79,18 +79,22 @@ class TrtllmRaggedPrefillBackend(MLAPrefillBackend):
v_head_dim=v_head_dim,
vllm_config=vllm_config,
)
(self._workspace_buffer,) = current_workspace_manager().get_simultaneous(
(
(envs.VLLM_FLASHINFER_WORKSPACE_BUFFER_SIZE,),
torch.uint8,
),
self._workspace_spec = (
(envs.VLLM_FLASHINFER_WORKSPACE_BUFFER_SIZE,),
torch.uint8,
)
# Reserve capacity without retaining a view: a retained view would pin
# the old buffer when another user grows the shared workspace.
current_workspace_manager().get_simultaneous(self._workspace_spec)
def prepare_metadata(
self,
prefill_metadata: "MLACommonPrefillMetadata",
) -> None:
super().prepare_metadata(prefill_metadata)
(self._workspace_buffer,) = current_workspace_manager().get_simultaneous(
self._workspace_spec
)
self._query_seq_lens = (
prefill_metadata.query_start_loc[1:] - prefill_metadata.query_start_loc[:-1]
)